Linux - Procesy (Processes)
Běžící instance programu se nazývá proces. Například dvě okna
terminálu jsou instancemi stejného terminálového programu, ale jsou to dva
různé procesy. V každém okně běží shell, který je zase jiný proces. Spuštěním
programu v tomto shellu se vytvoří další nový proces.
Více spolupracujících procesů v jedné aplikaci jí umožňuje dělat více věcí
najednou.
#include <unistd.h> |
typedef int __pid_t;
typedef __pid_t pid_t;
|
getpid(2) |
#include <unistd.h> |
pid_t getpid(void); |
-
vrací jedinečné pid aktuálního procesu
|
Př.
Napište program, který vypíše pid svého procesu.
Program nemažte, budete ho rozšiřovat o další probírané funkce!
getppid(2) |
#include <unistd.h> |
pid_t getppid(void); |
-
vrací jedinečné pid rodiče aktuálního procesu
-
každý proces má rodiče (kromě procesu init), procesy jsou uspořádány do
stromové struktury s procesem init jako vrcholem
|
Př.
Vypište pid rodiče.
ps(1) |
ps [options] |
-
defaultně (bez parametrů) vypíše procesy kontrolované terminálem, ve kterém
byl
ps spuštěn
-
přepínači lze určit, jaké procesy a informace o nich se mají vypsat, např.
-e pro všechny procesy, -u user pro procesy uživatele user, -l pro dlouhý
formát výpisu, -o pro vlastní formát, atd.
|
Př.
Vypište si všechny své procesy ve tvaru STAT USER PID
PPID NICE CMD
kill(1) |
kill pid ... |
-
ukončí procesy pid ... (pošle jim signál SIGTERM)
|
Př.
Spusťte si program top
a ukončete ho pomocí SIGTERM.
Vytvoření procesu
system(3) |
#include <stdlib.h> |
int system (const char * string); |
-
spustí příkaz ve string voláním /bin/sh -c string
-
její používání není bezpečné (blokuje některé signály), doporučuje se
používat
fork a exec
-
vrací 127, pokud shell nelze spustit, -1 při jiné chybě, jinak návratový kód
příkazu
|
Př.
Spusťte příkaz ls -la
.
fork(2) |
#include <unistd.h> |
pid_t fork(void); |
-
vytvoří stejný proces jako svého potomka, který se liší jen v pid a ppid
-
rodičovský proces i jeho potomek pokračují ve vykonávání programu od místa
volání fork
-
v rodiči vrací pid potomka, v potomkovi nulu, při chybě -1 v rodiči, potomek
se nevytvoří
|
Př.
Vytvořte potomka, který vypíše svoje pid.
exec(3) execve(2)
|
#include <unistd.h> |
int execl( const char *path, const char *arg, ...);
int execlp( const char *file, const char *arg, ...);
int execle( const char *path, const char *arg , ..., char * const envp[]);
int execv( const char *path, char *const argv[]);
int execvp( const char *file, char *const argv[]);
int execve (const char *filename, char *const argv [], char *const
envp[]);
|
-
tyto funkce nahradí aktuální program jiným
-
první argument je soubor (včetně cesty), který se má vykonat
-
const char *arg, ... jsou arg0, ...
spuštěného programu, poslední prvek seznamu musí být NULL
-
char *const argv[] je seznam arg0, ...
spuštěného programu, poslední prvek pole musí být NULL
-
char * const envp[] je seznam proměnných prostředí
spuštěného programu, poslední prvek seznamu musí být NULL, prvky
seznamu jsou očekávány ve tvaru "PROMENNA=hodnota"
-
funkcím
execvp a execlp stačí jen jméno
souboru ke spuštění, který se hledá ve vyhledávací cestě (proměnná PATH)
-
pokud se funkce ukončí (vždy -1), je to chyba
|
Při vytváření nového procesu se nejdříve zavolá fork
,
který vytvoří kopii
aktuálního procesu, a pak exec
, který nový proces změní
na instanci programu,
který chceme spustit.
Př.
Spusťte příkaz date +%s
pomocí
fork
a exec
.
Linux plánuje procesy nezávisle, není zaručeno, který poběží dřív, jak
dlouho poběží.
nice(1) |
nice [OPTION]... [COMMAND [ARG]...] |
-
spustí program s jinou prioritou (defaultně má nový proces prioritu 0)
-
rozsah priorit je -20 (nejvyšší) až 19 (nejnižší)
-
pro novou prioritu je přepínač -n
-
jenom root může spustit proces se zápornou prioritou
|
Př.
Spusťte program yes
s nice 5. Zkontrolujte
si to pomocí ps
. Zkuste -5.
renice(1) |
renice priority [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...] |
-
změní prioritu běžících procesů
-
procesy lze určit pomocí pid nebo všechny procesy uživatele user
(defaultně pid)
-
priority může být [+-]číslo, priority = nice
-
jenom root může zvyšovat prioritu (snižovat nice)
|
Př.
Zvyšte nice programu yes
na 19.
Zkontrolujte si to pomocí ps
. Zkuste nice snížit.
nice(2) |
#include <unistd.h> |
int nice(int inc); |
-
přidá inc k prioritě volájícího procesu
-
jenom root může zadat záporný inc
-
vrací 0, při chybě -1
|
Př.
Zvyšte nice potomka.
sleep(3) |
#include <unistd.h> |
unsigned int sleep(unsigned int seconds); |
-
"uspí" proces na seconds sekund, nebo ho probudí neignorovaný signál
-
vrací 0, pokud čas uplynul, jinak zbývající počet sekund
|
Př.
Pozdržte potomka 2 sekundy a vypište datum znovu.
Ukončení procesu
exit(3) |
#include <stdlib.h> |
void exit(int status); |
-
normálně ukončí program, status je vrácen rodiči
-
funkce se neukončí
|
Př.
Ošetřete neúspěšný fork
pomocí
exit
.
int main(int argc, char *argv[], char *envp[]); |
-
ukončení
main je normální ukončení, návratová hodnota
vrácena rodiči
-
návratová hodnota funkce
main je sice int, ale používat
by se měly jen
hodnoty 0-127, kódy nad 128 včetně mají speciální význam - když je proces
ukončen signálem, návratová hodnota je 128 + číslo signálu
|
Př.
Ukončete korektně rodiče i potomka návratem z
main
.
abort(3) |
#include <stdlib.h> |
void abort(void); |
-
abnormálně (s core souborem) ukončí program
-
funkce se neukončí
|
Př.
Ošetřete neúspěšný exec
pomocí
abort
.
Potomek může proběhnout až po ukončení předka, protože oba procesy jsou
plánovány nezávisle. To je někdy nežádoucí, někdy chceme, aby rodič počkal,
až se potomek ukončí.
wait(2) |
#include <sys/types.h>
#include <sys/wait.h>
|
pid_t wait(int *status) |
-
zastaví proces, dokud se neukončí nějaký potomek, nebo pokud proces dostane
ukončující signál nebo se volá nějaký signal-handler
-
pokud už potomek skončil (zombie), funkce se hned ukončí, všechny prostředky
systému přidělené potomkovi jsou uvolněny
-
pokud status není NULL, na místo, kam ukazuje, se uloží informace o potomkovi
-
vrací pid potomka, při chybě -1
|
Př.
V rodiči počkejte na potomka.
waitpid(2) |
#include <sys/types.h>
#include <sys/wait.h>
|
pid_t waitpid(pid_t pid, int *status, int options); |
-
zastaví proces, dokud se neukončí potomek s ID pid, nebo pokud proces dostane
ukončující signál nebo se volá nějaký signal-handler
-
pokud už potomek skončil (zombie), funkce se hned ukončí, všechny prostředky
systému přidělené potomkovi jsou uvolněny
-
pokud status není NULL, na místo, kam ukazuje, se uloží informace o potomkovi
-
pokud je pid -1, funkce se chová jako wait
-
vrací pid potomka, při chybě -1, nebo 0, pokud žádný potomek neskončil a v
options bylo WNOHANG
|
Ze status
(int) lze získat různé informace o potomkovi
těmito makry:
WIFEXITED(status) |
-
vrací nenulové číslo, pokud potomek spončil normálně
|
WEXITSTATUS(status) |
-
vrací návratový kód potomka
-
smí se vyhodnotit, jen pokud
WIFEXITED je nenulové
|
Př.
Vypište návratový kód potomka.
WIFSIGNALED(status) |
-
vrací nenulové číslo, pokud potomka ukončil signál
|
WTERMSIG(status) |
-
vrací číslo signálu, který ukončil potomka
-
smí se vyhodnotit, jen pokud
WIFSIGNALED je nenulové
|
Zombie procesy
Co se stane, když potomek skončí a rodič na něj nečeká voláním
wait
?
Informace o jeho ukončení budou dočasně ztraceny a z něj se stane zombie.
Zombie je proces, který skončil, ale ještě nebyl ze systému
odstraněn, to je
povinnost rodiče. Potom, co se proces ukončí, stane se z něj zombie, dokud
si informace o jeho ukončení nevyzvedne jeho rodič pomocí wait
.
Pokud si rodič tyto informace nevyzvedne nikdy (ani po svém ukončení),
zombie adoptuje proces init a odstraní jej ze systému.
OTÁZKA: Co se s potomkem stane, když rodič skončí dřív než on? Bude bez
rodiče?
PŘÍKLAD
Další funkce týkající se procesů jsou:
-
getuid(2) / setuid(2)
-
geteuid(2) / seteuid(2)
-
getgid(2) / setgid(2)
-
getegid(2) / setegid(2)
- clone(2)
- vfork(2)
-
getpriority(2) / setpriority(2)
- wait3(2)
- wait4(2)