Etudier le comportement des différents processus créés par le programme suivant, en supposant que l'exécution de la fonction alpha(n) (dont le détail est sans importance) dure 2n secondes.
#define BASE 100000 int duree; /* Boucle de calcul */ int alpha (int n) { int i, s = 0; n = n * duree; for (i = 1; i < n; i++) s = s + i * i; return s; } main (int argc, char **argv) { pid_t pid; int etat; duree = atoi (argv [1]) * BASE; if (fork () == 0) if (fork () == 0) sleep (10); else { alpha (2); exit (2); } if (fork () == 0) { alpha (3); exit (3); } if (fork () == 0) { sleep (2); exit (10); } alpha (1); pid = wait (&etat); pid = wait (&etat); sleep (3); alpha (1); pid = wait (&etat); pid = wait (&etat); }
Cette trace d'exécution est obtenue en ajoutant diverses fonctions d'affichage (voir le programme détaillé) ; sur la machine utilisée, le choix de l'argument 20 pour le lancement du programme permet d'obtenir un temps de calcul voisin de 2n secondes pour la fonction alpha(n).Des commentaires (en vert) ont été ajoutés manuellement ; dans ces commentaires, on n'a gardé que les trois derniers chiffres des numéros (pid) des processus.
almira% forkw 20 [0.00] 26790 (25733) : créé [0.01] 26791 (26790) : créé [0.02] 26793 (26791) : créé et endormi 10 secondes [0.02] 26794 (26790) : créé et endormi 2 secondes [0.03] 26791 (26790) : calcule (2) [0.03] 26790 (25733) : calcule (1) [0.14] 26792 (26790) : créé et calcule (3) 790 / | \ / | \ / | \ 791 792 794 | | 793 [2.59] 26794 (26790) : terminé Temps cpu 0.00 , temps système 0.01 Ce processus a simplement dormi deux secondes. 790, 791, 792 calculent simultanément. [6.24] 26790 (25733) : attend [6.29] 26790 : reçoit le signal de fin de 26794 (code 10) [6.35] 26790 (25733) : attend Un peu plus de six secondes se sont écoulées avant que 790 termine son calcul, d'une durée théorique de deux secondes, car trois processus actifs se partagent le processeur. Dans le jargon Unix, on dit que le temps réel vaut 6 secondes et le temps cpu 2 secondes. 790 / | / | / | 791 792 | | 793 791 et 792 calculent. [10.15] 26793 (26791) : réveillé [10.16] 26795 (26793) : créé et calcule (3) [10.20] 26793 (26791) : calcule (1) [10.58] 26796 (26793) : créé et endormi 2 secondes Noter que le code exécuté par le processus 793 ne comporte pas d'appel-système exit après sleep (10). Donc ce processus, une fois réveillé, crée les processus 795 et 796, comme le processus 790 l'a fait pour les processus 792 et 794. 790 / | / | / | 791 792 | | 793 / \ / \ 795 796 791, 792, 793, 795 calculent simultanément [12.07] 26791 (26790) : terminé Temps cpu 4.10 , temps système 0.00 [12.21] 26790 : reçoit le signal de fin de 26791 (code 2) [12.21] 26790 (25733) : endormi 3 secondes [12.59] 26796 (26793) : terminé Temps cpu 0.00 , temps système 0.00 790 | | | 792 | | 793 / \ / \ 795 796 (zombi) [15.21] 26790 (25733) : réveillé et calcule (1) [18.97] 26792 (26790) : terminé Temps cpu 6.22 , temps système 0.00 [19.34] 26790 (25733) : attend [19.36] 26790 : reçoit le signal de fin de 26792 (code 3) [19.36] 26790 (25733) : attend [19.37] 26790 n'a plus de fils [19.37] 26790 (25733) : terminé Temps cpu 4.10 , temps système 0.05 Le temps réel d'exécution de 792 vaut environ 19 secondes, pour un temps cpu de six secondes, car plusieurs processus actifs se sont partagé le processeur. almira% [21.52] 26793 (1) : attend [21.54] 26793 : reçoit le signal de fin de 26796 (code 10) [21.87] 26793 (1) : attend Noter que 793, orphelin, est rattaché au processus 1, et que l'invite réapparaît après la terminaison de 790. Les processus 793 et 795 existent en arrière-plan. [22.89] 26795 (26793) : terminé Temps cpu 6.20 , temps système 0.00 [22.90] 26793 : reçoit le signal de fin de 26795 (code 3) [22.90] 26793 (1) : endormi 3 secondes Le temps réel d'exécution de 795 vaut environ treize secondes, pour un temps cpu de six secondes, car plusieurs processus actifs se sont partagé le processeur. [25.90] 26793 (1) : réveillé et calcule (1) [27.95] 26793 (1) : attend [27.95] 26793 n'a plus de fils 793 est seul au monde ! [27.95] 26793 (1) : attend [27.95] 26793 n'a plus de fils [27.96] 26793 (1) : terminé Temps cpu 4.13 , temps système 0.01 Diagramme récapitulatif d'exécution des principaux processus : alpha(1) attend dort alpha(1) 790 |------------| | |--------| 0.03 6.24 12.21 15.21 19.37 alpha(2) 791 |-------------------------| 0.03 12.07 alpha(3) 792 |----------------------------------------| 0.14 18.97 dort alpha(1) attend dort alpha(1) 793 | |------------------------| | |------| 0.02 10.15 21.52 22.90 25.90 27.95 alpha(3) 795 |-------------------------------| 10.16 22.89Le temps d'exécution global est donc d'environ 28 secondes ; le processeur est occupé en permanence pendant les 23 premières secondes :Si un processus est terminé par un signal, et si le processus père attend sa terminaison, il obtient le numéro du signal ayant provoqué la terminaison de son fils. Exemple :
- les calculs simultanés durent environ 11*2=22 secondes ;
- le reste du temps (pendant ces 23 secondes) est consacré aux créations de processus, aux changements de contexte, aux exécutions des appels-système (attente, mise en sommeil, et affichage) ; aucun processus externe ne semble perturber l'exécution de ce programme.
almira% forkw 20 [0.00] 16594 (16476) : créé [0.01] 16595 (16594) : créé [0.01] 16596 (16595) : créé et endormi 10 secondes [0.03] 16597 (16594) : créé et calcule (3) [0.05] 16598 (16594) : créé et endormi 2 secondes [0.08] 16594 (16476) : calcule (1) [0.34] 16595 (16594) : calcule (2) [2.06] 16598 (16594) : terminé Temps cpu 0.00 , temps système 0.01 [6.47] 16594 (16476) : attend [6.55] 16594 : reçoit le signal de fin de 16598 (code 10) [6.59] 16594 (16476) : attend La commande kill 16597 est émise depuis une autre fenêtre. [8.00] 16594 : reçoit le signal de fin de 16597 (signal 15) 15 est le numéro de SIGTERM, émis par défaut par la commande kill.On peut aussi envoyer un signal à tous les processus créés par ce programme car le premier est leader de groupe.almira% ps -j PPID PID PGID SID TT TPGID STAT UID TIME COMMAND 16475 16476 16476 16475 p3 16641 SE 1006 0:00 -bin/csh (csh) 16476 16641 16641 16475 p3 16641 SE 1006 0:02 forkw 20 16641 16643 16641 16475 p3 16641 R 1006 0:04 forkw 20 16645 16646 16641 16475 p3 16641 R 1006 0:00 forkw 20 almira% kill -INT -16641Les processus 16641, 16643 et 16646 reçoivent le signal SIGINT, et sont tués faute de moniteur pour traiter le signal.
#include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> #define BASE 100000 int duree; double depart; int alpha (int n) { int i, s = 0; n = n * duree; for (i = 1; i < n; i++) s = s + i * i; return s; } double chrono () { struct timeval date; gettimeofday (&date, 0); return date.tv_sec + date.tv_usec * 1e-6; } void identite (char *message) { printf ("[%.2lf] ", chrono () - depart); printf ("%d (%d) : %s\n", getpid (), getppid (), message ); } void attendre (void) { pid_t pid; int etat; identite ("attend"); pid = wait (&etat); printf ("[%.2lf] ", chrono () - depart); if (pid < 0) { printf ("%d n'a plus de fils\n", getpid ()); return; } printf ("%d : reçoit le signal de fin de %d", getpid (), pid); if (WIFEXITED (etat) ) printf (" (code %d)\n", WEXITSTATUS (etat) ); else printf (" (signal %d)\n", WTERMSIG (etat) ); } void terminer (int code) { struct rusage ressource; double cpu, sys; identite ("terminé"); getrusage (RUSAGE_SELF, &ressource); cpu = ressource.ru_utime.tv_sec + ressource.ru_utime.tv_usec * 1e-6; sys = ressource.ru_stime.tv_sec + ressource.ru_stime.tv_usec * 1e-6; printf (" Temps cpu %.2lf , temps système %.2lf\n", cpu, sys); exit (code); } main (int argc, char **argv) { struct timeval date; if (argc < 2) duree = BASE; else duree = atoi (argv [1]) * BASE; depart = chrono (); identite ("créé"); if (fork () == 0) { identite ("créé"); if (fork () == 0) { identite ("créé et endormi 10 secondes"); sleep (10); identite ("réveillé"); } else { identite ("calcule (2)"); alpha (2); terminer (2); } } if (fork () == 0) { identite ("créé et calcule (3)"); alpha (3); terminer (3); } if (fork () == 0) { identite ("créé et endormi 2 secondes"); sleep (2); terminer (10); } identite ("calcule (1)"); alpha (1); attendre (); attendre (); identite ("endormi 3 secondes"); sleep (3); identite ("réveillé et calcule (1)"); alpha (1); attendre (); attendre (); terminer (0); }