Jak jsem v některém z předchozích dílů napsal, procesor jednotlivé procesy velmi rychle přepíná, čímž vzniká iluze, že běží více procesů najednou (paralelně). Nikdo a nic vám však nemůže zaručit, že jeden proces bude zpracován dříve než druhý, že mu bude poskytnuto přesně tolik a tolik procesorového času atd. Pokud dva procesy (nadřízený a podřízený) mají běžet nezávisle na sobě, je to v pořádku. Pokud je však nadřízený proces závislý na podřízeném, vzniká zde problém. Bude-li nadřízený proces ukončen dříve než proces podřízený, může to ohrozit funkci programu. Tento problém mohou řešit funkce ze skupiny wait, ty umožní volajícímu procesu počkat na ukončení podřízeného procesu.
Čekání na ukončení procesu – systémová volání wait
Nejjednoduší funkcí ze skupiny wait je funkce wait(). Ta zablokuje volající proces až do doby, kdy bude podřízený proces ukončen. Funkce vrací návratový kód procesu pomocí ukazatele na celočíselnou hodnotu, který lze zjistit makrem WEXITSTATUS. Pomocí makraWIFEXITED můžete zjistit, zda byl proces ukončen normálně (voláním funkce exit() nebo návratem z funkce main()), nebo zda byl ukončen na základě nezpracovaného signálu. Pokud byl ukončen signálem, použijte makro WTERMSIG, které zjistí číslo signálu, jímž byl proces ukončen.
Následující příklad spustí nový proces pomocí fork() a execvp() volaných z funkce spawn() uvedené v osmém dílu. Nadřízený proces poté čeká na ukončení podřízeného procesu voláním funkcewait() a zjistí návratový kód.
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
int child_status;
/* Argumenty spousteneho procesu */
char *arg_list[] = {
"ls",
"-l",
"/",
NULL
};
/* Spusti novy proces pomoci funkce spawn().
Ignoruje PID, ktere jinak funkce vraci */
spawn("ls", arg_list);
/* Ceka na ukonceni podrizeneho procesu */
wait(&child_status);
if (WIFEXITED(child_status))
printf("Podrizeny proces byl ukoncen normalne s navratovym kodem %d\n", WEXITSTATUS(child_status));
else
printf("Podrizeny proces byl ukoncen abnormalne\n");
return(0);
}
Dalšími systémovými voláními, která umožňují vyčkat na ukončení podřízeného procesu, jsou waitpid(), wait3() await4():
#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);
#define _USE_BSD
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/wait.h>
pid_t wait3(int *status, int options, struct rusage *rusage)
pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage)
Funkce waitpid() může být použita k čekání na specifický podřízený proces a nikoliv na libovolný (jako funkce wait()). Funkce wait3() vrací statistiku o využití CPU procesem a funkcewait4() umožňuje specifikovat další informace o procesech, na které se má čekat.
Podrobnější informace o třech výše popsaných funkcích naleznete v manuálových stránkách.
Procesy zombie
Proces zombie je proces, který byl ukončen, ale nebyl doposud „uklizen“. Odklizení provádí nadřízený proces. Tento proces automaticky provede funkci wait(), proto není před jejím voláním nutné ověřovat, zda je příslušný podřízený proces stále aktivní. Mějme příklad: program vytvoří nový proces, provade nějaké výpočty a pak na něj zavolá funkci wait(). Když však podřízený proces skončí dříve, než nadřízený proces zavolá funkci wait(), stane se z něj proces zombie. Až nadřízený proces zavolá funkci wait(), získá návratový kód podřízeného procesu, tento proces se zruší a funkce wait() se okamžitě vrátí.
Pokud ale nadřízený proces funkci wait() nezavolá, nebude podřízený proces „uklizen“. Takové procesy potom zůstávají v systému jako procesy zombie a ubírají uživatelům systémové prostředky.
Asynchronní odstraňování podřízených procesů
Když používáte podřízené procesy, je užitečné volat besprostředně po jejich spuštění funkci wait() a čekat na ukončení procesu. Často je však nutné provádět nějakou činnost paralelně s podřízenými procesy. Musíte však dát pozor, aby se nevytvářely procesy zombie. Řešením může být periodické volání funkce wait3() nebo wait4(). Tímto způsobem lze také odklidit procesy zombie, protože funkce wait3()a wait4() lze spustit s parametrem WNOHANG, který zajistí tzv. neblokující mód – funkce uklidí ukončený podřízený proces (pokud existuje) nebo se vrátí, pokud neexistuje. Nadřízený proces tedy nebude blokován jako v případě, kdybyste volali funkci wait().
Dalším způsobem, jak odstraňovat ukončené podřízené procesy, je zpracování signálu SIGCHLD. Tento signál je zasílán nadřízenému procesu pokaždé, když je podřízený proces ukončen. Stačí jej tedy pouze zachytit (podrobně se budu zabývat zpracováním signálů v některém z dalších dílů) a spustit funkci wait(). Pokud potřebujete návratový kód procesu, je nutné jeho hodnotu uložit. Protože pokud je proces jednou uklizen, není možné ji zpátky získat.
V příkladu je použita funkce clean_up(), která se zavolá pokaždé, když je zachycen signál SIGCHLD.
#include <signal.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
sig_atomic_t child_exit_status;
void clean_up(int signal_number)
{
/* Odstrani podrizeny proces */
int status;
wait(&status);
/* Priradi navratovy kod globalni promene */
child_exit_status = status;
}
int main()
{
/* Dalsi prikazy nastavi zpracovani signalu SIGCHLD */
struct sigaction sigchld_action;
memset(&sigchld_action, 0, sizeof(sigchld_action));
sigchld_action.sa_handler = &clean_up;
sigaction(SIGCHLD, &sigchld_action, NULL);
/* Nyni uz muzete vytvaret nove procesy, provadet vypocty ... */
/* ... */
return(0);
}
Dnes by to bylo vše, doufám, že se vám nově získané informace budou hodit. V příštím dílu se začneme zabývat vlákny – vysvětlíme si, o co jde, a vytvoříme si nové vlákno.