Cu ajutorul apelului fork putem crea noi procese, iar cu ajutorul apelurilor din familia exec putem initia noi programe. Cand un proces foloseste una din functiile exec, procesul este complet inlocuit cu noul program, iar noul program incepe executia functiei sale main. PID-ul nu se schimba prin folosirea exec, deoarece nu se creeaza un nou proces. exec doar inlocuieste procesul curent (segmentele de text, date, heap, stiva) cu un program nou de pe disc.
Sunt 6 functii in familia de apeluri exec. Sintaxele de apel sunt:
#include <unistd.h> char **environ; int execl (const char *path, const char *arg0, ..., (char *) NULL); int execlp (const char *file, const char *arg0, ..., (char *) NULL); int execle (const char *path, const char *arg0, ..., (char *) NULL, const char *envp[]); int execv (const char *path, const char *argv[]); int execvp (const char *file, const char *argv[]); int execve (const char *path, const char *argv[], const char *envp[]);
Aceste 6 functii se incadreaza in doua categorii. execl, execlp, execle au un numar variabil de parametri care se incheie cu un pointer NULL. execv, execvp, execve au un vector de siruri ca al doilea parametru. In ambele cazuri, noul program porneste cu argumentele date in vectorul argv transmis ca parametru functiei main. De obicei toate functiile sunt implementate folosind execve, desi nu este obligatoriu.
Functiile care se termina in p (execlp, exevp) se deosebesc de celelalte prin faptul ca vor cauta variabila de mediu PATH pentru a gasi executabilul pentru fisierul prezentat. Daca executabilul nu este in PATH, va trebui transmis un nume complet (denumire absoluta).
Variabila globala environ este folosita pentru transmiterea unei valori mediului noului program. Alternativ, un argument al functiilor execve si execle permite transmiterea unui vector de siruri care vor fi folosite ca noul mediu al programului.
Un exemplu de folosire a celor 6 functii exec este prezentat mai jos (shamelessly copied from ``Beginning Linux Programming, 2nd Edition''):
/* * exemple de utilizare a functiilor din familia exec; se considera * pentru exemplificare comanda "ps -ax" * * din "Beginning Linux Programming, second edition" * capitolul 10 "Processes and Signals" */ #include <unistd.h> /* exemplu de lista de argumente */ /* argv[0] trebuie sa fie un nume de program */ const char *ps_argv[] = {"ps", "-ax", 0}; /* exemplu de mediu al procesului; nu este neaparat necesar */ const char *ps_envp[] = {"PATH=/bin:/usr/bin", "TERM=console", 0}; /* apeluri posibile ale functiilor din familia exec */ execl ("/bin/ps", "ps", "-ax", 0); /* presupunem ca ps e in /bin */ execlp ("ps", "ps", "-ax", 0); /* presupunem ca /bin e in PATH */ execle ("/bin/ps", "ps", "-ax", 0, ps_env); /* se transmite mediul propriu */ execv ("/bin/ps", ps_argv); execvp ("ps", ps_argv); execve ("/bin/ps", ps_argv. ps_env);
Urmatorul exemplu este un exemplu complet. Un proces creeaza un proces fiu. Procesul fiu va folosi exec pentru ``ls -l'', iar procesul parinte va astepta terminarea fiului si, daca acesta a incheiat cu succes, va folosi exec pentru ``df -h''. Implementarea este o simplificare a operatorului ``&SPMamp;'' din interpretorul de comenzi. (daca avem ``cmd1 && cmd2'' se va executa cmd1 si apoi si cmd2, dar numai daca cmd1 a intors cod de iesire 0 - succes)
/* * simple_exec.c - utilizare simpla a apelului exec(3); implementare * foarte simplificata a operatorului && din * interpretorul de comenzi */ #include <stdio.h> #include <stdlib.h> #include <sys/types.h> #include <unistd.h> #include <sys/wait.h> /* * in cadrul functiei main se creeaza un proces fiu; acesta va executa * (folosind exec) comanda ls -l; procesul parinte asteapta terminarea * fiului si, daca fiul s-a terminat normal, apoi executa comanda df -h */ int main (void) { pid_t pid; /* valoare intoarsa de fork(2) */ int status; /* variabila utilizata de wait(2) */ /* se creeaza procesul fiu */ pid = fork (); switch (pid) { case 0: /* procesul fiu */ /* se executa comanda ls -l folosind execlp */ if (execlp ("ls", "ls", "-l", 0) < 0) { perror ("exec"); exit (EXIT_FAILURE); } exit (EXIT_SUCCESS); break; case -1: /* eroare */ perror ("fork"); exit (EXIT_FAILURE); break; default: /* procesul parinte */ break; } /* codul ce urmeaza va fi executat doar de procesul parinte */ /* se astepta terminarea procesului fiu */ if (wait (&status) < 0) { perror ("wait"); exit (EXIT_FAILURE); } /* se verifica daca procesul fiu s-a terminat normal */ if (WIFEXITED (status) && WEXITSTATUS (status) == 0) { /* * daca procesul fiu s-a terminat normal, se executa * comanda "df -h" folosind execlp */ if (execlp ("df", "df", "-h", 0) < 0) { perror ("exec"); exit (EXIT_FAILURE); } } return 0; }
Dupa rulare, programul va genera urmatoarea iesire:
razvan@ragnarok:~/cfiles/solab/lab2$ gcc -Wall -pedantic -ansi -o exec simple_exec.c razvan@ragnarok:~/cfiles/solab/lab2$ ./exec total 288 -rw-r--r-- 1 razvan razvan 1008 Oct 2 12:33 commands.txt -rwxr-xr-x 1 razvan razvan 12683 Oct 2 12:48 exec -rw-r--r-- 1 razvan razvan 854 Oct 2 12:35 exec_usage.c -rw-r--r-- 1 razvan razvan 2099 Oct 2 11:57 fork_init.c drwxr-xr-x 2 razvan razvan 4096 Oct 1 22:08 lab2 -rw-r--r-- 1 razvan razvan 13898 Oct 1 22:11 lab2-split.tgz -rw-r--r-- 1 razvan razvan 29620 Oct 1 22:10 lab2.html -rw-r--r-- 1 razvan razvan 34163 Oct 2 12:46 lab2.lyx -rw-r--r-- 1 razvan razvan 116665 Oct 1 22:04 lab2.ps -rw-r--r-- 1 razvan razvan 26803 Oct 1 22:08 lab2.tex -rw-r--r-- 1 razvan razvan 10509 Oct 1 22:11 lab2.tgz -rw-r--r-- 1 razvan razvan 1982 Oct 2 12:47 simple_exec.c -rw-r--r-- 1 razvan razvan 1967 Oct 2 11:55 simple_fork.c -rw-r--r-- 1 razvan razvan 1774 Oct 2 12:14 wait_zombie.c Filesystem Size Used Avail Use% Mounted on /dev/hda8 9.9G 5.2G 4.2G 56% / tmpfs 126M 0 126M 0% /dev/shm