15%

Poupe 15% em todos os serviços

Teste as suas habilidades e obtenha Desconto em qualquer plano

Utilizar o código:

Skills
Começar a trabalhar
12.12.2023

Inanição de Processos em Sistemas Operacionais: Causas, Mecanismos e Soluções de Nível de Produção

A privação de processos ocorre quando um processo é indefinidamente negado o tempo de CPU, memória ou largura de banda de I/O de que necessita para progredir — não porque os recursos não existam, mas porque a política de escalonamento favorece consistentemente outros processos. Ao contrário do deadlock, onde todos os processos concorrentes estão bloqueados, a privação permite que o sistema pareça funcional enquanto degrada ou interrompe silenciosamente cargas de trabalho específicas.

Esta distinção é operacionalmente importante: um processo privado não produz erros ao nível do kernel, não gera dumps de falha e pode não acionar os limites de alerta padrão — tornando-o uma das patologias de desempenho mais insidiosas em ambientes de servidor multi-inquilino e de alta concorrência.

O Que a Privação Realmente Significa ao Nível do Kernel

O termo é emprestado da ecologia de recursos: um processo “sofre privação” quando é perpetuamente superado na competição por um recurso finito. Nos sistemas operativos modernos, o Linux Completely Fair Scheduler (CFS), as filas de prioridade do Windows NT e o escalonador BSD ULE implementam mecanismos para prevenir a privação — no entanto, ela ainda surge em produção em condições específicas.

Ao nível do kernel, a privação manifesta-se como um processo cujo tempo de execução virtual (na terminologia CFS) ou tempo de espera cresce ilimitadamente sem nunca ser selecionado para execução. O processo permanece no estado TASK_RUNNING — está pronto e elegível — mas o escalonador nunca lhe concede uma fatia de CPU porque tarefas de maior prioridade ou mais frequentemente executáveis sempre o preemptam.

Distinção técnica fundamental:

  • Deadlock: Dois ou mais processos estão mutuamente bloqueados, cada um aguardando por um recurso detido pelo outro. O sistema não faz qualquer progresso nessas tarefas.
  • Privação: Um ou mais processos são perpetuamente ignorados pelo escalonador. Outros processos continuam a funcionar normalmente.
  • Livelock: Os processos não estão bloqueados, mas mudam continuamente de estado em resposta uns aos outros sem fazer progresso real.

Causas Raiz da Privação de Processos

Compreender a privação requer examinar os mecanismos específicos que a produzem, não apenas listar “recursos limitados” como causa.

1. Inversão de Prioridade Estática Sem Envelhecimento

A maioria dos escalonadores baseados em prioridade atribui uma prioridade fixa ou semi-fixa a cada processo. Se um processo de baixa prioridade é sempre preemptado por um fluxo de tarefas de média e alta prioridade, nunca é executado. O modo de falha crítico aqui é a ausência de envelhecimento — uma técnica onde a prioridade efetiva de um processo é incrementalmente aumentada quanto mais tempo espera. Sem envelhecimento, uma tarefa de fundo de baixa prioridade num servidor ocupado pode esperar indefinidamente.

No Linux, o intervalo de valores nice (-20 a +19) e as prioridades em tempo real (SCHED_FIFO, SCHED_RR) criam exatamente este risco. Um processo a correr sob SCHED_FIFO com prioridade 99 preemptará todos os processos SCHED_OTHER no mesmo núcleo de CPU até que ceda voluntariamente ou bloqueie.

2. Enfileiramento Injusto nos Escalonadores de I/O

A privação de CPU está bem documentada, mas a privação de I/O é igualmente destrutiva e frequentemente ignorada. O escalonador de I/O do Linux (historicamente CFQ, agora BFQ ou mq-deadline dependendo da versão do kernel e do tipo de armazenamento) gere a ordem em que os pedidos de dispositivos de bloco são servidos. Sob cargas de trabalho de escrita sequencial intensa — comuns em servidores de bases de dados e aplicações com uso intensivo de registos — o escalonador de I/O pode desprioritizar pedidos de leitura aleatória de outros processos, efetivamente privando-os de acesso ao disco.

