Przejdź do treści
Lekcja 20 z 117

Kurs › Moduł 2 › Lekcja 20

Rodzaje procesów

⏱ ok. 12 min czytania· moduł 2: Procesy i jak działa system

Po tej lekcji: kursant rozróżnia procesy pierwszoplanowe, w tle, demony, procesy jądra i wątki, wie, czym jest PID 1, sieroty i zombie, i czyta stany R, S, D, T, Z w ps.

Wstęp

górna część top, czerwona ramka wokół słowa „zombie”

Tasks: 112 total,   1 running, 110 sleeping,   0 stopped,   1 zombie

Szef zagląda Ci przez ramię: „ZOMBIE? Mamy wirusa? Dzwonię do działu bezpieczeństwa!”.

Spokojnie. Zombie w Linuksie to nie wirus i nie atak. To zwykła, codzienna rzecz, która czasem mówi Ci, że jakiś program jest napisany niechlujnie. Dziś zrobimy własnego zombie, na żywo, zobaczymy, czemu kill -9 go nie rusza, i jak się go naprawdę pozbywa. A po drodze poznasz całą załogę kuchni.

Pierwszy plan i tło

Zostajemy w restauracji. Kucharz może gotować przy Twoim stoliku, na Twoich oczach. Dopóki nie skończy, czekasz i nic innego nie zamówisz. To proces pierwszoplanowy.

sleep 5

Przez pięć sekund terminal jest zajęty. Klawiatura należy do sleep.

Ale kucharz może też pójść na zaplecze. Robi swoje, a Ty w tym czasie zamawiasz dalej. To proces w tle. Wystarczy znak & na końcu:

sleep 5 &

Terminal od razu jest wolny. Całe zarządzanie tym, co w tle, czyli jobs, fg, bg, Ctrl+Z, ma swój lekcja, czwarty od dziś. Dziś wystarczy, że wiesz: oba rodzaje mają terminal i oba są dziećmi Twojego Basha. Jak zamkniesz terminal, zwykle giną razem z nim.

Demony: nocna zmiana bez stolika

Są też kucharze, którzy w ogóle nie mają stolika. Pracują całą noc, nikt na nich nie patrzy, a jak przychodzi zamówienie przez okienko, po prostu je realizują. To demony, po angielsku daemon.

Demon to proces, który działa w tle, nie ma terminala i zwykle czeka na coś: na połączenie, na godzinę, na plik. Po nazwie często go poznasz, bo kończy się na „d”:

  • sshd — czeka na połączenia SSH,
  • systemd-journald — zbiera logi,
  • crond na RHEL i cron na Ubuntu — budzik, uruchamia zadania o czasie,
  • httpd na RHEL i apache2 na Ubuntu — serwer WWW, ta sama recepcja hotelu pod dwiema nazwami,
  • chronyd — pilnuje zegara.

A jeden demon już znasz. Pamiętasz, czemu sesja tmux przeżyła rozłączenie?

ps -o pid,ppid,sid,tty,stat,comm -p $(pgrep -f "tmux: server")
  PID  PPID   SID TT       STAT COMMAND
 3536     1  3536 ?        Ss   tmux: server

Serwer tmux to klasyczny demon. Rodzic: proces numer 1. Terminal: znak zapytania, czyli żaden. W STAT ma małe s, czyli jest liderem własnej sesji, odcięty od Twojego okna. Dlatego zamknięcie terminala go nie obchodzi. Twoje okno to był tylko stolik, przy którym na chwilę usiadłeś.

[Ubuntu] [RHEL] Na obu rodzinach demonami zarządza systemd. To on je uruchamia, pilnuje i wstaje do nich w nocy. Szczegóły w lekcji o usługach.

Procesy jądra: obsługa techniczna

W ps aux widziałeś nazwy w nawiasach kwadratowych. To wątki jądra. Nie są programami z dysku, tylko kawałkami samego jądra, które robią robotę w tle: zapisują dane na dysk, sprzątają pamięć, obsługują przerwania. Elektryk i hydraulik budynku. Nie gotują, ale bez nich kuchnia stanie.

ps -o pid,ppid,stat,cmd -p 2,3,15
ps --ppid 2 -o pid,comm | head -4
  PID  PPID STAT CMD
    2     0 S    [kthreadd]
    3     2 S    [pool_workqueue_release]
   15     2 R    [rcu_preempt]
  PID COMMAND
    3 pool_workqueue_release
    4 kworker/R-rcu_gp
    5 kworker/R-sync_wq

