15%

Ahorra 15%<\/span> en todos los servicios de hosting

Pon a prueba tus habilidades y obtén Descuento<\/span> en cualquier plan de hosting

Usa el código:

Skills
Comenzar
12.12.2023

Inanición de Procesos en Sistemas Operativos: Causas, Mecanismos y Soluciones de Nivel Productivo

La inanición de procesos ocurre cuando a un proceso se le niega indefinidamente el tiempo de CPU, la memoria o el ancho de banda de I/O que necesita para avanzar — no porque los recursos no existan, sino porque la política de planificación favorece consistentemente a otros procesos. A diferencia del deadlock, donde todos los procesos en competencia están bloqueados, la inanición permite que el sistema parezca funcional mientras degrada o detiene silenciosamente cargas de trabajo específicas.

Esta distinción importa operacionalmente: un proceso en inanición no produce errores a nivel del kernel, no genera volcados de memoria y puede no activar los umbrales de alerta estándar — lo que lo convierte en una de las patologías de rendimiento más insidiosas en entornos de servidores multi-tenant y de alta concurrencia.

Lo que la Inanición Significa Realmente a Nivel del Kernel

El término está tomado de la ecología de recursos: un proceso “se muere de hambre” cuando es perpetuamente superado en la competencia por un recurso finito. En los sistemas operativos modernos, el Linux Completely Fair Scheduler (CFS), las colas de prioridad de Windows NT y el planificador BSD ULE implementan mecanismos para prevenir la inanición — sin embargo, sigue apareciendo en producción bajo condiciones específicas.

A nivel del kernel, la inanición se manifiesta como un proceso cuyo tiempo de ejecución virtual (en terminología CFS) o tiempo de espera crece sin límite sin ser nunca seleccionado para ejecución. El proceso permanece en el estado TASK_RUNNING — está listo y es elegible — pero el planificador nunca le otorga un fragmento de CPU porque las tareas de mayor prioridad o más frecuentemente ejecutables siempre lo desplazan.

Distinción técnica clave:

  • Deadlock: Dos o más procesos están mutuamente bloqueados, cada uno esperando un recurso retenido por el otro. El sistema no avanza en absoluto en esas tareas.
  • Inanición: Uno o más procesos son perpetuamente ignorados por el planificador. Otros procesos continúan ejecutándose normalmente.
  • Livelock: Los procesos no están bloqueados pero cambian continuamente de estado en respuesta entre sí sin hacer progreso real.

Causas Raíz de la Inanición de Procesos

Comprender la inanición requiere examinar los mecanismos específicos que la producen, no solo enumerar “recursos limitados” como causa.

1. Inversión de Prioridad Estática Sin Envejecimiento

La mayoría de los planificadores basados en prioridades asignan una prioridad fija o semi-fija a cada proceso. Si un proceso de baja prioridad es siempre desplazado por una serie de tareas de prioridad media y alta, nunca se ejecuta. El modo de fallo crítico aquí es la ausencia de envejecimiento — una técnica donde la prioridad efectiva de un proceso se incrementa gradualmente cuanto más tiempo espera. Sin envejecimiento, un trabajo en segundo plano de baja prioridad en un servidor ocupado puede esperar indefinidamente.

En Linux, el rango de valores nice (-20 a +19) y las prioridades en tiempo real (SCHED_FIFO, SCHED_RR) crean exactamente este riesgo. Un proceso ejecutándose bajo SCHED_FIFO con prioridad 99 desplazará a todos los procesos SCHED_OTHER en el mismo núcleo de CPU hasta que ceda voluntariamente o se bloquee.

2. Encolamiento Injusto en los Planificadores de I/O

La inanición de CPU está bien documentada, pero la inanición de I/O es igualmente destructiva y a menudo se pasa por alto. El planificador de I/O de Linux (históricamente CFQ, ahora BFQ o mq-deadline dependiendo de la versión del kernel y el tipo de almacenamiento) gestiona el orden en que se atienden las solicitudes de dispositivos de bloque. Bajo cargas de escritura secuencial intensas — comunes en servidores de bases de datos y aplicaciones con muchos registros — el planificador de I/O puede despriorizar las solicitudes de lectura aleatoria de otros procesos, privándolos efectivamente del acceso al disco.

