Co to jest proces? ps od środka
Po tej lekcji: kursant rozumie różnicę między programem a procesem, wie, co to PID i PPID, jak powstaje nowy proces (fork i exec), i umie znaleźć dowolny proces przez ps, pgrep, pstree i folder /proc.
Wstęp
Piątek, 16:40. Serwer zwalnia. Klikać nie ma gdzie, bo serwer nie ma myszki ani okienek. Ale jest jedna linijka:
ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head -5 PID USER %CPU %MEM COMMAND
2214 eryk 38.5 0.1 gzip
3526 root 4.3 1.2 apt-get
5959 root 1.0 0.2 python3
1 root 0.2 0.1 systemdPięć najbardziej zapracowanych procesów, od największego zjadacza procesora. W trzy sekundy wiesz, kogo zapytać „co ty robisz?”. Dziś nauczysz się czytać taką listę i zbudujesz ją sam.
Program a proces: przepis i kucharz
Wyobraź sobie kuchnię w restauracji. Na półce leży książka z przepisami. Przepis na pierogi sam z siebie nic nie gotuje. Leży i czeka.
Dopiero kiedy kucharz bierze przepis i zaczyna lepić, coś się dzieje. Kucharz ma swój stół, swoje garnki, swoje zamówienie i swój numerek na fartuchu.
W Linuksie przepis to program: plik na dysku, na przykład /usr/bin/sleep. Kucharz to proces: uruchomiony program, który dostał od systemu pamięć, numer, właściciela i otwarte pliki.
Jeden przepis, wielu kucharzy. Jeśli trzy razy uruchomisz sleep, masz jeden program i trzy procesy. Każdy z własnym numerem, każdy niezależny.
PID i PPID: numer na fartuchu i kto cię zatrudnił
Każdy proces dostaje od jądra numer. To PID, od „process ID”, identyfikator procesu. Jak numerek z automatu w urzędzie: unikalny, dopóki sprawa trwa. Kiedy proces się kończy, numer wraca do puli i kiedyś dostanie go ktoś inny.
Drugi numer to PPID, „parent process ID”, czyli numer rodzica. Każdy proces ktoś uruchomił. Kucharza zatrudnił szef kuchni, szefa kuchni kierownik zmiany.
echo $$
bash -c 'echo "ja: $$, rodzic: $PPID"'4900
ja: 5137, rodzic: 4900$$ to PID Twojego Basha. Potem uruchamiamy drugiego Basha w środku pierwszego i pytamy go o PID i o rodzica. Rodzic to 4900, czyli my. Rodzinka się zgadza.
A kto jest na samej górze? Proces numer 1. Na każdej współczesnej maszynie z Ubuntu i z RHEL to systemd, czyli kierownik zmiany. Jądro uruchamia go jako pierwszego przy starcie, a on uruchamia całą resztę. Lekcja o starcie systemu i o systemd jeszcze przed nami.
fork i exec: jak rodzi się proces
Teraz rzecz, która brzmi dziwnie, ale wyjaśnia pół tego modułu. W Linuksie nowy proces nie powstaje z niczego. Powstaje w dwóch krokach.
Krok 1: fork, czyli klonowanie. Kucharz staje przy stole i robi swoją kopię. Identyczną: ten sam fartuch, te same garnki, nawet to samo zamówienie w ręku. Jedyna różnica: klon dostaje nowy numer na fartuchu, a jego rodzicem jest oryginał.
Krok 2: exec, czyli wymiana przepisu. Klon wyrzuca stary przepis i bierze nowy. Numer na fartuchu zostaje, ale robota jest już zupełnie inna.
Kiedy wpisujesz w Bashu ls, Bash robi forka, czyli swoją kopię, a ta kopia robi exec na program ls. Dlatego rodzicem ls jest Twój Bash.
Czy da się zobaczyć exec na własne oczy? Da się:
bash -c 'echo "Mój PID: $$"; exec sleep 100' &
ps -o pid,ppid,cmd -p $![1] 5574
Mój PID: 5574
PID PPID CMD
5574 4900 sleep 100Bash pod numerem 5574 przedstawił się, a potem komendą exec zamienił się w sleep. Pod tym samym numerem siedzi już zupełnie inny program. Jak aktor, który w połowie sztuki przebrał się za kogoś innego, ale nikt nie zmienił mu nazwiska w programie teatru. $! to PID ostatniego procesu uruchomionego w tle, przyda się nam jeszcze nie raz.
ps: zdjęcie kuchni
ps to „process status”. Robi zdjęcie: kto teraz gotuje. Jedno zdjęcie, nie film. Film będzie w lekcji o top.
ps PID TTY TIME CMD
4900 pts/3 00:00:00 bash
5104 pts/3 00:00:00 psBez opcji ps pokazuje tylko procesy z Twojego terminala. Tu jest Bash i sam ps, który złapał się na własnym zdjęciu. Selfie.
TTY to terminal, do którego proces jest podpięty. pts/3 to trzecie okienko pseudo-terminala, takie, jakie dostajesz przez SSH albo w oknie terminala. TIME to nie czas od uruchomienia, tylko czas, przez który proces naprawdę używał procesora. Kucharz może stać w kuchni od rana i mieć dwie minuty faktycznego lepienia.
ps aux i ps -ef: dwa dialekty
Cały system zobaczysz na dwa sposoby. I tu Linux jest jak Polska: dwa dialekty, oba poprawne, każdy region upiera się przy swoim.
ps aux
ps -efps aux to styl BSD, bez myślnika. ps -ef to styl uniksowy, z myślnikiem. Oba pokazują wszystkie procesy, tylko z innymi kolumnami.
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 2 0.0 0.0 0 0 ? S 16:17 0:00 [kthreadd]
root 4 0.0 0.0 0 0 ? I< 16:17 0:00 [kworker/R-rcu_gp]USER— właściciel procesu, czyli czyje klucze ten kucharz nosi.%CPUi%MEM— ile procesora i pamięci RAM zużywa.VSZ— pamięć wirtualna w kilobajtach: wszystko, co proces sobie zarezerwował. Jak stolik zarezerwowany na dwadzieścia osób.RSS— pamięć, którą naprawdę zajmuje w RAM-ie. Ilu gości faktycznie przyszło. Patrz naRSS, nie naVSZ.TTY— terminal. Znak zapytania znaczy: proces nie ma terminala.STAT— stan:Sśpi,Rpracuje i tak dalej. Cały następna lekcja jest o stanach.START— kiedy wystartował.TIME— ile czasu procesora zjadł.COMMAND— komenda razem z argumentami. Nazwa w nawiasach kwadratowych, jak[kthreadd], to wątek jądra. O nich też w następnej lekcji.
A ps -ef ma UID, PID, PPID, C (zużycie procesora), STIME (start), TTY, TIME, CMD. Admini lubią -ef właśnie za kolumnę PPID: od razu widać, kto jest czyim rodzicem.
-o i --sort: zdjęcie na zamówienie
Najbardziej przydatna para opcji. -o mówi, jakie kolumny chcesz zobaczyć, --sort po czym sortować.
ps -eo pid,user,%cpu,%mem,comm --sort=-%cpu | head -5
ps aux --sort=-%mem | head -4
ps -o pid,ppid,user,stat,etime,cmd -p 4900
ps -C sleep -o pid,ppid,etime,cmd-eto wszystkie procesy, czyli „every”.-o pid,user,...to lista kolumn po przecinku, bez spacji.--sort=-%cpusortuje po procesorze. Minus znaczy malejąco, od największego. Bez minusa rośnie.-p 4900pokazuje jeden konkretny proces po PID.-C sleeppokazuje procesy o danej nazwie.etimeto czas od uruchomienia, czyli ile kucharz już stoi w kuchni.
To jest dokładnie linijka z haka. Nic magicznego, tylko kolumny na zamówienie.
pgrep: „gdzie jest ten kucharz?”
Często nie potrzebujesz zdjęcia całej kuchni. Chcesz tylko numer jednego kucharza.
sleep 300 &
sleep 200 &
pgrep sleep
pgrep -a sleep
pgrep -u root -c
pgrep -x slee; echo "kod: $?"5738
5739
5738 sleep 300
5739 sleep 200
94
kod: 1pgrep sleepwypisuje same PID-y. Idealne do skryptów.-adopisuje całą komendę,-lsamą nazwę.-u roottylko procesy roota, a-czamiast listy podaje liczbę.-xwymaga dokładnej nazwy.sleenie pasuje dosleep, więc nic nie ma i kod wyjścia to 1. Kciuk w dół.-fszuka w całej linii komend, nie tylko w nazwie. Uwaga na nie, w lekcji o sygnałach zobaczysz, jak potrafi ugryźć.
pstree: drzewo genealogiczne
sleep 1000 &
pstree -p $$bash(4900)-+-pstree(5989)
`-sleep(5988)pstree rysuje rodzinę jako drzewo, a -p dopisuje numery. Tu widać mój Bash i jego dwoje dzieci: sleep w tle i sam pstree. Z opcją -s dostajesz odwrotny kierunek: przodków aż do procesu numer 1. Na maszynie wirtualnej zobaczysz coś w stylu systemd(1) na górze, dalej serwer SSH, Twoją sesję i Twojego Basha. Cała linia rodu, jak w kronice szlacheckiej.
/proc/PID: teczka każdego kucharza
Pamiętasz /proc z lekcji o mapie Linuksa? Folder, którego nie ma na dysku. Każdy proces ma tam swój podfolder o nazwie równej PID. To jego teczka osobowa.
sleep 1000 &
grep -E '^(Name|State|Pid|PPid|Uid)' /proc/$!/status
tr '\0' ' ' < /proc/$!/cmdline; echo
ls -l /proc/$!/exe /proc/$!/cwdName: sleep
State: S (sleeping)
Pid: 5988
PPid: 4900
Uid: 1000 1000 1000 1000
sleep 1000
lrwxrwxrwx 1 eryk eryk 0 Nov 3 16:26 /proc/5988/cwd -> /home/eryk
lrwxrwxrwx 1 eryk eryk 0 Nov 3 16:26 /proc/5988/exe -> /usr/bin/sleepstatus— wizytówka: nazwa, stan, PID, rodzic, właściciel, pamięć.cmdline— pełna komenda. Argumenty są tam oddzielone niewidocznym znakiem zerowym, dlategotrzamienia go na spacje.exe— drogowskaz do pliku programu. Który przepis ten kucharz trzyma w ręku.cwd— folder, w którym proces pracuje.fd— otwarte pliki. Przyda się bardzo, kiedy będziemy szukać, kto trzyma usunięty plik i zjada dysk.
Jak to działa pod spodem
W jądrze każdy proces to struktura danych, kartka w kartotece kierownika. Jest na niej numer, rodzic, stan, właściciel, pamięć, otwarte pliki. Jądro trzyma te kartki u siebie i nikomu ich nie pożycza.
Ale pokazuje je przez okienko: /proc. I tu niespodzianka: ps, pgrep, pstree i top nie mają żadnego tajnego dostępu do jądra. Po prostu czytają pliki z /proc, jak Ty cat-em. Sprawdź sam:
ls -d /proc/[0-9]* | wc -l
ps -e --no-headers | wc -l153
153Tyle samo folderów z numerami, ile procesów na liście. „Wszystko jest plikiem” znowu robi robotę.
Ściąga flag
| Komenda | Flaga | Co robi |
|---|---|---|
ps | (bez opcji) | procesy z tego terminala |
ps | aux | wszystkie procesy, styl BSD, z %CPU, %MEM, RSS |
ps | -ef | wszystkie procesy, styl uniksowy, z PPID |
ps | -e | wszystkie procesy |
ps | -o kol1,kol2 | wybrane kolumny |
ps | --sort=-kol | sortuje malejąco po kolumnie |
ps | -p PID | jeden proces po numerze |
ps | -C nazwa | procesy o danej nazwie |
ps | -u użytkownik | procesy danego użytkownika |
ps | --ppid PID | dzieci danego procesu |
pgrep | -a / -l | PID z pełną komendą / z nazwą |
pgrep | -x | dokładna nazwa |
pgrep | -f | szuka w całej linii komend |
pgrep | -u / -c | po użytkowniku / tylko liczba |
pstree | -p | dopisuje PID-y |
pstree | -s PID | przodkowie procesu aż do PID 1 |
⚠ Typowy błąd
ps aux | grep "sleep 777"root 28990 0.0 0.0 2740 1772 pts/3 S 16:33 0:00 sleep 777
root 28993 0.0 0.0 3572 1984 pts/3 S+ 16:33 0:00 grep --color=auto sleep 777Szukasz jednego procesu, a dostajesz dwa. Drugi to… sam grep. Rura uruchamia obie komendy jednocześnie, pamiętasz z lekcji 7? Więc kiedy ps robi zdjęcie, grep już stoi w kuchni z kartką „sleep 777” w ręku i łapie się na fotkę. Skrypt, który liczy linijki, żeby sprawdzić, czy usługa działa, zawsze dostanie co najmniej jedną. Usługa leży, a skrypt melduje, że wszystko gra.
Dwa wyjścia. Porządne: pgrep -a -f "sleep 777". Sprytne: ps aux | grep "[s]leep 777". Wzorzec [s]leep pasuje do słowa „sleep”, ale w linii komend samego grep-a stoi [s]leep, z nawiasami, i ten napis już do wzorca nie pasuje. Kucharz w przebraniu nie rozpoznaje sam siebie w lustrze.
✎ Zadanie
Uruchom sleep 777 &.
- Znajdź jego PID trzema sposobami: przez
$!, przezpgrepi przezps. - Sprawdź jego PPID i porównaj z
echo $$. - W
/proc/PIDsprawdź, jaki plik programu wykonuje ten proces i w jakim folderze pracuje. - Na koniec jedną linijką wypisz pięć procesów, które zajmują najwięcej pamięci RAM.
Pokaż rozwiązanie
sleep 777 &
echo $!
pgrep -a -f "sleep 777"
ps -o pid,ppid,cmd -p $!
echo $$
ls -l /proc/$!/exe /proc/$!/cwd
ps -eo pid,user,rss,comm --sort=-rss | head -6[1] 30999
30999
30999 sleep 777
PID PPID CMD
30999 17206 sleep 777
17206
/proc/30999/cwd -> /home/eryk
/proc/30999/exe -> /usr/bin/sleep
PID USER RSS COMMAND
2950 eryk 412880 firefox
2411 eryk 287316 gnome-shellTrzy sposoby, jeden numer. PPID równa się $$, bo to Twój Bash zatrudnił tego kucharza. exe pokazuje /usr/bin/sleep, czyli przepis, a cwd folder, w którym stałeś. A ranking pamięci sortujemy po rss, bo to prawdziwa zajętość RAM-u, a nie rezerwacja na wyrost. Jeśli w pierwszej linijce zobaczyłeś przeglądarkę — gratuluję, Twój komputer działa zgodnie z prawami fizyki.
Co dalej
Wiesz już, kto gotuje. W następnej lekcji zobaczysz, że kucharze bywają bardzo różni: tacy, co pracują przy Tobie, tacy w tle, demony bez terminala, wątki, sieroty i zombie. I zrobimy prawdziwego zombie, na żywo.