Proces není nic jiného než běžící instance programu. Je také definován jako program v akci.
Koncept procesu je základním konceptem systému Linux. Procesy mohou plodit další procesy, zabíjet jiné procesy, komunikovat s jinými procesy a mnoho dalšího.
V tomto tutoriálu probereme životní cyklus procesu a dotykovou základnu na základě různých aspektů, kterými proces prochází ve svém životním cyklu.
1. Kód vs program vs proces
Nejprve pochopíme rozdíl mezi kódem, programem a procesem.
Kód: Následuje příklad kódu:
#include <stdio.h> #include <unistd.h> int main(void) { printf("\n Hello World\n"); sleep(10); return 0; }
Uložme výše uvedenou část kódu do souboru s názvem helloWorld.c. Takže tento soubor se stane kódem.
Program: Nyní, když je kód zkompilován, vytvoří spustitelný soubor. Zde je návod, jak je výše uvedený kód zkompilován:
$ gcc -Wall helloWorld.c -o helloWorld
To by vytvořilo spustitelný soubor s názvem helloWorld. Tento spustitelný soubor je známý jako program.
Proces: Nyní spusťte tento spustitelný soubor:
$ ./helloWorld Hello World
Po spuštění se vytvoří proces odpovídající tomuto spustitelnému souboru (nebo programu). Tento proces provede veškerý strojový kód, který byl v programu. To je důvod, proč je proces známý jako spuštěná instance programu.
Chcete-li zkontrolovat podrobnosti o nově vytvořeném procesu, spusťte příkaz ps následujícím způsobem:
$ ps -aef | grep hello* 1000 6163 3017 0 18:15 pts/0 00:00:00 ./helloWorld
Chcete-li porozumět výstupu příkazu ps, přečtěte si náš článek o příkladech příkazů ps.
2. Rodičovský a podřízený proces
Každý proces má nadřazený proces a může nebo nemusí mít podřízené procesy. Vezměme to jeden po druhém. Zvažte výstup příkazu ps na mém počítači Ubuntu:
1000 3008 1 0 12:50 ? 00:00:23 gnome-terminal 1000 3016 3008 0 12:50 ? 00:00:00 gnome-pty-helper 1000 3017 3008 0 12:50 pts/0 00:00:00 bash 1000 3079 3008 0 12:58 pts/1 00:00:00 bash 1000 3321 1 0 14:29 ? 00:00:12 gedit root 5143 2 0 17:20 ? 00:00:04 [kworker/1:1] root 5600 2 0 17:39 ? 00:00:00 [migration/1] root 5642 2 0 17:39 ? 00:00:00 [kworker/u:69] root 5643 2 0 17:39 ? 00:00:00 [kworker/u:70] root 5677 2 0 17:39 ? 00:00:00 [kworker/0:2] root 5680 2 0 17:39 ? 00:00:00 [hci0] root 5956 916 0 17:39 ? 00:00:00 /sbin/dhclient -d -sf /usr/lib/NetworkManager/nm-dhcp-client.action -pf /run/sendsigs. root 6181 2 0 18:35 ? 00:00:00 [kworker/1:0] root 6190 2 0 18:40 ? 00:00:00 [kworker/1:2] 1000 6191 3079 0 18:43 pts/1 00:00:00 ps -aef
Celá čísla ve druhém a třetím sloupci výše uvedeného výstupu představují ID procesu a ID nadřazeného procesu. Sledujte čísla zvýrazněná tučně. Když jsem provedl příkaz 'ps -aef', byl vytvořen proces, jeho ID procesu je 6191. Nyní se podívejte na ID jeho nadřazeného procesu, je to 3079. Když se podíváte na začátek výstupu, uvidíte, že ID 3079 je ID procesu bash procesu. To potvrzuje, že bash shell je rodičem pro jakýkoli příkaz, který přes něj spustíte.
Podobně i pro procesy, které nejsou vytvořeny prostřednictvím shellu, existuje nějaký rodičovský proces. Stačí spustit příkaz „ps -aef“ na vašem počítači se systémem Linux a sledovat sloupec PPID (ID rodičovského procesu). Neuvidíte v něm žádnou prázdnou položku. To potvrzuje, že každý proces má nadřazený proces.
Nyní pojďme k dětským procesům. Kdykoli proces vytvoří další proces, první se nazývá rodič, zatímco druhý se nazývá podřízený proces. Technicky vzato je podřízený proces vytvořen voláním funkce fork() z kódu. Obvykle, když spustíte příkaz ze shellu, za fork() následuje řada funkcí exec().
Diskutovali jsme o tom, že každý proces má nadřazený proces, což může přinést otázku, co se stane s podřízeným procesem, jehož nadřazený proces je zabit? No, to je dobrá otázka, ale vraťme se k ní někdy později.
3. Proces init
Když se zavede systém Linux, první věc, která se nahraje do paměti, je vmlinuz. Je to spustitelný komprimovaný linuxový kernel. To má za následek vytvoření procesu init. Toto je první proces, který se vytvoří. Proces Init má PID jeden a je superrodičem všech procesů v linuxové relaci. Pokud považujete strukturu procesu Linux za strom, pak je init počátečním uzlem tohoto stromu.
Chcete-li potvrdit, že init je první proces, můžete na svém Linuxovém boxu spustit příkaz pstree. Tento příkaz zobrazí strom procesů pro relaci Linuxu.
Zde je ukázkový výstup:
init-+-NetworkManager-+-dhclient | |-dnsmasq | `-3*[{NetworkManager}] |-accounts-daemon---2*[{accounts-daemon}] |-acpid |-at-spi-bus-laun-+-dbus-daemon | `-3*[{at-spi-bus-laun}] |-at-spi2-registr---{at-spi2-registr} |-avahi-daemon---avahi-daemon |-bamfdaemon---3*[{bamfdaemon}] |-bluetoothd |-colord---{colord} |-console-kit-dae---64*[{console-kit-dae}] |-cron |-cups-browsed |-cupsd |-2*[dbus-daemon] |-dbus-launch |-dconf-service---2*[{dconf-service}] |-evince---3*[{evince}] |-evinced---{evinced} |-evolution-sourc---2*[{evolution-sourc}] |-firefox-+-plugin-containe---16*[{plugin-containe}] | `-36*[{firefox}] |-gconfd-2 |-gedit---3*[{gedit}] |-6*[getty] |-gnome-keyring-d---7*[{gnome-keyring-d}] |-gnome-terminal-+-bash | |-bash-+-less | | `-pstree | |-gnome-pty-helpe | `-3*[{gnome-terminal}] |-gvfs-afc-volume---2*[{gvfs-afc-volume}] |-gvfs-gphoto2-vo---{gvfs-gphoto2-vo} |-gvfs-mtp-volume---{gvfs-mtp-volume} |-gvfs-udisks2-vo---{gvfs-udisks2-vo} |-gvfsd---{gvfsd} |-gvfsd-burn---2*[{gvfsd-burn}] |-gvfsd-fuse---4*[{gvfsd-fuse}] ... ... ...
Výstup potvrzuje, že init je v horní části stromu procesu. Také, pokud budete pozorovat text tučně, uvidíte úplný rodičovský potomek procesu pstree. Přečtěte si více o pstree v našem článku o stromech a pstree.
Nyní se vraťme k otázce (nechali jsme otevřenou v minulé sekci) o důsledcích, když je rodičovský proces zabit, když je dítě stále naživu. V tomto případě se dítě zjevně stane sirotkem, ale je adoptováno procesem init. Proces init se tedy stane novým rodičem těch podřízených procesů, jejichž rodiče jsou ukončeni.
4. Životní cyklus procesu
V této části probereme životní cyklus normálního linuxového procesu, než je zabit a odstraněn z tabulky procesů jádra.
- Jak již bylo řečeno, pomocí fork() se vytvoří nový proces, a pokud má být spuštěn nový spustitelný soubor, potom se po fork() zavolá rodina funkcí exec(). Jakmile je tento nový proces vytvořen, zařadí se do fronty procesů, které jsou připraveny ke spuštění.
- Pokud byla zavolána pouze fork(), je vysoce pravděpodobné, že nový proces běží v uživatelském režimu, ale pokud je zavolána exec(), bude nový proces běžet v režimu jádra, dokud pro něj nebude vytvořen nový adresní prostor procesu.
- Pokud je proces spuštěn, proces s vyšší prioritou jej může předcházet přerušením. V tomto případě se preemptovaný proces znovu zařadí do fronty procesů, které jsou připraveny ke spuštění. Tento proces převezme plánovač v nějaké pozdější fázi.
- Proces může během běhu vstoupit do režimu jádra. To je možné, když to vyžaduje přístup k nějakému zdroji, jako je textový soubor, který je uložen na pevném disku. Protože operace zahrnující přístup k hardwaru mohou nějakou dobu trvat, je vysoce pravděpodobné, že proces přejde do režimu spánku a probudí se pouze tehdy, když budou požadovaná data dostupná. Když je proces probuzen, neznamená to, že se začne okamžitě spouštět, znovu se zařadí do fronty a ve vhodnou dobu bude vybrán ke spuštění plánovačem.
- Proces lze zabít mnoha způsoby. Může zavolat funkci exit() pro ukončení nebo může zpracovat signály Linuxu pro ukončení. Některé signály také nelze zachytit a způsobí okamžité ukončení procesu.
- Existují různé typy procesů Linuxu. Jakmile je proces zabit, není zcela odstraněn. Záznam obsahující některé související informace je uchováván v tabulce adres procesu jádra, dokud nadřazený proces explicitně nezavolá funkce wait() nebo waitpid(), aby získal stav ukončení podřízeného procesu. Dokud to rodičovský proces neudělá, je ukončený proces známý jako zombie proces.