Este es un problema frecuente en entornos de VPS Hosting donde múltiples inquilinos comparten la infraestructura de almacenamiento subyacente y la contención de I/O es una preocupación operacional real.

3. Presión de Memoria y el OOM Killer

Cuando la RAM física se agota, el Out-Of-Memory (OOM) killer del kernel de Linux selecciona un proceso para terminar basándose en un oom_score. Aunque técnicamente esto es una terminación en lugar de inanición, el estado previo — donde un proceso es repetidamente enviado al disco y nunca recibe suficiente memoria residente para ejecutarse eficientemente — constituye inanición de memoria. El proceso técnicamente se ejecuta pero avanza de forma insignificante debido a los constantes fallos de página y el I/O de intercambio.

4. Contención de Bloqueos e Inanición de Mutex

En aplicaciones multi-hilo, la inanición ocurre a nivel de las primitivas de sincronización. Si un mutex o semáforo usa una política de adquisición no equitativa (último en entrar, primero en salir o selección aleatoria entre hilos en espera), un hilo específico puede ser perpetuamente ignorado aunque el bloqueo se libere frecuentemente. Esto es distinto de la planificación a nivel del SO y ocurre completamente dentro del espacio de usuario o el subsistema de sincronización del kernel.

5. Inanición de Ancho de Banda de Red

En entornos contenerizados y virtualizados, un proceso o contenedor que consume todo el ancho de banda de red disponible puede privar a otros procesos de I/O de red. Sin modelado de tráfico mediante tc (control de tráfico) y cgroups, un único proceso descontrolado puede monopolizar el rendimiento de la NIC.

Inanición vs. Deadlock vs. Livelock: Comparación Técnica

PropiedadInaniciónDeadlockLivelock
Progreso del sistemaSí (otros procesos se ejecutan)No (los procesos bloqueados se detienen)Aparente (sin progreso real)
Estado bloqueadoNo (el proceso es ejecutable)Sí (el proceso espera un recurso)No (el proceso está activo)
Recurso retenidoNoSí (retención circular y espera)No
Auto-resoluciónA veces (con envejecimiento)Nunca (requiere intervención)Raramente
Dificultad de detecciónAlta (sin error explícito)Media (detección de ciclos)Alta (aparece como actividad)
Causa principalPolítica de planificación injustaDependencia circular de recursosBucles reactivos de cambio de estado
Señal del kernel de LinuxNingunaNinguna (posible soft lockup)Ninguna

Cómo los Planificadores Modernos Abordan la Inanición

El Linux Completely Fair Scheduler (CFS)

CFS, introducido en el kernel de Linux 2.6.23, aborda la inanición rastreando el tiempo de ejecución virtual (vruntime) para cada proceso. El planificador siempre selecciona el proceso con el vruntime más bajo — lo que significa que los procesos que han recibido menos tiempo de CPU son sistemáticamente priorizados. Este diseño hace que la inanición pura de CPU sea casi imposible bajo CFS para procesos SCHED_OTHER.

Sin embargo, CFS no protege contra la inanición causada por procesos en tiempo real. Cualquier proceso planificado bajo SCHED_FIFO o SCHED_RR desplaza a todas las tareas SCHED_OTHER. El parámetro del kernel /proc/sys/kernel/sched_rt_runtime_us (predeterminado: 950.000 microsegundos por segundo) reserva el 5% del tiempo de CPU para tareas no en tiempo real precisamente para evitar esto.

Envejecimiento de Prioridades

Los algoritmos clásicos de envejecimiento incrementan la prioridad efectiva de un proceso en una cantidad fija por cada ciclo de planificación que pasa esperando. Una vez que la prioridad efectiva alcanza el nivel más alto, se garantiza la ejecución del proceso. Después de ejecutarse, su prioridad se restablece a su valor base. Esta es la solución de referencia para la inanición basada en prioridades y está implementada de diversas formas en Windows NT, Solaris y los planificadores más antiguos de Linux.

Encolamiento Justo y Encolamiento Justo Ponderado (WFQ)

Para recursos de red y I/O, el Encolamiento Justo Ponderado asigna a cada flujo o proceso una parte del ancho de banda proporcional a su peso. Incluso si un flujo de alto peso genera más tráfico, los flujos de bajo peso tienen garantizada una tasa de servicio mínima. Linux implementa esto mediante las disciplinas Hierarchical Token Bucket (HTB) y Stochastic Fair Queuing (SFQ) en el subsistema tc.

