TRAVAUX PRATIQUES Programmation Système Langage C / Système UNIX. 1 ère année Génie Informatique Interface C/UNIX, appels système (NB Enoncés donnés en supplément des exemples et exercices du Poly) 1. Primitive fork() - Créer le programme fork.c suivant: printf("processus fils %d\n", getpid()); else printf("processus pere %d\n", getpid()); - Compiler et exécuter. - Faire afficher aussi et pour chaque processus, le n pid du processus père. Primitive getppid() - Quel est ce processus père? - Constater avec la commande ps, le n pid du shell. - Création de deux fils. printf("un processus fils %d\n", getpid()); else /* Creer un autre fils ici. */ printf("un autre processus fils %d\n", getpid()); else printf("processus pere %d\n", getpid()); - Tester et commenter le programme suivant : printf("a\n"); fork(); printf("b\n"); - Idem pour le programme suivant printf("a\n"); fork(); Najib TOUNSI 1
printf("b\n"); fork(); printf("c\n"); 2. Le Couple fork() / exec() - Créer le programme prog.c suivant: main(int argc, char* argv[]){ printf("le programme %s lancé par un autre\n", argv[0]); - Compiler par: $ cc -o prog prog.c - Exécuter par: $ prog - Quel est cet «autre» programme dans ce cas? - Créer le programme ForkExec.c suivant: #include <unistd.h> execl("prog", "prog", NULL); else printf("le programme normal\n"); - Compiler et exécuter. Constater le résultat. - Transformer prog.c en progparam.c suivant. Compiler le vers prog. main(int argc, char* argv[]){ int i; printf("le programme (%s) lancé et parametres recus:\n", argv[0]); for(i=1; i<argc; i++) printf("%s\n",argv[i]); - Modifier le programme ForkExec.c en remplaçant la ligne execl par: execl("prog", "prog", "Ali", "Rabat", "21", NULL); - Compiler et exécuter. Constater le résultat. 3. Communication entre processus. Pimitive kill(). Avec shell. - Créer le programme bouc.c suivant qui boucle indéfiniment. while(1); - Compiler et exécuter en background par a.out & - Noter le n du processus et faire ps. - Taper maintenant, kill -15 n processus (ou bien kill -TERM n processus ). Refaire ps. Najib TOUNSI 2
- Recommencer avec les signaux 11 (SEGV) ou 9 (KILL), etc... NB. kill -l, donne la liste des signaux possibles. idem, mais plus détaillé, avec man 7 signal. Communication père -> fils. Le père envoie au fils un signal avec kill. - Créer le programme kill.c suivant ou le processus fils boucle indéfiniment et est arrêté par le processus père. int pid; if ((pid=fork()) == 0) while(1); else{ sleep(2); kill (pid, SIGTERM); sleep(2); - Compiler et exécuter. Résultat? - Exécuter en background maintenant (a.out &). - Faire ensuite ps, et attendre la fin avant de refaire ps. Constater le fait. - Faire: man 7 signal - et remplacer SIGTERM par un autre signal. SIGINT, SIGQUIT, SIGBUS, SIGFPE,... Que se passe t-il avec SIGFPE? Vérifier l'existence d'un fichier core. - Utiliser SIGCHLD maintenant, et refaire la même chose qu'en b) et b'). 4. Déroutement de signaux. La primitive signal(). - Créer le programme suivant où le processus père boucle jusqu'à réception de signal (SIGBUS) par le fils : main() { int pid; if ( (pid=fork())!=0){ printf("processus pere: Mon fils est %d\n", pid); /* signal( SIGBUS, handl); */ while(1) sleep(1); /* boucle jusqu'a interruption */ else { sleep(1); printf("processus fils est %d\n",getpid()); kill(getppid(), SIGBUS); /* interrompt le pere */ void handl(int sig){ /* Deroutement du signal recu */ printf("message recu 5/5 %d\n",sig); exit(0); - Compiler et exécuter. Constater le message système (ERREUR BUS) du au signal reçu. - Decommenter maintenant la ligne /* signal( ) */. Refaire l'exécution et constater que Najib TOUNSI 3
le signal a bien été capté par le programme. - Faire varier les signaux captés (exple SIGUSR1, SIGCHLD,...) - Faire un programme simple qui fait une division par zéro, et constater le résultat. Capter le signal SIGFPE maintenant, et vérifier le résultat. int x=1,y=1; /* signal(sigfpe, handl) ; */ x /= x-y; void handl(int sig){ printf("attention, division par zero. Signal: %d\n",sig); exit(0); - Ignorer un signal. Faire le programme qui utilise le handler système SIG_IGN pour ignorer un signal /* signal(sigbus, SIG_IGN) ;*/ sleep(10); - Exécuter en background et envoyer (kill -BUS pid) le signal SIGBUS. Constater le réésultat. $ a.out & [1] 4231 $ kill -BUS 4231 Decommenter la ligne signal() et recommencer. - Capter un signal (handler utilisateur), et revenir au handler système par défaut (SIG_DFL). signal(sigbus, handl) ; while(1); void handl(int sig){ printf("message recu 5/5 signal= %d\n",sig); signal (SIGBUS, SIG_DFL); - Exécuter par: $ a.out& [1] 4553 $ kill -BUS 4553 $ Message recu 5/5 signal= 7 $ kill -BUS 4553 [1]- Bus error a.out et constater le résultat (! 4553 est le n du processus de cet exemple. Utiliser celui qui s affiche dans son cas). 5. Primitives wait() et waitpid(), d'attente entre processus. Najib TOUNSI 4
- Ecrire et exécuter le programme suivant. Constater le résultat. Le père se bloque par wait() en attendant le fils. #include <sys/types.h> #include <sys/wait.h> int status; int pidf, pidw; if ((pidf = fork()) == 0) { system("ps"); sleep(5); /* exit(1025);*/ else { printf("j'attend %d\n",pidf); pidw= wait(&status); printf("%d est termine \n",pidw); /* printf("\n%d %d \n\n", status, WEXITSTATUS(status)); */ - Le père peut récupérer la valeur exit() du fils. (2e octet du paramètre status) #include <sys/types.h> #include <sys/wait.h> int status; int pidf, pidw; if ((pidf = fork()) == 0) { system("ps"); sleep(5); exit(5); else { printf("j'attend %d\n",pidf); pidw= wait(&status); printf("%d est termine \n",pidw); printf("\n%d %d %d \n\n", status, status/256, WEXITSTATUS(status)); - Avec waitpid(), on peut désigner le fils à attendre. Vérifier le sur le programme suivant qui lance deux processus fils et attend le deuxième. #include <sys/types.h> #include <sys/wait.h> main() { pid_t pid, pid1, pid2; int status; if ((pid1 = fork()) == 0) exit(0); else if ((pid2 = fork()) == 0) sleep(2), exit(1); else { system("ps"); pid = waitpid(pid2, &status, 0); printf(" fils termine %d son status %d \n", pid, WEXITSTATUS(status)); - On ne bloque pas le père en attente. (On utilise waitpid(pid1, &status, WNOHANG)). Constater que le programme se termine très vite, car il n'attend pas le fils qui dort. Najib TOUNSI 5
Najib TOUNSI 6