Este é um problema frequente em ambientes de VPS Hosting onde múltiplos inquilinos partilham a infraestrutura de armazenamento subjacente e a contenção de I/O é uma preocupação operacional real.

3. Pressão de Memória e o OOM Killer

Quando a RAM física está esgotada, o OOM killer (Out-Of-Memory) do kernel Linux seleciona um processo para terminar com base num oom_score. Embora isto seja tecnicamente uma terminação em vez de privação, o estado precursor — onde um processo é repetidamente transferido para o disco e nunca recebe memória residente suficiente para executar eficientemente — constitui privação de memória. O processo tecnicamente corre, mas faz progresso negligenciável devido a falhas de página constantes e I/O de swap.

4. Contenção de Bloqueios e Privação de Mutex

Em aplicações multi-thread, a privação ocorre ao nível da primitiva de sincronização. Se um mutex ou semáforo usa uma política de aquisição não equitativa (último a entrar, primeiro a sair ou seleção aleatória entre threads em espera), uma thread específica pode ser perpetuamente ignorada mesmo que o bloqueio seja frequentemente libertado. Isto é distinto do escalonamento ao nível do SO e ocorre inteiramente no espaço do utilizador ou no subsistema de sincronização do kernel.

5. Privação de Largura de Banda de Rede

Em ambientes contentorizados e virtualizados, um processo ou contentor que consuma toda a largura de banda de rede disponível pode privar outros processos de I/O de rede. Sem modelação de tráfego via tc (controlo de tráfego) e cgroups, um único processo descontrolado pode monopolizar o débito da NIC.

Privação vs. Deadlock vs. Livelock: Comparação Técnica

PropriedadePrivaçãoDeadlockLivelock
Progresso do sistemaSim (outros processos correm)Não (processos bloqueados param)Aparente (sem progresso real)
Estado bloqueadoNão (processo está executável)Sim (processo aguarda recurso)Não (processo está ativo)
Recurso detidoNãoSim (espera circular com detenção)Não
Auto-resoluçãoÀs vezes (com envelhecimento)Nunca (requer intervenção)Raramente
Dificuldade de deteçãoAlta (sem erro explícito)Média (deteção de ciclos)Alta (aparece como atividade)
Causa principalPolítica de escalonamento injustaDependência circular de recursosCiclos reativos de mudança de estado
Sinal do kernel LinuxNenhumNenhum (possível soft lockup)Nenhum

Como os Escalonadores Modernos Abordam a Privação

O Linux Completely Fair Scheduler (CFS)

O CFS, introduzido no kernel Linux 2.6.23, aborda a privação rastreando o tempo de execução virtual (vruntime) para cada processo. O escalonador seleciona sempre o processo com o menor vruntime — o que significa que os processos que receberam menos tempo de CPU são sistematicamente priorizados. Este design torna a privação pura de CPU quase impossível sob CFS para processos SCHED_OTHER.

No entanto, o CFS não protege contra a privação por processos em tempo real. Qualquer processo escalonado sob SCHED_FIFO ou SCHED_RR preempta todas as tarefas SCHED_OTHER. O parâmetro do kernel /proc/sys/kernel/sched_rt_runtime_us (padrão: 950.000 microssegundos por segundo) reserva 5% do tempo de CPU para tarefas não em tempo real precisamente para prevenir isto.

Envelhecimento de Prioridade

Os algoritmos clássicos de envelhecimento incrementam a prioridade efetiva de um processo por um valor fixo para cada ciclo de escalonamento que passa em espera. Assim que a prioridade efetiva atinge o nível mais alto, a execução do processo é garantida. Após correr, a sua prioridade é reposta ao valor base. Esta é a solução clássica para a privação baseada em prioridade e está implementada em várias formas no Windows NT, Solaris e escalonadores Linux mais antigos.

Enfileiramento Justo e Enfileiramento Justo Ponderado (WFQ)

Para recursos de rede e I/O, o Enfileiramento Justo Ponderado atribui a cada fluxo ou processo uma quota de largura de banda proporcional ao seu peso. Mesmo que um fluxo de alto peso gere mais tráfego, os fluxos de baixo peso têm garantida uma taxa mínima de serviço. O Linux implementa isto através das disciplinas Hierarchical Token Bucket (HTB) e Stochastic Fair Queuing (SFQ) no subsistema tc.