Diagnóstico de Inanición en Sistemas Linux en Producción

Identificar la inanición requiere correlacionar múltiples fuentes de datos simultáneamente.

Análisis de Planificación 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

La salida de schedstat proporciona el tiempo acumulado que el proceso pasó esperando en la cola de ejecución (run_delay en nanosegundos) — una medida directa de la inanición de planificación.

Indicadores de Inanición de Memoria

# 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"

Detección de Inanición 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}'

Los procesos en estado D (sueño no interrumpible) están bloqueados en I/O. Una población persistente de procesos en estado D es un fuerte indicador de inanición de I/O o saturación del subsistema de almacenamiento.

Soluciones de Nivel Productivo y Estrategias de Mitigación

Implementar cgroups v2 para Aislamiento de Recursos

Control Groups (cgroups v2) proporcionan el mecanismo más robusto para prevenir la inanición en entornos multi-proceso y contenerizados. Al asignar cuotas explícitas de CPU, memoria y I/O a grupos de procesos, se garantizan asignaciones mínimas de recursos independientemente de la carga del 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

El peso de CPU en cgroups v2 usa un rango de 1–10000, donde el valor predeterminado es 100. Un grupo de procesos con peso 200 recibe el doble de la cuota de CPU que uno con peso 100 bajo contención.

Ajustar el Planificador de Linux para su Carga de Trabajo

# 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 Planificación Apropiadas por Proceso

# 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

La clase SCHED_IDLE (chrt -i) es la herramienta correcta para tareas verdaderamente en segundo plano — solo se ejecuta cuando no existe ningún otro proceso ejecutable, eliminando completamente su capacidad de privar de recursos a otras cargas de trabajo.

Selección del Planificador 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

BFQ (Budget Fair Queuing) está diseñado específicamente para prevenir la inanición de I/O garantizando a cada proceso una parte proporcional del ancho de banda del disco. Es el planificador recomendado para entornos de hosting compartido y servidores de bases de datos.

Control de Ancho de Banda de Red con 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 configuración garantiza a cada clase un mínimo de 100 Mbps mientras permite el uso en ráfaga hasta la capacidad total del enlace de 1 Gbps cuando el ancho de banda está disponible.

Ajuste de Sobrecompromiso de Memoria e Intercambio

# 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

Establecer vm.swappiness=10 instruye al kernel a preferir recuperar la caché de páginas antes que intercambiar memoria de procesos, reduciendo significativamente la probabilidad de inanición de memoria bajo carga moderada.

Inanición en Entornos Virtualizados y Contenerizados

En Servidores Dedicados que ejecutan hipervisores (KVM, VMware ESXi, Hyper-V), la inanición puede ocurrir en dos capas distintas:

Inanición a nivel del hipervisor: Una máquina virtual es privada de ciclos de CPU por el planificador del hipervisor. KVM usa el CFS del kernel anfitrión para la planificación de vCPU, lo que significa que una VM con un peso de cuota de CPU menor puede ser privada de recursos por VMs con pesos mayores bajo contención. El DRS (Distributed Resource Scheduler) de VMware usa cuotas, reservas y límites para controlar esto.

Inanición a nivel del SO invitado: Dentro de la propia VM, se aplican las mismas dinámicas de planificación a nivel del SO. Una carga de trabajo contenerizada ejecutándose bajo Docker o Kubernetes sin límites de recursos explícitos puede monopolizar la CPU y la memoria del SO invitado, privando de recursos a los contenedores co-ubicados.

Para entornos de Kubernetes, siempre defina tanto requests como limits en las especificaciones de los pods:

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

El valor requests determina la ubicación de planificación y el peso de cuota de CPU del cgroup. Sin él, el planificador de Kubernetes no tiene base para una ubicación justa, y el tiempo de ejecución del contenedor asigna pesos predeterminados (iguales) — lo que aún permite la inanición si un contenedor satura consistentemente su límite de CPU.

Inanición en Servidores de Bases de Datos y Aplicaciones

Los motores de bases de datos implementan sus propios planificadores internos que son independientes del planificador del SO. PostgreSQL usa un modelo de proceso por conexión donde cada proceso backend compite por los recursos del SO normalmente, pero la contención de bloqueos dentro de la base de datos (bloqueos a nivel de fila, bloqueos de aviso) puede causar inanición a nivel de aplicación donde consultas específicas esperan indefinidamente la adquisición del bloqueo.

