Zagłodzenie procesów w systemach operacyjnych: przyczyny, mechanizmy i rozwiązania klasy produkcyjnej
Głodzenie procesów (ang. process starvation) występuje, gdy proces jest bezterminowo pozbawiany czasu CPU, pamięci lub przepustowości I/O potrzebnych do działania — nie dlatego, że zasoby nie istnieją, ale dlatego, że polityka szeregowania konsekwentnie faworyzuje inne procesy. W przeciwieństwie do zakleszczenia (deadlock), gdzie wszystkie konkurujące procesy są zablokowane, głodzenie pozwala systemowi wyglądać na sprawny, jednocześnie po cichu degradując lub zatrzymując określone zadania.
To rozróżnienie ma znaczenie operacyjne: głodzony proces nie generuje błędów na poziomie jądra, nie tworzy zrzutów awaryjnych i może nie wyzwalać standardowych progów alertów — co czyni go jedną z najbardziej podstępnych patologii wydajnościowych w środowiskach wielodostępnych i wysokowspółbieżnych serwerów.
Co głodzenie faktycznie oznacza na poziomie jądra
Termin ten zapożyczono z ekologii zasobów: proces „głoduje”, gdy jest stale wyprzedzany w dostępie do ograniczonego zasobu. We współczesnych systemach operacyjnych Linux Completely Fair Scheduler (CFS), kolejki priorytetowe Windows NT oraz scheduler BSD ULE implementują mechanizmy zapobiegające głodzeniu — a mimo to pojawia się ono w środowiskach produkcyjnych w określonych warunkach.
Na poziomie jądra głodzenie objawia się jako proces, którego wirtualny czas wykonania (w terminologii CFS) lub czas oczekiwania rośnie nieograniczenie bez bycia kiedykolwiek wybranym do wykonania. Proces pozostaje w stanie TASK_RUNNING — jest gotowy i kwalifikuje się — ale scheduler nigdy nie przydziela mu wycinka CPU, ponieważ zadania o wyższym priorytecie lub częściej uruchamiane zawsze go wywłaszczają.
Kluczowe rozróżnienie techniczne:
- Zakleszczenie (Deadlock): Dwa lub więcej procesów jest wzajemnie zablokowanych, każdy czeka na zasób przetrzymywany przez drugi. System nie czyni żadnych postępów w tych zadaniach.
- Głodzenie (Starvation): Jeden lub więcej procesów jest stale pomijanych przez scheduler. Inne procesy działają normalnie.
- Livelock: Procesy nie są zablokowane, ale ciągle zmieniają stan w odpowiedzi na siebie nawzajem, nie czyniąc rzeczywistych postępów.
Główne przyczyny głodzenia procesów
Zrozumienie głodzenia wymaga zbadania konkretnych mechanizmów, które je powodują, a nie tylko wymieniania „ograniczonych zasobów” jako przyczyny.
1. Statyczna inwersja priorytetów bez starzenia
Większość schedulerów opartych na priorytetach przypisuje każdemu procesowi stały lub półstały priorytet. Jeśli proces o niskim priorytecie jest zawsze wywłaszczany przez strumień zadań o średnim i wysokim priorytecie, nigdy nie jest wykonywany. Krytycznym trybem awarii jest tutaj brak starzenia (aging) — techniki, w której efektywny priorytet procesu jest stopniowo zwiększany im dłużej czeka. Bez starzenia niskopriorytетowe zadanie w tle na zajętym serwerze może czekać w nieskończoność.
W systemie Linux zakres wartości nice (-20 do +19) oraz priorytety czasu rzeczywistego (SCHED_FIFO, SCHED_RR) stwarzają dokładnie to ryzyko. Proces działający pod SCHED_FIFO z priorytetem 99 będzie wywłaszczał każdy proces SCHED_OTHER na tym samym rdzeniu CPU, dopóki dobrowolnie nie ustąpi lub nie zostanie zablokowany.
2. Niesprawiedliwe kolejkowanie w schedulerach I/O
Głodzenie CPU jest dobrze udokumentowane, ale głodzenie I/O jest równie destrukcyjne i często pomijane. Scheduler I/O systemu Linux (historycznie CFQ, obecnie BFQ lub mq-deadline w zależności od wersji jądra i typu pamięci masowej) zarządza kolejnością obsługi żądań urządzeń blokowych. Przy dużych sekwencyjnych obciążeniach zapisu — typowych dla serwerów baz danych i aplikacji intensywnie korzystających z logów — scheduler I/O może obniżać priorytet żądań losowego odczytu innych procesów, skutecznie pozbawiając je dostępu do dysku.
Jest to częsty problem w środowiskach VPS Hosting, gdzie wielu najemców współdzieli podstawową infrastrukturę pamięci masowej, a rywalizacja o I/O jest realnym problemem operacyjnym.
3. Presja pamięci i OOM Killer
Gdy fizyczna pamięć RAM jest wyczerpana, mechanizm OOM (Out-Of-Memory) killer jądra Linux wybiera proces do zakończenia na podstawie oom_score. Choć technicznie jest to zakończenie, a nie głodzenie, stan poprzedzający — gdzie proces jest wielokrotnie przenoszony na dysk i nigdy nie otrzymuje wystarczającej ilości pamięci rezydentnej do wydajnego działania — stanowi głodzenie pamięci. Proces technicznie działa, ale czyni znikome postępy z powodu ciągłych błędów stron i operacji I/O na swap.
4. Rywalizacja o blokady i głodzenie muteksów
W aplikacjach wielowątkowych głodzenie występuje na poziomie prymitywów synchronizacji. Jeśli muteks lub semafor używa niesprawiedliwej polityki akwizycji (ostatni wchodzi, pierwszy wychodzi lub losowy wybór spośród oczekujących wątków), konkretny wątek może być stale pomijany, nawet jeśli blokada jest często zwalniana. Różni się to od szeregowania na poziomie OS i występuje całkowicie w przestrzeni użytkownika lub podsystemie synchronizacji jądra.
5. Głodzenie przepustowości sieci
W środowiskach konteneryzowanych i zwirtualizowanych, proces lub kontener zużywający pełną dostępną przepustowość sieci może pozbawiać inne procesy dostępu do I/O sieciowego. Bez kształtowania ruchu za pomocą tc (traffic control) i cgroups, jeden niekontrolowany proces może zmonopolizować przepustowość karty sieciowej.
Głodzenie vs. zakleszczenie vs. livelock: porównanie techniczne
| Właściwość | Głodzenie | Zakleszczenie | Livelock |
|---|---|---|---|
| Postęp systemu | Tak (inne procesy działają) | Nie (zablokowane procesy zatrzymują się) | Pozorny (brak rzeczywistego postępu) |
| Stan zablokowania | Nie (proces jest uruchamialny) | Tak (proces czeka na zasób) | Nie (proces jest aktywny) |
| Przetrzymywany zasób | Nie | Tak (cykliczne przetrzymywanie i oczekiwanie) | Nie |
| Samorozwiązywanie | Czasami (ze starzeniem) | Nigdy (wymaga interwencji) | Rzadko |
| Trudność wykrycia | Wysoka (brak jawnego błędu) | Średnia (wykrywanie cykli) | Wysoka (wygląda jak aktywność) |
| Główna przyczyna | Niesprawiedliwa polityka szeregowania | Cykliczna zależność zasobów | Reaktywne pętle zmiany stanu |
| Sygnał jądra Linux | Brak | Brak (możliwy soft lockup) | Brak |
Jak nowoczesne schedulery radzą sobie z głodzeniem
Linux Completely Fair Scheduler (CFS)
CFS, wprowadzony w jądrze Linux 2.6.23, rozwiązuje problem głodzenia poprzez śledzenie wirtualnego czasu wykonania (vruntime) dla każdego procesu. Scheduler zawsze wybiera proces z najniższym vruntime — co oznacza, że procesy, które otrzymały mniej czasu CPU, są systematycznie faworyzowane. Ten projekt sprawia, że czyste głodzenie CPU jest prawie niemożliwe w CFS dla procesów SCHED_OTHER.
Jednak CFS nie chroni przed głodzeniem przez procesy czasu rzeczywistego. Każdy proces szeregowany pod SCHED_FIFO lub SCHED_RR wywłaszcza wszystkie zadania SCHED_OTHER. Parametr jądra /proc/sys/kernel/sched_rt_runtime_us (domyślnie: 950 000 mikrosekund na sekundę) rezerwuje 5% czasu CPU dla zadań nieczasowych właśnie po to, aby temu zapobiec.
Starzenie priorytetów
Klasyczne algorytmy starzenia zwiększają efektywny priorytet procesu o stałą wartość w każdym cyklu szeregowania, który spędza on czekając. Gdy efektywny priorytet osiągnie najwyższy poziom, wykonanie procesu jest gwarantowane. Po uruchomieniu jego priorytet resetuje się do wartości bazowej. Jest to podręcznikowe rozwiązanie głodzenia opartego na priorytetach i jest implementowane w różnych formach w Windows NT, Solaris i starszych schedulerach Linux.
Sprawiedliwe kolejkowanie i ważone sprawiedliwe kolejkowanie (WFQ)
Dla zasobów sieciowych i I/O, Weighted Fair Queuing przypisuje każdemu przepływowi lub procesowi udział w przepustowości proporcjonalny do jego wagi. Nawet jeśli przepływ o wysokiej wadze generuje więcej ruchu, przepływom o niskiej wadze gwarantowana jest minimalna szybkość obsługi. Linux implementuje to za pomocą dyscyplin Hierarchical Token Bucket (HTB) i Stochastic Fair Queuing (SFQ) w podsystemie tc.
Diagnozowanie głodzenia w produkcyjnych systemach Linux
Identyfikacja głodzenia wymaga jednoczesnego korelowania wielu źródeł danych.
Analiza szeregowania 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 -20Wynik schedstat dostarcza skumulowanego czasu, jaki proces spędził czekając w kolejce uruchamiania (run_delay w nanosekundach) — bezpośredniej miary głodzenia szeregowania.
Wskaźniki głodzenia pamięci
# 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"Wykrywanie głodzenia 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}'Procesy w stanie D (nieprzerywalny sen) są zablokowane na I/O. Utrzymująca się populacja procesów w stanie D jest silnym wskaźnikiem głodzenia I/O lub nasycenia podsystemu pamięci masowej.
Rozwiązania produkcyjne i strategie łagodzenia
Implementacja cgroups v2 dla izolacji zasobów
Control Groups (cgroups v2) zapewniają najbardziej niezawodny mechanizm zapobiegania głodzeniu w środowiskach wieloprocesowych i konteneryzowanych. Przypisując jawne limity CPU, pamięci i I/O do grup procesów, gwarantujesz minimalne alokacje zasobów niezależnie od obciążenia systemu.
# 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.procsWaga CPU w cgroups v2 używa zakresu 1–10000, gdzie domyślna wartość to 100. Grupa procesów z wagą 200 otrzymuje dwukrotnie większy udział CPU niż ta z wagą 100 przy rywalizacji.
Dostrajanie schedulera Linux do obciążenia
# 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_usStosowanie odpowiednich polityk szeregowania dla każdego procesu
# 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.shKlasa SCHED_IDLE (chrt -i) jest właściwym narzędziem dla naprawdę zadań w tle — działa tylko wtedy, gdy nie istnieje żaden inny uruchamialny proces, całkowicie eliminując jej zdolność do głodzenia innych obciążeń.
Wybór schedulera 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.rulesBFQ (Budget Fair Queuing) jest specjalnie zaprojektowany, aby zapobiegać głodzeniu I/O poprzez gwarantowanie każdemu procesowi proporcjonalnego udziału w przepustowości dysku. Jest to zalecany scheduler dla środowisk hostingu współdzielonego i serwerów baz danych.
Kontrola przepustowości sieci za pomocą 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 10Ta konfiguracja gwarantuje każdej klasie minimum 100 Mbps, jednocześnie pozwalając na wykorzystanie do pełnej pojemności łącza 1 Gbps, gdy przepustowość jest dostępna.
Dostrajanie nadmiernego przydziału pamięci i 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_ratioUstawienie vm.swappiness=10 instruuje jądro, aby preferowało odzyskiwanie pamięci podręcznej stron zamiast przenoszenia pamięci procesów do swap, znacznie zmniejszając prawdopodobieństwo głodzenia pamięci przy umiarkowanym obciążeniu.
Głodzenie w środowiskach zwirtualizowanych i konteneryzowanych
Na Serwerach Dedykowanych z hipernadzorcami (KVM, VMware ESXi, Hyper-V), głodzenie może wystąpić na dwóch odrębnych warstwach:
Głodzenie na poziomie hipernadzorcy: Maszyna wirtualna jest pozbawiana cykli CPU przez scheduler hipernadzorcy. KVM używa CFS jądra hosta do szeregowania vCPU, co oznacza, że VM z niższą wagą udziału CPU może być głodzona przez VM z wyższymi wagami przy rywalizacji. DRS (Distributed Resource Scheduler) VMware używa udziałów, rezerwacji i limitów do kontrolowania tego.
Głodzenie na poziomie systemu operacyjnego gościa: Wewnątrz samej VM obowiązują te same dynamiki szeregowania na poziomie OS. Skonteneryzowane obciążenie działające pod Docker lub Kubernetes bez jawnych limitów zasobów może zmonopolizować CPU i pamięć systemu operacyjnego gościa, głodząc współlokowane kontenery.
W środowiskach Kubernetes zawsze definiuj zarówno requests, jak i limits w specyfikacjach podów:
resources:
requests:
cpu: "250m"
memory: "512Mi"
limits:
cpu: "1000m"
memory: "1Gi"Wartość requests określa rozmieszczenie podczas szeregowania i wagę udziału CPU cgroup. Bez niej scheduler Kubernetes nie ma podstaw do sprawiedliwego rozmieszczenia, a środowisko uruchomieniowe kontenerów przypisuje domyślne (równe) wagi — co nadal pozwala na głodzenie, jeśli jeden kontener konsekwentnie nasyca swój limit CPU.
Głodzenie w serwerach baz danych i aplikacji
Silniki baz danych implementują własne wewnętrzne schedulery niezależne od schedulera OS. PostgreSQL używa modelu jeden proces na połączenie, gdzie każdy proces backendu normalnie konkuruje o zasoby OS, ale rywalizacja o blokady wewnątrz bazy danych (blokady na poziomie wierszy, blokady doradcze) może powodować głodzenie na poziomie aplikacji, gdzie konkretne zapytania czekają w nieskończoność na akwizycję blokady.
MySQL/InnoDB używa puli wątków z konfigurowalnymi limitami współbieżności (innodb_thread_concurrency). Ustawienie tej wartości zbyt nisko powoduje głodzenie zapytań, gdy wątki kolejkują się czekając na sloty wykonania. Ustawienie zbyt wysoko powoduje thrashing CPU. Zalecana wartość początkowa to 2 × number of CPU cores.
W przypadku serwerów WWW, Nginx i Apache mają odmienne profile głodzenia. Sterowany zdarzeniami model Nginx jest z natury odporny na głodzenie workerów, ale wyczerpanie puli połączeń upstream (np. do PHP-FPM lub backendowego API) tworzy głodzenie na poziomie aplikacji. MPM prefork Apache może wyczerpać swój limit MaxRequestWorkers, powodując nieskończone kolejkowanie nowych połączeń — forma głodzenia połączeń.
Te kwestie są bezpośrednio istotne przy konfiguracji VPS z cPanel dla obciążeń hostingu współdzielonego, gdzie wiele witryn konkuruje o pule workerów PHP-FPM i limity połączeń MySQL.
Infrastruktura monitorowania dla zapobiegania głodzeniu
Reaktywna diagnoza jest niewystarczająca dla systemów produkcyjnych. Proaktywny stos monitorowania powinien obejmować:
Metryki Prometheus + Node Exporter do obserwowania:
node_schedstat_waiting_seconds_total— skumulowany czas oczekiwania w kolejce uruchamiania CPU na CPUnode_vmstat_pgmajfault— główne błędy stron wskazujące na presję pamięcinode_disk_io_time_weighted_seconds_total— nasycenie kolejki I/Onode_pressure_cpu_waiting_seconds_total— presja CPU Linux PSI (Pressure Stall Information)node_pressure_memory_full_seconds_total— czas pełnego zatrzymania PSI pamięci
Linux PSI (dostępny od jądra 4.20) jest najbardziej bezpośrednim wskaźnikiem głodzenia dostępnym w jądrze. Raportuje procent czasu, w którym zadania były zablokowane czekając na zasoby CPU, pamięci lub I/O:
# Real-time PSI monitoring
cat /proc/pressure/cpu
cat /proc/pressure/memory
cat /proc/pressure/ioFormat wyjścia: some avg10=X.XX avg60=X.XX avg300=X.XX total=NNNN gdzie some wskazuje, że co najmniej jedno zadanie było zablokowane. Wartości powyżej 10–15% dla avg60 wymagają natychmiastowego zbadania.
Dla zespołów zarządzających Panelami Sterowania VPS lub niestandardowymi stosami serwerów, integracja metryk PSI z dashboardami Grafana zapewnia wczesne ostrzeżenie przed degradacją wydajności widocznej dla użytkowników przez głodzenie.
Praktyczna macierz decyzyjna: wybór właściwego mechanizmu anty-głodzeniowego
| Objaw | Typ zasobu | Zalecane narzędzie | Cel konfiguracji |
|---|---|---|---|
| Zadania w tle nigdy się nie kończą | CPU | SCHED_IDLE lub nice +19 | Eliminacja rywalizacji CPU w tle |
| Skoki opóźnień interaktywnych pod obciążeniem | CPU | Dostrajanie CFS + waga CPU cgroups v2 | Gwarancja udziału procesu interaktywnego |
| Przekroczenie limitu czasu zapytań bazy danych | CPU + blokada | innodb_thread_concurrency, limit czasu blokady | Ograniczenie czasu oczekiwania na blokadę |
| Zadania intensywne dyskowe blokują serwowanie WWW | I/O | Scheduler BFQ + cgroups v2 io.weight | Proporcjonalna alokacja I/O |
| Zabicia OOM kontenerów pod obciążeniem | Pamięć | cgroups v2 memory.min + vm.swappiness | Gwarancja minimalnej pamięci rezydentnej |
| Proces intensywny sieciowo głodzi inne | Sieć | HTB + SFQ przez tc | Gwarancja przepustowości per klasa |
| VM głodzona przez hipernadzorcę | vCPU | Rezerwacje/udziały CPU hipernadzorcy | Rezerwacja minimalnych cykli vCPU |
Kluczowe wnioski techniczne
- Nigdy nie polegaj na domyślnym szeregowaniu dla serwerów z mieszanymi obciążeniami. Jawnie klasyfikuj procesy używając
chrt,nicei cgroups v2 na podstawie ich wrażliwości na opóźnienia i priorytetu biznesowego. - Włącz monitorowanie PSI (
/proc/pressure/*) na wszystkich produkcyjnych systemach Linux. Jest to najdokładniejszy wskaźnik głodzenia w czasie rzeczywistym w jądrze i ma prawie zerowe narzuty. - Używaj BFQ dla dysków obrotowych i wszelkich urządzeń NVMe obsługujących mieszane obciążenia losowe/sekwencyjne w środowiskach wielodostępnych. Gwarancje sprawiedliwości są warte marginalnego narzutu przepustowości.
- Bez wyjątku ustawiaj żądania zasobów Kubernetes. Nieustawione
requests.cputo nie „nieograniczone” — to zobowiązanie szeregowania, które umożliwia głodzenie CPU na poziomie kontenera. - Odróżniaj głodzenie od zakleszczenia przed interwencją. Zabicie i ponowne uruchomienie głodzonego procesu nie naprawia podstawowej nierównowagi szeregowania; jedynie tymczasowo usuwa objaw.
- Audytuj przypisania priorytetów czasu rzeczywistego (
SCHED_FIFO/SCHED_RR) na każdym systemie, gdzie są używane. Jeden błędnie skonfigurowany proces czasu rzeczywistego może w nieskończoność głodzić wszystkie obciążenia o normalnym priorytecie na rdzeniu CPU. - W środowiskach Hostingu Współdzielonego, egzekwuj limity CPU i I/O per konto na poziomie cgroup, zamiast polegać wyłącznie na ograniczaniu szybkości na poziomie aplikacji.
Często zadawane pytania
Jaka jest różnica między głodzeniem a zakleszczeniem w systemie operacyjnym?
Zakleszczenie występuje, gdy dwa lub więcej procesów jest trwale zablokowanych, każdy przetrzymując zasób potrzebny drugiemu — żaden proces nie czyni postępów. Głodzenie występuje, gdy proces jest stale pomijany przez scheduler mimo bycia uruchamialnym; inne procesy działają normalnie. Zakleszczenie wymaga przerwania cyklicznej zależności; głodzenie wymaga naprawy polityki szeregowania, zazwyczaj poprzez implementację starzenia lub sprawiedliwego kolejkowania.
Jak scheduler Linux CFS zapobiega głodzeniu CPU?
CFS śledzi wirtualny czas wykonania (vruntime) dla każdego procesu i zawsze wybiera do wykonania proces z najniższym vruntime. Zapewnia to, że procesy otrzymujące mniej czasu CPU są systematycznie faworyzowane, czyniąc nieskończone głodzenie CPU procesów SCHED_OTHER prawie niemożliwym. Jednak procesy czasu rzeczywistego (SCHED_FIFO, SCHED_RR) całkowicie omijają CFS i nadal mogą głodzić normalne procesy, jeśli parametr sched_rt_runtime_us nie jest poprawnie ustawiony.
Jak mogę wykryć, czy proces jest głodzony na serwerze Linux?
Odczytaj /proc/<PID>/schedstat, aby sprawdzić skumulowany czas oczekiwania w kolejce uruchamiania. Monitoruj /proc/pressure/cpu pod kątem metryk zatrzymania PSI. Użyj perf sched latency --sort max, aby zidentyfikować procesy z nieprawidłowo wysokim opóźnieniem szeregowania. Procesy w trwałym stanie D widoczne w wynikach ps aux wskazują na głodzenie I/O, a nie głodzenie CPU.
Czy głodzenie procesów wpływa inaczej na środowiska VPS i serwerów chmurowych niż na sprzęt fizyczny?
Tak. Na VPS głodzenie może wystąpić zarówno na warstwie hipernadzorcy (scheduler hipernadzorcy odmawiający czasu vCPU Twojej VM), jak i wewnątrz systemu operacyjnego gościa. Głodzenie na poziomie hipernadzorcy jest niewidoczne dla standardowych narzędzi monitorowania OS i wymaga metryk specyficznych dla hipernadzorcy lub zauważalnego czasu kradzieży (%st w wynikach top). Wysoki czas kradzieży — zazwyczaj powyżej 5–10% utrzymującego się — wskazuje, że hipernadzorca nie dostarcza cykli vCPU, do których Twoja VM jest uprawniona.
Jaki jest najszybszy sposób na zapobieganie głodzeniu innych przez konkretny proces na zajętym serwerze?
Przypisz go do klasy szeregowania SCHED_IDLE za pomocą chrt -i -p 0 <PID>. Ta klasa wykonuje się tylko wtedy, gdy nie istnieje żaden inny uruchamialny proces, gwarantując, że nie może głodzić żadnego innego obciążenia. Dla procesów w tle intensywnych I/O, dodatkowo ustaw ich priorytet I/O na klasę idle: ionice -c 3 -p <PID>. Połączenie obu eliminuje proces jako źródło głodzenia CPU i I/O za pomocą dwóch poleceń i bez żadnych zmian w aplikacji.