Diagnosticar a Privação em Sistemas Linux de Produção

Identificar a privação requer correlacionar múltiplas fontes de dados simultaneamente.

Análise de Escalonamento de CPU

# Check per-process CPU wait time and scheduling statistics
cat /proc/<PID>/schedstat

# Monitor scheduler latency with perf
perf sched latency --sort max

# Identify processes with high voluntary/involuntary context switches
pidstat -w 1 10

# Check real-time process priorities that may be starving others
ps -eo pid,comm,cls,pri,ni --sort=-pri | head -20

O resultado de schedstat fornece o tempo cumulativo que o processo passou à espera na fila de execução (run_delay em nanossegundos) — uma medida direta da privação de escalonamento.

Indicadores de Privação de Memória

# Check swap activity — high si/so values indicate memory starvation
vmstat 1 10

# Identify processes with high major page fault rates
pidstat -r 1 10

# Check OOM kill history
dmesg | grep -i "oom|killed process"

# Inspect per-process memory pressure
cat /proc/<PID>/status | grep -E "VmRSS|VmSwap|VmPeak"

Deteção de Privação de I/O

# Per-process I/O wait statistics
iotop -b -n 5

# Block device queue depth and wait times
iostat -x 1 5

# Check I/O scheduler in use for each block device
cat /sys/block/sda/queue/scheduler

# Identify processes blocked on I/O
ps aux | awk '$8 ~ /D/ {print}'

Os processos no estado D (sono não interrompível) estão bloqueados em I/O. Uma população persistente de processos no estado D é um forte indicador de privação de I/O ou saturação do subsistema de armazenamento.

Soluções de Nível de Produção e Estratégias de Mitigação

Implementar cgroups v2 para Isolamento de Recursos

Os Control Groups (cgroups v2) fornecem o mecanismo mais robusto para prevenir a privação em ambientes multi-processo e contentorizados. Ao atribuir quotas explícitas de CPU, memória e I/O a grupos de processos, garante alocações mínimas de recursos independentemente da carga do sistema.

# Create a cgroup with CPU weight (higher weight = more CPU share)
mkdir /sys/fs/cgroup/my_service
echo "100" > /sys/fs/cgroup/my_service/cpu.weight

# Set memory limit to prevent memory starvation of other groups
echo "2G" > /sys/fs/cgroup/my_service/memory.max

# Assign process to cgroup
echo <PID> > /sys/fs/cgroup/my_service/cgroup.procs

O peso de CPU no cgroups v2 usa um intervalo de 1–10000, onde o padrão é 100. Um grupo de processos com peso 200 recebe o dobro da quota de CPU de um com peso 100 sob contenção.

Ajustar o Escalonador Linux para a Sua Carga de Trabalho

# Increase scheduler migration cost to reduce cache thrashing (latency-sensitive workloads)
echo 500000 > /proc/sys/kernel/sched_migration_cost_ns

# Reduce scheduler granularity for more frequent preemption (throughput workloads)
echo 1000000 > /proc/sys/kernel/sched_min_granularity_ns

# Ensure real-time tasks cannot starve normal tasks
echo 950000 > /proc/sys/kernel/sched_rt_runtime_us

Aplicar Políticas de Escalonamento Adequadas por Processo

# Set a process to batch scheduling (explicitly low-priority, won't starve interactive tasks)
chrt -b -p 0 <PID>

# Set a CPU-intensive background job to idle scheduling class
chrt -i -p 0 <PID>

# Adjust nice value for a running process
renice -n 10 -p <PID>

# Run a new command with reduced priority
nice -n 15 ./my_background_script.sh

A classe SCHED_IDLE (chrt -i) é a ferramenta correta para tarefas verdadeiramente em segundo plano — só corre quando não existe nenhum outro processo executável, eliminando completamente a sua capacidade de privar outras cargas de trabalho.

Seleção do Escalonador de I/O