MySQL/InnoDB usa un grupo de hilos con límites de concurrencia configurables (innodb_thread_concurrency). Establecer este valor demasiado bajo causa inanición de consultas mientras los hilos hacen cola esperando ranuras de ejecución. Establecerlo demasiado alto causa thrashing de CPU. El valor inicial recomendado es 2 × number of CPU cores.

Para servidores web, Nginx y Apache tienen perfiles de inanición distintos. El modelo orientado a eventos de Nginx es inherentemente resistente a la inanición de trabajadores, pero el agotamiento del grupo de conexiones upstream (p. ej., a PHP-FPM o una API backend) crea inanición a nivel de aplicación. El MPM prefork de Apache puede agotar su límite MaxRequestWorkers, causando que las nuevas conexiones hagan cola indefinidamente — una forma de inanición de conexiones.

Estas consideraciones son directamente relevantes al configurar un VPS con cPanel para cargas de trabajo de hosting web compartido, donde múltiples sitios compiten por grupos de trabajadores PHP-FPM y límites de conexiones MySQL.

Infraestructura de Monitoreo para la Prevención de Inanición

El diagnóstico reactivo es insuficiente para los sistemas en producción. Una pila de monitoreo proactivo debe incluir:

Métricas de Prometheus + Node Exporter a vigilar:

  • node_schedstat_waiting_seconds_total — tiempo de espera acumulado en la cola de ejecución de CPU por CPU
  • node_vmstat_pgmajfault — fallos de página mayores que indican presión de memoria
  • node_disk_io_time_weighted_seconds_total — saturación de la cola de I/O
  • node_pressure_cpu_waiting_seconds_total — presión de CPU de Linux PSI (Pressure Stall Information)
  • node_pressure_memory_full_seconds_total — tiempo de parada completa de memoria PSI

Linux PSI (disponible desde el kernel 4.20) es el indicador de inanición más directo disponible en el kernel. Informa el porcentaje de tiempo que las tareas estuvieron detenidas esperando recursos de CPU, memoria o I/O:

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

Formato de salida: some avg10=X.XX avg60=X.XX avg300=X.XX total=NNNN donde some indica que al menos una tarea estuvo detenida. Los valores superiores al 10–15% en avg60 requieren investigación inmediata.

Para equipos que gestionan Paneles de Control VPS o pilas de servidores personalizadas, integrar métricas PSI en paneles de Grafana proporciona advertencia temprana antes de que la inanición degrade el rendimiento visible para el usuario.

Matriz de Decisión Práctica: Elegir el Mecanismo Anti-Inanición Correcto

SíntomaTipo de RecursoHerramienta RecomendadaObjetivo de Configuración
Los trabajos en segundo plano nunca se completanCPUSCHED_IDLE o nice +19Eliminar la competencia de CPU en segundo plano
Picos de latencia interactiva bajo cargaCPUAjuste de CFS + peso de CPU de cgroups v2Garantizar la cuota del proceso interactivo
Tiempo de espera agotado en consultas de base de datosCPU + Bloqueoinnodb_thread_concurrency, tiempo de espera de bloqueoLimitar el tiempo de espera de bloqueo
Los trabajos intensivos en disco bloquean el servicio webI/OPlanificador BFQ + io.weight de cgroups v2Asignación proporcional de I/O
Terminaciones OOM de contenedores bajo cargaMemoriamemory.min de cgroups v2 + vm.swappinessGarantizar memoria residente mínima
Un proceso intensivo en red priva a otrosRedHTB + SFQ mediante tcGarantía de ancho de banda por clase
VM privada de recursos por el hipervisorvCPUReservas/cuotas de CPU del hipervisorReservar ciclos mínimos de vCPU

