Corrigindo "fork: Resource temporarily unavailable"

Corrigindo "fork: Resource temporarily unavailable"

O que significa "fork: Resource temporarily unavailable"?

Conclusão: Uma chamada a fork(2) (ou clone(2)) foi temporariamente recusada porque um limite foi atingido. O kernel retorna EAGAIN, e o shell ou aplicação imprime "Resource temporarily unavailable". Nem sempre é pouca memória; a causa geralmente é um limite de contagem de processos ou threads.

Este erro aparece no instante em que um programa tenta criar um processo filho ou thread. fork() / clone() retorna EAGAIN (errno 11), que é convertido em string e impresso.

$ ./myapp
fork: retry: Resource temporarily unavailable
bash: fork: retry: Resource temporarily unavailable
bash: fork: Resource temporarily unavailable

O próprio shell pode emiti-lo, caso em que você não consegue nem executar um novo comando. O ponto-chave: isto não é um problema de disco ou rede. Você esgotou o orçamento do kernel para "quantas tarefas podem existir ao mesmo tempo", então apenas a criação de novas é bloqueada. Processos existentes continuam executando, então o sintoma é estritamente "não consigo iniciar nada novo".

Premissas (ambiente alvo)

  • SO: Ubuntu / Linux típico (baseado em systemd)
  • Sintoma: fork / clone falha com Resource temporarily unavailable
  • Você pode ler ulimit / /proc / systemctl (algumas alterações permanentes precisam de sudo)

Por que fork falha com EAGAIN?

Conclusão: A causa é uma de quatro: (1) limite de processos por usuário (RLIMIT_NPROC = ulimit -u), (2) teto de PID / thread do sistema (pid_max / threads-max), (3) limite de tarefas do cgroup (systemd TasksMax), ou (4) esgotamento de memória. Na maioria das vezes é (1) ou o baseado em cgroup (3).

Aqui estão os caminhos que fazem fork retornar EAGAIN, agrupados por qual limite é atingido.

Causa O limite real Onde começar a verificar
Limite de processos por usuário RLIMIT_NPROC (ulimit -u) ulimit -u / ps -u <user>
Teto de PID do sistema kernel.pid_max /proc/sys/kernel/pid_max
Teto de threads do sistema kernel.threads-max /proc/sys/kernel/threads-max
Limite de tarefas do cgroup systemd TasksMax (pids cgroup) systemctl show -p TasksMax
Esgotamento de memória Não consegue alocar estruturas de tarefas free -h / dmesg

Trabalhe do orçamento mais próximo para fora. Comece com "o limite deste usuário (ulimit -u)", depois "o TasksMax do cgroup que agrupa o serviço", e finalmente "o pid_max / threads-max do sistema". Aplicações web e containers que criam muitas threads geralmente atingem o limite do cgroup (3) primeiro.

ulimit -u é descrito como "contagem de processos", mas internamente o Linux conta cada thread como uma tarefa. Uma aplicação multithread pode atingir o limite "com apenas poucos processos", então sempre conte o uso atual em unidades de thread.

Como verifico a contagem e limites atuais de processos / threads?

Conclusão: Primeiro leia o limite com ulimit -u, conte o uso atual com ps -eLf | wc -l (ou por usuário), depois compare. Se o uso está no limite, esse orçamento é o culpado.

Comece com o limite em vigor para seu shell atual.

$ ulimit -u
4096

Depois, conte as tarefas atuais (threads incluídas). O -L em ps -eLf expande cada thread para sua própria linha, correspondendo à unidade que fork/clone conta.

# Total de threads no sistema (aprox., inclui 1 linha de cabecalho)
$ ps -eLf | wc -l

# Threads de um usuario especifico
$ ps -L -u www-data | wc -l
3987

Se o uso atual (3987) está logo abaixo do limite (4096), o orçamento está gasto. Para ver qual processo está produzindo tarefas em massa, ordene por contagem de threads (nlwp) decrescente.