# For NVMe SSDs (low-latency, no rotational penalty): use none or mq-deadline
echo "mq-deadline" > /sys/block/nvme0n1/queue/scheduler

# For HDDs with mixed workloads: use bfq for fairness
echo "bfq" > /sys/block/sda/queue/scheduler

# Make persistent across reboots (add to /etc/udev/rules.d/)
echo 'ACTION=="add|change", KERNEL=="sda", ATTR{queue/scheduler}="bfq"' 
  > /etc/udev/rules.d/60-scheduler.rules

O BFQ (Budget Fair Queuing) foi especificamente concebido para prevenir a privação de I/O, garantindo a cada processo uma quota proporcional de largura de banda do disco. É o escalonador recomendado para ambientes de alojamento partilhado e servidores de bases de dados.

Controlo de Largura de Banda de Rede com tc

# Create a root HTB qdisc on the primary interface
tc qdisc add dev eth0 root handle 1: htb default 30

# Add a parent class with total bandwidth
tc class add dev eth0 parent 1: classid 1:1 htb rate 1gbit

# Add child classes with guaranteed minimums (prevents starvation)
tc class add dev eth0 parent 1:1 classid 1:10 htb rate 100mbit ceil 1gbit
tc class add dev eth0 parent 1:1 classid 1:20 htb rate 100mbit ceil 1gbit

# Add SFQ leaf to each class for per-flow fairness
tc qdisc add dev eth0 parent 1:10 handle 10: sfq perturb 10
tc qdisc add dev eth0 parent 1:20 handle 20: sfq perturb 10

Esta configuração garante a cada classe um mínimo de 100 Mbps enquanto permite uso em rajada até à capacidade total de 1 Gbps da ligação quando a largura de banda está disponível.

Ajuste de Sobrecomprometimento de Memória e Swap

# Reduce swappiness to minimize swap-induced memory starvation
echo 10 > /proc/sys/vm/swappiness

# Enable memory overcommit accounting (prevents OOM from surprising processes)
echo 2 > /proc/sys/vm/overcommit_memory

# Set overcommit ratio (total allocatable = RAM * ratio + swap)
echo 80 > /proc/sys/vm/overcommit_ratio

Definir vm.swappiness=10 instrui o kernel a preferir recuperar a cache de páginas em vez de fazer swap da memória do processo, reduzindo significativamente a probabilidade de privação de memória sob carga moderada.

Privação em Ambientes Virtualizados e Contentorizados

Em Servidores Dedicados a correr hipervisores (KVM, VMware ESXi, Hyper-V), a privação pode ocorrer em duas camadas distintas:

Privação ao nível do hipervisor: Uma máquina virtual é negada ciclos de CPU pelo escalonador do hipervisor. O KVM usa o CFS do kernel anfitrião para o escalonamento de vCPU, o que significa que uma VM com um peso de quota de CPU menor pode ser privada por VMs com pesos maiores sob contenção. O DRS (Distributed Resource Scheduler) da VMware usa quotas, reservas e limites para controlar isto.

Privação ao nível do SO convidado: Dentro da própria VM, aplicam-se as mesmas dinâmicas de escalonamento ao nível do SO. Uma carga de trabalho contentorizada a correr sob Docker ou Kubernetes sem limites de recursos explícitos pode monopolizar a CPU e memória do SO convidado, privando contentores co-localizados.

Para ambientes Kubernetes, defina sempre tanto requests como limits nas especificações de pod:

resources:
  requests:
    cpu: "250m"
    memory: "512Mi"
  limits:
    cpu: "1000m"
    memory: "1Gi"

O valor requests determina o posicionamento de escalonamento e o peso da quota de CPU do cgroup. Sem ele, o escalonador Kubernetes não tem base para posicionamento justo, e o runtime do contentor atribui pesos padrão (iguais) — o que ainda permite privação se um contentor saturar consistentemente o seu limite de CPU.

Privação em Servidores de Bases de Dados e Aplicações