Conclusiones Técnicas Clave

  • Nunca confíe en la planificación predeterminada para servidores con cargas de trabajo mixtas. Clasifique explícitamente los procesos usando chrt, nice y cgroups v2 según su sensibilidad a la latencia y prioridad empresarial.
  • Habilite el monitoreo PSI (/proc/pressure/*) en todos los sistemas Linux en producción. Es el indicador de inanición en tiempo real más preciso del kernel y tiene una sobrecarga casi nula.
  • Use BFQ para discos giratorios y cualquier dispositivo NVMe que sirva cargas de trabajo mixtas aleatorias/secuenciales en entornos multi-tenant. Las garantías de equidad valen la sobrecarga marginal de rendimiento.
  • Establezca las solicitudes de recursos de Kubernetes sin excepción. Un requests.cpu no establecido no es “ilimitado” — es una responsabilidad de planificación que permite la inanición de CPU a nivel de contenedor.
  • Distinga la inanición del deadlock antes de intervenir. Matar y reiniciar un proceso en inanición no corrige el desequilibrio de planificación subyacente; solo elimina temporalmente el síntoma.
  • Audite las asignaciones de prioridad en tiempo real (SCHED_FIFO/SCHED_RR) en cualquier sistema donde estén en uso. Un único proceso en tiempo real mal configurado puede privar de recursos a todas las cargas de trabajo de prioridad normal en un núcleo de CPU indefinidamente.
  • Para entornos de Hosting Web Compartido, aplique cuotas de CPU y I/O por cuenta a nivel de cgroup en lugar de depender únicamente de la limitación de velocidad a nivel de aplicación.

Preguntas Frecuentes

¿Cuál es la diferencia entre inanición y deadlock en un sistema operativo?

El deadlock ocurre cuando dos o más procesos están permanentemente bloqueados, cada uno reteniendo un recurso que el otro necesita — ningún proceso avanza. La inanición ocurre cuando un proceso es perpetuamente ignorado por el planificador a pesar de ser ejecutable; otros procesos continúan ejecutándose normalmente. El deadlock requiere romper una dependencia circular; la inanición requiere corregir la política de planificación, típicamente implementando envejecimiento o encolamiento justo.

¿Cómo previene el planificador CFS de Linux la inanición de CPU?

CFS rastrea un tiempo de ejecución virtual (vruntime) para cada proceso y siempre selecciona el proceso con el vruntime más bajo para su ejecución. Esto garantiza que los procesos que reciben menos tiempo de CPU sean sistemáticamente priorizados, haciendo que la inanición indefinida de CPU de los procesos SCHED_OTHER sea casi imposible. Sin embargo, los procesos en tiempo real (SCHED_FIFO, SCHED_RR) omiten CFS por completo y aún pueden privar de recursos a los procesos normales si el parámetro sched_rt_runtime_us no está configurado correctamente.

¿Cómo puedo detectar si un proceso está siendo privado de recursos en un servidor Linux?

Lea /proc/<PID>/schedstat para verificar el tiempo acumulado de espera en la cola de ejecución. Monitoree /proc/pressure/cpu para métricas de parada PSI. Use perf sched latency --sort max para identificar procesos con latencia de planificación anormalmente alta. Los procesos en estado persistente D visibles en la salida de ps aux indican inanición de I/O en lugar de inanición de CPU.

¿Afecta la inanición de procesos a los entornos VPS y de servidores en la nube de manera diferente que al hardware físico?

Sí. En un VPS, la inanición puede ocurrir tanto en la capa del hipervisor (el planificador del hipervisor negando tiempo de vCPU a su VM) como dentro del SO invitado. La inanición a nivel del hipervisor es invisible para las herramientas de monitoreo estándar del SO y requiere métricas específicas del hipervisor o un tiempo de robo notable (%st en la salida de top). Un tiempo de robo alto — típicamente superior al 5–10% sostenido — indica que el hipervisor no está entregando los ciclos de vCPU a los que su VM tiene derecho.

¿Cuál es la forma más rápida de evitar que un proceso específico prive de recursos a otros en un servidor ocupado?

Asígnelo a la clase de planificación SCHED_IDLE con chrt -i -p 0 <PID>. Esta clase solo se ejecuta cuando no existe ningún otro proceso ejecutable, garantizando que no pueda privar de recursos a ninguna otra carga de trabajo. Para procesos en segundo plano intensivos en I/O, establezca adicionalmente su prioridad de I/O a la clase inactiva: ionice -c 3 -p <PID>. Combinar ambos elimina el proceso como fuente de inanición de CPU y I/O con dos comandos y sin cambios en la aplicación.

15%

Ahorra 15%<\/span> en todos los servicios de hosting

Pon a prueba tus habilidades y obtén Descuento<\/span> en cualquier plan de hosting

Usa el código:

Skills
Comenzar