$ ps -eo pid,nlwp,user,comm --sort=-nlwp | head
  PID NLWP USER     COMMAND
 2314 1820 www-data java
 1190  214 mysql    mysqld

O processo com NLWP (contagem de threads) descontrolado é a origem real. Se é um vazamento não intencional de threads (pool de conexões ou contagem de workers mal configurados), suspeite da aplicação antes de elevar qualquer limite.

Antes de elevar um limite, decida se o uso é "um número saudável que é simplesmente muito pequeno" ou "anormalmente alto por causa de um vazamento". Encobrir um vazamento elevando o limite só faz recorrer mais tarde com um teto mais alto.

Como corrijo o limite por usuário (ulimit -u / nproc)?

Conclusão: Temporariamente você pode ampliar com ulimit -u <n>, mas para aplicar entre logins, defina nproc em /etc/security/limits.conf (ou limits.d/). Daemons não recebem limits.conf, então precisam da configuração do systemd.

Primeiro amplie apenas o shell atual para confirmar que o sintoma desaparece.

# Elevar o soft limit, ate o hard limit
$ ulimit -u 8192

A configuração permanente difere por caminho de login. Logins interativos (SSH / console) respeitam limits.conf.

$ sudo nano /etc/security/limits.conf
# <domain> <type> <item> <value>
www-data  soft  nproc  8192
www-data  hard  nproc  16384
*         soft  nproc  4096