Os motores de bases de dados implementam os seus próprios escalonadores internos que são independentes do escalonador do SO. O PostgreSQL usa um modelo de processo por ligação onde cada processo de backend compete normalmente por recursos do SO, mas a contenção de bloqueios dentro da base de dados (bloqueios ao nível de linha, bloqueios consultivos) pode causar privação ao nível da aplicação onde consultas específicas aguardam indefinidamente pela aquisição de bloqueios.

O MySQL/InnoDB usa um pool de threads com limites de concorrência configuráveis (innodb_thread_concurrency). Definir este valor demasiado baixo causa privação de consultas à medida que as threads ficam em fila à espera de slots de execução. Defini-lo demasiado alto causa thrashing de CPU. O valor inicial recomendado é 2 × number of CPU cores.

Para servidores web, o Nginx e o Apache têm perfis de privação distintos. O modelo orientado a eventos do Nginx é inerentemente resistente à privação de workers, mas o esgotamento do pool de ligações upstream (por exemplo, para PHP-FPM ou uma API de backend) cria privação ao nível da aplicação. O MPM prefork do Apache pode esgotar o seu limite MaxRequestWorkers, fazendo com que novas ligações fiquem em fila indefinidamente — uma forma de privação de ligações.

Estas considerações são diretamente relevantes ao configurar um VPS com cPanel para cargas de trabalho de alojamento web partilhado, onde múltiplos sites competem por pools de workers PHP-FPM e limites de ligações MySQL.

Infraestrutura de Monitorização para Prevenção de Privação

O diagnóstico reativo é insuficiente para sistemas de produção. Uma pilha de monitorização proativa deve incluir:

Métricas do Prometheus + Node Exporter a observar:

  • node_schedstat_waiting_seconds_total — tempo cumulativo de espera na fila de execução de CPU por CPU
  • node_vmstat_pgmajfault — falhas de página principais indicando pressão de memória
  • node_disk_io_time_weighted_seconds_total — saturação da fila de I/O
  • node_pressure_cpu_waiting_seconds_total — pressão de CPU do Linux PSI (Pressure Stall Information)
  • node_pressure_memory_full_seconds_total — tempo de paragem total de memória PSI

O Linux PSI (disponível desde o kernel 4.20) é o indicador de privação mais direto disponível no kernel. Reporta a percentagem de tempo em que as tarefas estavam paradas à espera de recursos de CPU, memória ou I/O:

# Real-time PSI monitoring
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/io

Formato de saída: some avg10=X.XX avg60=X.XX avg300=X.XX total=NNNN onde some indica que pelo menos uma tarefa estava parada. Valores acima de 10–15% em avg60 justificam investigação imediata.

Para equipas que gerem Painéis de Controlo VPS ou pilhas de servidor personalizadas, integrar métricas PSI em dashboards Grafana fornece aviso antecipado antes que a privação degrade o desempenho voltado para o utilizador.

Matriz de Decisão Prática: Escolher o Mecanismo Anti-Privação Correto

SintomaTipo de RecursoFerramenta RecomendadaAlvo de Configuração
Tarefas em segundo plano nunca completamCPUSCHED_IDLE ou nice +19Eliminar competição de CPU em segundo plano
Picos de latência interativa sob cargaCPUAjuste CFS + peso de CPU cgroups v2Garantir quota de processo interativo
Consultas de base de dados a expirarCPU + Bloqueioinnodb_thread_concurrency, timeout de bloqueioLimitar tempo de espera de bloqueio
Tarefas intensivas em disco bloqueiam o serviço webI/OEscalonador BFQ + cgroups v2 io.weightAlocação proporcional de I/O
OOM kills de contentor sob cargaMemóriacgroups v2 memory.min + vm.swappinessGarantir memória residente mínima
Processo intensivo em rede priva outrosRedeHTB + SFQ via tcGarantia de largura de banda por classe
VM privada pelo hipervisorvCPUReservas/quotas de CPU do hipervisorReservar ciclos mínimos de vCPU