Szefem obsługi jest kthreadd z numerem 2. Jego rodzic to zero, bo uruchamia go samo jądro. Wszystkie inne wątki jądra są jego dziećmi, mają PPID 2. Nazwy, które zobaczysz najczęściej: kworker (złota rączka do wszystkiego), ksoftirqd (kończenie obsługi przerwań), kswapd0 (przenoszenie pamięci do swapa). Jeśli kswapd0 zjada procesor, to nie jego wina. To znak, że brakuje RAM-u.

I ważne: tych procesów nie zabijasz. Nie da się, nie trzeba, nie próbuj. To jak wyrzucenie hydraulika w trakcie wymiany rury.

Wątki: kucharz z kilkoma parami rąk

Proces może mieć kilka wątków. To jak kucharz, któremu wyrosły dodatkowe ręce: jedna miesza zupę, druga kroi cebulę, trzecia odbiera telefon. Wszystkie ręce dzielą ten sam stół, te same garnki i ten sam PID. Różnią się tylko tym, co akurat robią.

python3 -c "import threading,time; [threading.Thread(target=time.sleep,args=(300,)).start() for _ in range(3)]; time.sleep(300)" &
ps -o pid,nlwp,cmd -p $!
ps -T -p $!
  PID NLWP CMD
 8557    4 python3 -c import threading,time; [threading.Thread(...
  PID  SPID TTY          TIME CMD
 8557  8557 pts/3    00:00:00 python3
 8557  8559 pts/3    00:00:00 python3
 8557  8560 pts/3    00:00:00 python3
 8557  8561 pts/3    00:00:00 python3

Jeden program w Pythonie, który odpala trzy wątki. NLWP mówi: cztery wątki, czyli główny plus trzy dodatkowe. ps -T pokazuje każdy osobno: PID ten sam, ale każdy wątek ma własny numer w kolumnie SPID. Bazy danych, przeglądarki i serwery aplikacji mają tych rąk dziesiątki albo setki.

PID 1: kierownik zmiany

Proces numer 1 na Ubuntu i RHEL to systemd. Kierownik zmiany. Uruchamia całą resztę i ma dwa obowiązki, o których mało kto wie.

Pierwszy: adoptuje sieroty. Za chwilę zobaczysz, co to znaczy.

Drugi: zamyka rachunki po zmarłych. O tym za dwie chwile.

A do tego jest chroniony. Jądro dostarcza procesowi numer 1 tylko te sygnały, na które on sam zgłosił obsługę. Chodzi o to, żeby nikt nie położył całego systemu jedną literówką. Gdyby PID 1 jednak zginął, jądro wpada w panikę i system staje. Kierownik zmiany wyszedł, zamykamy restaurację.

Sieroty: kucharz bez szefa

Co się dzieje, kiedy rodzic kończy pracę, a dziecko jeszcze gotuje?

bash -c 'sleep 500 & echo "dziecko: $!"'
ps -o pid,ppid,stat,cmd -p 6675
dziecko: 6675
  PID  PPID STAT CMD
 6675     1 S    sleep 500

Bash uruchomił sleep 500 w tle, wypisał numer dziecka i od razu się zakończył. sleep został sierotą. Ale nie błąka się po kuchni bez opieki. Jądro natychmiast przepisało go pod proces numer 1. PPID zmienił się na 1. Kierownik zmiany adoptuje każdego.

I to jest w porządku. Sierota to nie błąd, tylko normalna sytuacja. Tak zresztą powstają demony: celowo zostawiają rodzica i przechodzą pod kierownika.

Zombie: niezamknięty rachunek

Teraz gwiazda lekcji.

Gość zjadł, zapłacił i wyszedł. Ale kelner nie zamknął rachunku w kasie. Na sali nikogo już nie ma, a w systemie dalej wisi otwarty stolik numer 7. To jest zombie.

Po ludzku: proces dziecko się zakończył. Jądro zabrało mu już pamięć i pliki. Zostawiło tylko maleńką kartkę z numerem i kodem wyjścia, czyli informacją, jak poszło: kciuk w górę czy w dół. Ta kartka czeka, aż rodzic ją odbierze. Jeśli rodzic jej nigdy nie odbierze, kartka wisi. Proces nie żyje, ale wciąż jest na liście. Żywy trup. Stąd nazwa.

Zróbmy własnego:

bash -c 'sleep 1 & exec sleep 300' &
sleep 2
ps -o pid,ppid,stat,cmd --ppid $!
ps -o pid,ppid,stat,cmd -p $!
[1] 6425
  PID  PPID STAT CMD
 6426  6425 Z    [sleep] <defunct>
  PID  PPID STAT CMD
 6425  4900 S    sleep 300

Co tu się stało? Bash uruchomił dziecko sleep 1 w tle, a potem przez exec zamienił się w sleep 300. Pamiętasz exec z poprzedniej lekcji? Ten sam PID, inny program. A sleep w ogóle nie wie, że ma jakieś dziecko, więc nigdy nie odbiera rachunku. Po sekundzie sleep 1 kończy pracę i staje się zombie: stan Z, nazwa w nawiasach i dopisek <defunct>, czyli „nieżyjący”.

To jest właśnie niechlujny rodzic. W prawdziwym życiu to zwykle aplikacja z błędem, która uruchamia procesy pomocnicze i o nich zapomina.

To co, zabijamy?

kill -9 6426
ps -o pid,ppid,stat,cmd -p 6426
  PID  PPID STAT CMD
 6426  6425 Z    [sleep] <defunct>

Nic. Najmocniejszy sygnał w systemie i zero efektu. Bo nie da się zabić kogoś, kto już nie żyje. To jak dzwonienie do gościa, żeby wyszedł z restauracji, kiedy jest już w domu. Problem nie siedzi w zombie, tylko w rodzicu.

Więc kończymy rodzica:

kill 6425
sleep 2
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'
echo "zombie: $(ps -eo stat= | grep -c Z)"
zombie: 0

Rodzic znika, zombie zostaje sierotą, kierownik zmiany go adoptuje i od razu zamyka rachunek. Czysto.

Czy zombie są groźne? Jeden, dwa, nawet dziesięć: nie. Nie zjadają procesora ani pamięci. Zajmują tylko miejsce w tabeli procesów, czyli numer PID. Ale jeśli liczba zombie rośnie z godziny na godzinę, kiedyś skończą się numery i system nie uruchomi żadnego nowego procesu. Wtedy szukasz rodzica i zgłaszasz błąd jego twórcom, a na razie restartujesz rodzica.

Stany procesu: co kucharz teraz robi

Kolumna STAT w ps i S w top to stan. Pierwsza litera jest najważniejsza:

  • R — running. Kucharz stoi przy kuchence albo czeka w kolejce, żeby do niej podejść. Oba przypadki to R.
  • S — sleeping. Śpi i czeka na coś: na dostawę, na klienta, na dane z sieci. Obudzi się na dzwonek. Tak wygląda większość procesów w systemie, prawie przez cały czas.
  • D — uninterruptible sleep, sen nieprzerywalny. Kucharz czeka na dostawę, której nie wolno przerwać. Nie reaguje na nic, nawet na kill -9. Zwykle czeka na dysk albo na dysk sieciowy, na przykład NFS. Na moment to normalne. Ale wiele procesów wiszących długo w D to znak, że dysk albo sieć do dysku umierają. I to właśnie D często podbija obciążenie systemu, które poznasz w następnej lekcji.
  • T — stopped. Zatrzymany, zamrożony w pół ruchu. Tak działa Ctrl+Z albo sygnał STOP.
  • Z — zombie. Już znasz.
  • I — idle, bezczynny wątek jądra. Widzisz go tylko przy procesach w nawiasach.

Zamrażanie na żywo:

sleep 900 &
kill -STOP $!
ps -o pid,stat,cmd -p $!
kill -CONT $!
ps -o pid,stat,cmd -p $!
  PID STAT CMD
 9044 T    sleep 900
  PID STAT CMD
 9044 S    sleep 900

Po STOP stan T. Po CONT, czyli „continue”, wraca do S. Jak gra w „Raz, dwa, trzy, Baba Jaga patrzy”.

Po pierwszej literze w ps aux bywają dodatkowe znaczki:

  • s — lider sesji, zwykle powłoka albo demon,
  • + — proces na pierwszym planie w swoim terminalu,
  • l — ma wiele wątków,
  • < — wysoki priorytet, N — niski. O priorytetach będzie osobna lekcja.

Cały system w jednym rankingu:

ps -eo stat= | cut -c1 | sort | uniq -c | sort -rn
     72 S
     36 I
      1 R

Trójka sort | uniq -c | sort -rn z lekcji 7 znowu w akcji. Prawie wszyscy śpią, jeden pracuje. I ten jeden to najczęściej sam ps, który akurat robi zdjęcie.

Jak to działa pod spodem

Kiedy proces się kończy, wywołuje exit z kodem wyjścia. Jądro sprząta jego pamięć i pliki, ale zostawia w tabeli procesów wpis z kodem. Potem wysyła rodzicowi sygnał SIGCHLD: „Twoje dziecko skończyło”. Rodzic powinien wtedy wywołać wait, odebrać kod i dopiero wtedy wpis znika.

I tu ciekawostka: Bash robi to za każdym razem, kiedy uruchamiasz komendę. Czeka na dziecko, odbiera jego kod i wkłada go do $?. Kciuk w górę albo w dół, który sprawdzasz po każdej komendzie, to dosłownie zawartość tej kartki po zombie. Bash jest wzorowym kelnerem.

A jeśli rodzic umiera pierwszy, jądro przepisuje jego dzieci pod proces numer 1, który automatycznie wywołuje wait i sprząta. To wszystko jest opisane w man 2 wait.

Ściąga flag

KomendaFlagaCo robi
ps-o statstan procesu (R, S, D, T, Z, I)
ps--ppid PIDdzieci danego procesu
ps-T -p PIDwątki jednego procesu
ps-eLfwszystkie wątki w systemie
ps-o nlwpliczba wątków procesu
ps-o ppid= -p PIDsam numer rodzica, bez nagłówka
pgrep-r Zprocesy w danym stanie, np. zombie
kill-STOP PIDzamraża proces (stan T)
kill-CONT PIDodmraża proces
toplinia Tasksile procesów w którym stanie, w tym zombie
pstree-pkto jest czyim rodzicem

⚠ Typowy błąd

Monitoring krzyczy: „3 zombie”. Admin robi:

kill -9 $(pgrep -r Z)

I nic. Robi to jeszcze raz, z sudo. Dalej nic. Potem pisze na forum, że Linux jest zepsuty.

Zombie nie da się zabić, bo już nie żyje. Trzeba znaleźć rodzica:

pgrep -r Z -a
ps -o ppid= -p 31048
31048 [sleep] <defunct>
31045

Drugi numer to rodzic. Ten jest winny. Sprawdzasz, co to za program, i robisz mu grzeczny restart. Najlepiej przez systemd, jeśli to usługa, bo wtedy wstanie z powrotem. Po restarcie rodzica zombie znikają, bo przejmuje je i sprząta proces numer 1. A jeśli rodzicem jest PID 1 i zombie wiszą długo, to masz poważniejszy problem i to temat na zgłoszenie, nie na kill.

✎ Zadanie

  • Zrób własnego zombie komendą z tej lekcji.
  • Znajdź go przez ps po stanie Z i sprawdź, kto jest jego rodzicem.
  • Spróbuj zabić zombie przez kill -9 i sprawdź, że dalej jest.
  • Posprzątaj go, kończąc rodzica, i sprawdź, że zombie zniknął.
  • Na koniec policz jedną linijką, ile procesów w Twoim systemie jest w każdym stanie.
Pokaż rozwiązanie
bash -c 'sleep 1 & exec sleep 300' &
sleep 2
ps -eo pid,ppid,stat,cmd | awk '$3 ~ /Z/'
kill -9 6426
ps -o pid,ppid,stat,cmd -p 6426
kill 6425
ps -eo stat= | cut -c1 | sort | uniq -c | sort -rn
 6426  6425 Z    [sleep] <defunct>
  PID  PPID STAT CMD
 6426  6425 Z    [sleep] <defunct>
     72 S
     36 I
      1 R

Zombie w stanie Z, rodzic w kolumnie PPID. Po kill -9 zombie dalej leży, bo trudno zabić kogoś dwa razy. Po zakończeniu rodzica zombie znika, bo sprząta go kierownik zmiany. Jeśli Twój zombie przetrwał i to, to gratuluję, masz materiał na horror. Albo pomyliłeś PID-y, co jest dużo bardziej prawdopodobne.

Co dalej

ps robi zdjęcie. W następnej lekcji włączamy film: top. Rozszyfrujemy cały ten ekran od góry do dołu, łącznie z tajemniczym load average, które wszyscy cytują, a mało kto wie, co znaczy.