soft é o valor padrão, hard é o teto que você pode elevar. Estes são aplicados via pam_limits.so do PAM, então você deve fazer login novamente após editar. No Ubuntu, o layout recomendado é colocar arquivos em /etc/security/limits.d/*.conf, que são lidos após o arquivo principal.

limits.conf se aplica apenas a sessões de login que passam pelo PAM. Não é aplicado a daemons (Nginx, MySQL, etc.) iniciados pelo systemd. Defina limites de processo para serviços com as configurações de cgroup / systemd na próxima seção. Confundir estes leva a "mudei mas nada aconteceu".

E se o cgroup / systemd TasksMax for a causa?

Conclusão: Serviços e sessões de login gerenciados pelo systemd são governados pelo pids.max do cgroup (= TasksMax). Se editar limits.conf não muda nada, este é quase sempre o culpado. Verifique o valor atual com systemctl show -p TasksMax, depois eleve com um drop-in.

O systemd agrupa cada serviço e cada sessão de usuário em um cgroup e limita a contagem total de tarefas com TasksMax. Primeiro leia o valor em vigor.

# Limite para um servico especifico
$ systemctl show -p TasksMax nginx.service

# Padrao do sistema (tambem UserTasksMax, etc.)
$ systemctl show -p DefaultTasksMax
TasksMax=4915

DefaultTasksMax frequentemente é uma porcentagem do pid_max do kernel (15% por padrão); sem um override por serviço, este padrão se aplica. Para elevar o limite de um serviço, crie um drop-in.

$ sudo systemctl edit nginx.service

Escreva o seguinte no editor (TasksMax sob [Service]).

[Service]
TasksMax=infinity

Aplique após salvar.

$ sudo systemctl daemon-reload
$ sudo systemctl restart nginx.service
$ systemctl show -p TasksMax nginx.service

O orçamento para usuários de login fica em UserTasksMax (/etc/systemd/logind.conf), aplicado ao user-<UID>.slice. Verifique isto também se um usuário interativo executando muitos processos ficar travado.

TasksMax=infinity significa "sem limite", mas remover o teto permite que um vazamento arraste todo o sistema (pid_max). Use somente após confirmar que a causa não é um vazamento; normalmente defina um valor concreto de "necessário + margem".

Como ajusto os limites do sistema (pid_max / threads-max)?

Conclusão: Se mesmo o total entre todos os usuários é insuficiente, eleve kernel.pid_max e kernel.threads-max. Altere temporariamente com sysctl e persista em /etc/sysctl.d/. Aplica-se a hosts com muitos containers ou alta concorrência.

Verifique os tetos do sistema para PIDs e threads simultâneos.

$ cat /proc/sys/kernel/pid_max
$ cat /proc/sys/kernel/threads-max
4194304
65536

Eleve temporariamente com sysctl -w. Persista colocando um arquivo em /etc/sysctl.d/.

# Alteracao temporaria (perdida no reboot)
$ sudo sysctl -w kernel.pid_max=4194304
$ sudo sysctl -w kernel.threads-max=131072

# Persistir
$ echo 'kernel.pid_max = 4194304' | sudo tee /etc/sysctl.d/99-pids.conf
$ sudo sysctl --system

Em sistemas 64-bit pid_max pode chegar a 4194304 (~4,19 milhões). Mas cada thread consome memória do kernel, então elevar cegamente esgota a memória primeiro. O padrão de threads-max é auto-calculado a partir da RAM, então verifique a margem com free -h antes de elevar.

Mesmo após elevar pid_max / threads-max, se a memória é curta, fork vai falhar com ENOMEM (Cannot allocate memory). Quando EAGAIN vira ENOMEM, memória -- não o limite -- é o gargalo. Veja Lidando com o OOM killer.

Recuperação de emergência: quando nem o shell consegue fazer fork

Conclusão: Mesmo quando você não pode executar um novo comando, comandos builtin do shell executam sem fork. Use kill, exec e controle de jobs para parar os processos descontrolados e liberar o orçamento sem criar nenhum comando externo.

Quando fork está esgotado, você não pode nem iniciar comandos externos como ps ou /usr/bin/kill. Aqui você luta apenas com builtins do bash, que não criam um novo processo.

# O kill builtin (nao o externo /bin/kill) sinaliza um job ou PID
$ kill %1
$ kill 2314

Você pode até localizar PIDs sem comandos externos, espiando /proc com o echo builtin e globbing.

# Listar seus processos via globbing (nenhum comando externo necessario)
$ for p in /proc/[0-9]*; do echo "$p"; done

Se ainda estiver fora de controle, use exec para substituir sua própria sessão descontrolada, ou trabalhe de outra sessão de login existente (uma conexão SSH que ainda está ativa). O último recurso é um reboot pelo console / painel da nuvem, mas sem encontrar a causa raiz (a origem do vazamento) vai recorrer.

Antes que o SSH pare de aceitar novas conexões, mantenha uma sessão de recuperação conectada. Incidentes de esgotamento de limites tendem a "bloquear novos logins quando você percebe", então uma sessão reserva separada da sua de trabalho é um salva-vidas.

Checklist quando ainda não resolve

Conclusão: Um fork EAGAIN significa que "o orçamento de contagem de tarefas está esgotado" está confirmado. Verifique do orçamento mais próximo para fora -- ulimit -u por usuário -> TasksMax do cgroup -> pid_max/threads-max do sistema -- e a causa converge para uma dessas camadas. Não esqueça de confirmar que não há vazamento.

  • [ ] Distinguiu EAGAIN (Resource temporarily unavailable) de ENOMEM (Cannot allocate memory)?
  • [ ] Comparou o valor de ulimit -u com a contagem atual de threads de ps -L?
  • [ ] Identificou o culpado com ps -eo pid,nlwp,... --sort=-nlwp?
  • [ ] É uma contagem saudável, ou um vazamento de threads / processos?
  • [ ] Decidiu se o alvo é uma sessão de login ou um daemon systemd (limits.conf vs TasksMax)?
  • [ ] systemctl show -p TasksMax <servico> é grande o suficiente?
  • [ ] Se o total global é insuficiente, verificou kernel.pid_max / kernel.threads-max?
  • [ ] Após elevar um limite, há margem de memória (free -h)?

Próximas leituras