Principais Conclusões Técnicas

  • Nunca confie no escalonamento padrão para servidores com cargas de trabalho mistas. Classifique explicitamente os processos usando chrt, nice e cgroups v2 com base na sua sensibilidade à latência e prioridade de negócio.
  • Ative a monitorização PSI (/proc/pressure/*) em todos os sistemas Linux de produção. É o indicador de privação em tempo real mais preciso no kernel e tem overhead quase nulo.
  • Use BFQ para discos rotativos e qualquer dispositivo NVMe que sirva cargas de trabalho mistas aleatórias/sequenciais em ambientes multi-inquilino. As garantias de equidade valem o overhead marginal de débito.
  • Defina pedidos de recursos Kubernetes sem exceção. Um requests.cpu não definido não é “ilimitado” — é uma responsabilidade de escalonamento que permite a privação de CPU ao nível do contentor.
  • Distinga a privação do deadlock antes de intervir. Matar e reiniciar um processo privado não corrige o desequilíbrio de escalonamento subjacente; apenas remove temporariamente o sintoma.
  • Audite as atribuições de prioridade em tempo real (SCHED_FIFO/SCHED_RR) em qualquer sistema onde estejam em uso. Um único processo em tempo real mal configurado pode privar indefinidamente todas as cargas de trabalho de prioridade normal num núcleo de CPU.
  • Para ambientes de Alojamento Web Partilhado, aplique quotas de CPU e I/O por conta ao nível do cgroup em vez de depender exclusivamente da limitação de taxa ao nível da aplicação.

Perguntas Frequentes

Qual é a diferença entre privação e deadlock num sistema operativo?

O deadlock ocorre quando dois ou mais processos estão permanentemente bloqueados, cada um detendo um recurso de que o outro necessita — nenhum processo faz progresso. A privação ocorre quando um processo é perpetuamente ignorado pelo escalonador apesar de estar executável; outros processos continuam a executar normalmente. O deadlock requer quebrar uma dependência circular; a privação requer corrigir a política de escalonamento, tipicamente implementando envelhecimento ou enfileiramento justo.

Como é que o escalonador Linux CFS previne a privação de CPU?

O CFS rastreia um tempo de execução virtual (vruntime) para cada processo e seleciona sempre o processo com o menor vruntime para execução. Isto garante que os processos que recebem menos tempo de CPU são sistematicamente priorizados, tornando a privação indefinida de CPU de processos SCHED_OTHER quase impossível. No entanto, os processos em tempo real (SCHED_FIFO, SCHED_RR) contornam completamente o CFS e ainda podem privar processos normais se o parâmetro sched_rt_runtime_us não estiver definido corretamente.

Como posso detetar se um processo está a ser privado num servidor Linux?

Leia /proc/<PID>/schedstat para verificar o tempo cumulativo de espera na fila de execução. Monitorize /proc/pressure/cpu para métricas de paragem PSI. Use perf sched latency --sort max para identificar processos com latência de escalonamento anormalmente alta. Processos em estado persistente D visíveis no resultado de ps aux indicam privação de I/O em vez de privação de CPU.

A privação de processos afeta ambientes VPS e de servidor cloud de forma diferente do que bare metal?

Sim. Num VPS, a privação pode ocorrer tanto na camada do hipervisor (o escalonador do hipervisor a negar tempo de vCPU à sua VM) como dentro do SO convidado. A privação ao nível do hipervisor é invisível para as ferramentas de monitorização padrão do SO e requer métricas específicas do hipervisor ou tempo de roubo notável (%st no resultado de top). Tempo de roubo elevado — tipicamente acima de 5–10% sustentado — indica que o hipervisor não está a entregar os ciclos de vCPU a que a sua VM tem direito.

Qual é a forma mais rápida de evitar que um processo específico prive outros num servidor ocupado?

Atribua-o à classe de escalonamento SCHED_IDLE com chrt -i -p 0 <PID>. Esta classe só executa quando não existe nenhum outro processo executável, garantindo que não pode privar nenhuma outra carga de trabalho. Para processos de fundo intensivos em I/O, defina adicionalmente a sua prioridade de I/O para a classe idle: ionice -c 3 -p <PID>. Combinar ambos elimina o processo como fonte de privação de CPU e I/O com dois comandos e zero alterações à aplicação.

15%

Poupe 15% em todos os serviços

Teste as suas habilidades e obtenha Desconto em qualquer plano

Utilizar o código:

Skills
Começar a trabalhar