430 likes | 608 Views
Systèmes d’exploitation. Les processus. Introduction. Concept de processus est le important dans un SE Processus = abstraction d’un programme en cours d’exécution
E N D
Systèmes d’exploitation Les processus
Introduction • Concept de processus est le important dans un SE • Processus = abstraction d’un programme en cours d’exécution • Tous les ordinateurs modernes peuvent exécuter plusieurs tâches à la fois (ex : programme en cours de calcul + affichage sur un terminal + impression) • MAIS le processeur, à un instant donné, n’exécute réellement qu’un seul programme • Dans un système multiprogrammé, le processeur passe d’un programme à un autre en exécutant chaque programme pendant quelques dizaines de millisecondes impression de parallélisme • Ce parallélisme est qualifié de pseudo-parallélisme Attention : ne pas confondre avec le parallélisme qui s’effectue au niveau matériel (nécessite plusieurs processeurs) • Le contrôle de plusieurs activités en pseudo-parallélisme est une tâche difficile qui a fait l’objet de nombreuses améliorations pour obtenir le modèle actuel
processus Un seul compteur ordinal 4 compteurs ordinaux D — —C — — B — —A — — A Commutationde processus B C C B D A Modèle conceptuel de4 processus séquentiels indépendants D Un seul programme actif à un instant donné Multiprogrammation de 4 processus Temps Le modèle des processus (1) • Un processus est un programme qui s’exécute et qui possède son compteur ordinal, ses registres et ses variables • Conceptuellement, chaque processus a son propre processeur virtuel • En réalité, le processeur commute entre plusieurs processus • Cette commutation rapide est appelée multiprogrammation
Le modèle des processus (2) • Comme le processeur commute entre les processus, la vitesse d’exécution d’un processus ne sera pas uniforme et variera vraissemblablement si les mêmes processus sont exécutés à nouveau • Attention : Ne pas confondre programme et processus (ex : confection d’un gâteau et piqûre d’abeille) • L’idée clé est qu’un processus est une activité d’un certain type qui possède un programme, des données, en entrée et en sortie, ainsi qu’un état courant • Un seul processeur peut être partagé entre plusieurs processus en se servant d’un algorithme d’ordonnancement qui détermine quand il faut suspendre un processus pour en servir un autre
Hiérarchie entre les processus • Les SE qui font appel au concept de processus doivent permettre de créer et détruire dynamiquement les processus • 2 exemples : Unix et MS-DOS • Unix : • Les processus sont créés par l’appel système fork • Le fork crée une copie conforme du processus appelant • À la suite du fork, le processus père continue à s’exécuter en « parallèle » avec son fils • Le processus père peut créer d’autres fils et ces processus fils peuvent eux-mêmes avoir des fils Arborescence de processus hiérarchie • MS-DOS : • Un appel système pour charger un fichier binaire en mémoire et l’exécuter en tant que processus fils • Contrairement à Unix, MS-DOS suspend le père jusqu’à ce que le fils ai terminé son exécution pas de pseudo-parallélisme
Les différents états d’un processus • Les processus, bien qu’étant des entités indépendantes doivent parfois interagir avec d’autres processus (ex : cat fic1 fic2 | grep mot) • L’ordre et la vitesse d’exécution des processus peut amener un des processus à se bloquer (ex : le grep est prêt à s’exécuter mais ne peut le faire faute de données) • Un processus se bloque lorsqu’il ne peut pas, pour une raison logique, poursuivre son exécution (ex : attente de données) • Un processus élu peut être arrêté et mis en situation d’attente (on dit qu’il est prêt), même s’il peut poursuivre son exécution (ex : le SE décide d’allouer le processeur à un autre processus) • Ces 2 situations sont totalement différentes : • Dans le premier cas, la suspension est inhérente au problème • Dans le second cas, il s’agit d’une caractéristique technique du système • 1. Élu (en cours d’exécution) • 3 états possibles : 2. Prêt (suspendu pour permettre l’exécution d’un autre processus) • 3. Bloqué (attendant un événement extérieur pour pouvoir continuer)
4 transitions peuvent avoir lieu entre les états 2 1 1. Le processus se bloque en attente de données 2. L’ordonnanceur choisit un autre processus Élu 3 3. L’ordonnanceur choisit le processus initial 4. Les données deviennent disponibles 4 • La transition 1 se produit lorsqu’un processus ne peut plus poursuivre son exécution Bloqué Prêt • La transition 2 a lieu lorsque l’ordonnanceur décide que le processus en cours s’est exécuté pendant une durée suffisante ; cette opération est appelée réquisition • La transition 3 se produit lorsque tous les processus ont obtenu leur quota de temps et qu’il faut relancer le premier processus • La transition 4 est réalisée si l’évènement extérieur attendu par un processus se produit Les transitions entre les états • Les transitions 2 et 3 sont provoqués par l’ordonnanceur de processus (module du SE) • Le rôle de l’ordonnanceur est très important : il choisit le processus à exécuter ainsi que le moment où il faut le lancer ; il constitue le niveau le plus bas du SE • Il existe stratégies d’ordonnancement, qui seront étudiées dans la suite du cours
Processus • Le nouveau modèle obtenu : 0 1 … n-1 n Ordonnanceur (scheduler) Le modèle des processus (suite) • Le modèle des processus permet de mieux comprendre ce qui se passe à l’intérieur du SE : • Une partie des processus exécutent les programmes des utilisateurs • Les autres processus font partie du système et gèrent les tâchent telles que les requêtes au gestionnaire de fichiers ou les opérations sur disques • L’ordonnanceur constitue le niveau le + bas du SE • Il est surmonté d’une multitude de processus • La gestion des interruptions, la suspension et la relance des processus sont déportées dans l’ordonnanceur • Le reste du SE est structuré sous forme de processus
La réalisation des processus • Pour mettre en œuvre pratiquement le modèle de processus, le SE a une table (tableau de structures) : la table des processus (TP) dont chaque entrée correspond à un processus particulier • Chaque entrée comporte des informations sur : • l’état du processus • son compteur ordinal • son pointeur de pile • son allocation mémoire • l’état des fichiers ouverts • … et tout ce qui doit être sauvé lorsqu’un processus passe de l’état élu à l’état prêt • Le contenu de la table des processus varie d’un système à un autre • En général, les informations concernent la : • La gestion du processus • La gestion mémoire • La gestion des fichiers
P1 P5 noyau TP • la TP est interne au noyau : les processus P5 P1 Les processus Unix • Un processus Unix se décompose en : • Un espace d’adressage (visible par l’utilisateur/le programmeur) • Le bloc de contrôle du processus (BCP) lui-même décomposé en : • Une entrée dans la TP du noyau struct proc définie dans <sys/proc.h> • Une struct user appelée zone u définie dans <sys/user.h> • Les processus Unix apportent • La multiplicité d’exécution (plusieurs processus peuvent être l’exécution d’un même prog.) • La protection des exécutions (un processus ne peut exécuter que ses instructions propres et ce de façon séquentielle) • Les processus Unix communiquent entre eux et avec le reste du monde grâce aux appels systèmes
Création d’un processus Unix • Cette création est réalisée par l’appel système : int fork(void); • Chaque processus est identifié par un numéro unique, son PID • Tous les processus (sauf le processus de pid=0) sont créés par un appel à fork • Le processus qui appelle le fork est appelé processus père • Le nouveau processus est appelé processus fils • Tout processus a 1 seul père mais peut avoir 0 ou plusieurs fils • Le processus de pid=0 est créé « manuellement » au démarrage de la machine • ce processus joue un rôle spécial pour le système (swappeur,gestionnaire de pages) • pour le bon fonctionnement des prog. utilisant fork, ce processus reste toujours utilisé • ce procéssus crée grâce à un appel fork, le processus init de pid=1 • Le processus de pid=1 est l’ancêtre de tous les autres processus (le processus 0 ne réalisant plus de fork) • il a notamment pour rôle de recueillir tous les processus orphelins de père (ceci afin de collecter les informations à la mort de chaque processus)
Format d’un fichier exécutable • Les compilateurs permettent de créer des fichiers exécutables • Ces fichiers ont un format particulier qui permet au noyau de les transformer en processus • Une en-tête qui décrit l’ensemble du fichier, ses attributs et sa carte des sections • Une section TEXT qui contient le code (en langage machine) • Une section DATA codée en langage machine qui comporte 2 parties : • Une partie pour les données initialisées • Une autre pour les données non initialisées (BSS) • Éventuellement d’autres sections: • Table des symboles pour le débuggeur • Images • Icones • Tables des chaînes • …
Chargement/changement d’un exécutable • L’appel système execve change l’exécutable du processus courant en chargeant un nouvel exécutable • Pour chaque section, de l’exécutable une région mémoire est alloué; soit au moins : • les régions code et données initialisées • mais aussi les régions des piles et du tas • La pile est une pile de structures qui sont empilées et dépilées lors de l’appel ou le retour de fonction • le pointeur de pile, un des registres de l’UC donne la profondeur courante de la pile • le code du programme gère les extensions de pile, c’est le noyau qui alloue l’espace nécessaire à ces extensions • un processus Unix pouvant s’exécuter dans 2 modes (noyau, utilisateur), une pile privée sera utilisée dans chaque mode • Le tas est une zone où est réalisée l’allocation dynamique avec les fonctions : Xalloc()
Structure interne des processus Adresse haute = 0xFFFFFFFF initialisé par exec lu par exec Adresse basse = 0
zone u et table des processus • Tous les processus sont associés à une entrée dans la TP qui interne au noyau • De +, le noyau alloue pour chaque processus une structure appelée zone u qui contient les données privées du processus (uniquement manipulable par le noyau) • La TP, permet d’accéder à la table des régions par processus (TRP) • Ce double niveau d’indirection permet le partage des régions • Dans une organisation en mémoire virtuelle, la TRP est matérialisée logiquement dans la table des pages • Les structures de régions dans TRP contiennent des infos sur le type, les droits d’accès et la localisation (adresses en mémoire ou adresses sur disque) de la région • Seule la zone u du processus courant est manipulable par le noyau, les autres sont inaccessibles • L’adresse de la zone u est placée dans le mot d’état du processus
Le contexte d’un processus • Le contexte d’un processus est l’ensemble des données qui permettent de reprendre l’exécution d’un processus qui a été interrompu • Il est constitué de : • son état • son mot d’état, en particulier : • la valeur des registres actifs • le compteur ordinal • les valeurs des variables globales statiques ou dynamiques • son entrée dans la TP • sa zone u • les piles utilisateurs et systèmes • les zones de code et de données • Le noyau et ses variables ne font partie du contexte d’aucun processus ! • L’exécution d’un processus se fait dans son contexte • Quand il y a changement de processus courant, il y a réalisation d’une commutation de mot d’état et d’un changement de contexte
Contexte d’unité centrale • Fonctions de très bas niveau, fondamentales pour pouvoir programmer un SE • Pour être exécuté et donner naissance à un processus, un programme et ses données doivent être chargées en mémoire centrale de l’UC • L’UC comprend des circuits logiques qui effectuent les instructions mais aussi des mémoires appelés registres : • L’accumulateur (reçoit le résultat d’une instruction) • Le registre d’instruction (contient l’instruction en cours) • Le compteur ordinal (adresse de l’instruction en mémoire) • Le registre d’adresse • Les registres de données (utilisées pour lire/écrire une donnée à une adresse spécifiée) • Les registres d’état du processeur (actif, mode (user/sys), vecteur d’interruptions, …) • Les registres d’état du processus (droits, adresses, priorités, …) • Ces registres forment le contexte d’unité centrale d’un processus
Commutation de mot d’état • Pour pouvoir exécuter un nouveau processus, il faut sauvegarder le contexte d’unité centrale du processus courant (mot d’état), puis charger le nouveau mot d’état • Cette opération est appelée commutation de mot d’état • Cette commutation doit se faire de manière non interruptible ! • Cette « super-instruction » utilise 2 adresses : • l’adresse de sauvegarde du mot d’état • l’adresse de lecteur du nouveau mot d’état • Le compteur ordinal faisant partie du mot d’état, ce changement provoque l’exécution dans le nouveau processus • Les fonctions setjmp/longjmp permettent de sauvegarder et de réinitialiser le contexte d’unité central du processus courant, en particulier le pointeur de pile
Les interruptions • Une interruption est une commutation de mot d’état provoquée par un signal produit par le matériel • Ce signal est la conséquence d’un évènement extérieur ou intérieur, il modifie l’état d’un indicateur qui est régulièrement testé par l’UC • Une fois le signal détecté, on utilise le vecteur d’interruptions pour identifier la cause et réaliser la tâche demandée • 3 grands types d’interruptions : • Externes (indépendantes du processus) : pannes, interventions de l’utilisateur • Déroutements : erreur interne du processeur (débordement, div/0, …)sauvegarde sur disque de l’image mémoire : core dumped • Appels systèmes : accès disque, demande d’E/S, … • Il existe plusieurs niveaux d’interruptions, ce qui permet au système de sélectionner l’interruption à traiter en priorité
Les interruptions sous Unix • 6 niveaux d’interruptions : • L’horloge est la plus prioritaire dans un système Unix
La cascade d’interruptions ! • Si durant le traitement d’une interruption, une autre interruption se produit et que ceci se répète durant le traitement de la nouvelle interruption : le système ne fait plus progresser le processus en cours, ni les interruptions ! • Nécessité de pouvoir retarder ou annuler une interruption 2 mécanismes : le masquage et le désarmement d’un niveau d’interruption • Masquer = ignorer temporairement un niveau d’interruption • Désarmer = rendre le positionnement de l’interruption caduque (impossible pour les déroutements)
États d’un processus(cas d’un système de gestion mémoire par swap) • Listes des états d’un processus : • :Le processus s’exécute en mode utilisateur • Le processus s’exécute en mode noyau • Le processus est prêt • Le processus est endormi en mémoire centrale • Le processus est prêt en zone swap • Le processus est endormi en zone swap • Le processus passe du mode noyau au mode utilisateur mais est préempté (bien que le processus soit prêt, il est retiré du traitement pour les autres processus progressent) • Naissance d’un processus, ce processus n’est pas encore prêt, il n’est pas encore endormi, c’est l’état initial de tous processus sauf le swappeur • Zombie : le processus vient de réaliser un exit, il apparaît uniquement dans la TP où il est conservé le temps que son père récupère le code de retour et d’autres informations de gestion (ex : tps d’exécution) • L’état « zombie » est l’état final des processus, il reste dans cet état jusqu’à ce que le père lise leur valeur de retour (status) à l’aide l’appel système wait()
Exécution en mode utilisateur Examiner et traiter les signaux Appel système interruption 1 Gestion interruption Retour au mode utilisateur 2 Exécution en mode noyau préemption Préempté 7 Ordonnancement du processus exit sleep Examinerles signaux 9 Zombie 3 Prêt en mémoire Bloqué (endormi) en mémoire Mémoiresuffisante wakeup 4 Mémoire centrale Swap Création swapin swapout fork 8 swapin Mémoireinsuffisante 5 6 Bloqué en zone swap Prêt en zone swap Diagramme d’état des processus
La TP Unix • La TP est localisée dans la mémoire du noyau • TP = Tableau de struct proc (<sys/proc.h>) • Cette structure contient les informations qui doivent toujours être accessibles par le noyau : • État (cf diagramme) • Adresse de la zone u • Adresses : taille et localisation en mémoire • pid et ppid : valeurs initialisées en état 8 • uid • Événement : descripteur de l’événement attendu lorsque le processus est endormi (bloqué) • Priorités : utilisés par l’ordonnanceur • Vecteurs d’interruptions du processus : ensemble des signaux reçus mais non traités • Divers : compteurs (ex: tps CPU pour faire payer le temps de calcul), alarme • Ces informations peuvent être obtenues à partir de la commande shell ps ou par consultation du pseudo système de fichiers /proc (et pour certaines d’entre elles, par différents appels systèmes)
La zone u • La zone u est utilisée lorsque un processus s’exécute (mode noyau ou utilisateur) • Une unique zone u est accessible à la fois : celle du processus en cours d’exécution (états 1 et 2) • La zone u est de type struct user définie dans <sys/user.h>, elle contient : • Pointeur sur la structure de processus de la TP • uid réel et effectif : détermine les privilèges donnés au processus • Compteurs de temps (user et system) • Terminal : terminal de contrôle du processus (s’il existe) • Erreur : dernière erreur rencontrée pendant un appel système • Retour : valeur de retour du dernier appel système • E/S : structures associées au E/S • Répertoires courant et racine (cf. chroot()) • Table des descripteurs • Limites (cf. ulimit en Bourne Shell et limit en csh) • umask : masque de création de fichiers • Ces informations ne sont accessibles que par une réponse du processus lui-même et donc par l’utilisation d’appels systèmes !
Les informations temporelles #include <sys/time.h> clock_t times (struct tms * buffer); • times remplit la structure pointée par buffer avec des informations sur le temps machine utilisé en état 1 et 2 Struct tms { clock_t tms_utime; /* user time */ clock_t tms_stime; /* system time */ clock_t tms_cutime; /* user time, children */ clock_t tms_csutime; /* system time, children */ }; • Contient des temps indiqués en microsecondes, la précision dépend de l’ordinateur
Changement des répertoires #include <unistd.h> int chroot(const char* path); • Permet de définir un nouveau point de départ pour les références absolues (commençant par /). • La référence .. de ce répertoire racine étant associée à lui-même, il n’est donc pas possible de sortir du sous-arbre défini par chroot • Les appels suivants permettent de changer le répertoire de travail du processus : • int chdir(const char* path); • int fchdir(int fd);
Récupération du pid #include <unistd.h> pid_t getpid(void); pid_t getppid(void); pid_t getpgrp(void); pid_t getpgrp2(pid_t pid); • L’appel à getpid retourne le pid du processus courant • getppid, le pid du processus père • getpgrp, le pid du groupe du processus courant • getpgrp2, le pid du groupe du processus pid(si pid=0, alors identique à getpgrp)
Identification de l’utilisateur • L’uid d’un processus est l’identification de l’utilisateur exécutant le processus • Le système utilise 3 uid : • euid : uid effective utilisé pour les tests d’accès • ruid : uid réelle • suid : uid sauvegardée, pour pouvoir revenir en arrière • #include <sys/types.h>#include <unistd.h>int setuid(uid_t uid) • Fonctionnement : • si euid=0 (root) alors les trois uid sont positionés à la valeur de uid, • sinon si uid=ruid ou suid alors euid prend la valeur uid (ruid et suid ne changent pas), • sinon RIEN ! (pas de changements) • La commande setreuid permet de changer le ruid, elle est utilisée pendant le login, seul le super utilisateur peut l’exécuter avec succès • Pour les goupes : int setgid(gid_t gid)
Tailles limites d’un processus #include <unistd.h> long ulimit(int cmd, …); • La commande cmd est : • UL_GETFIZE : retourne la taille maximum des fichiers en blocs • UL_SETFSIZE : positionne cette valeur avec le deuxième argument • UL_GETMAXBRK : valeur maximale pour l’appel d’allocation dynamique de mémoire : brk. • Ces valeurs sont héritées du processus père • Remarque : cet appel système n’est pas implémenté dans les noyaux 2.0 des systèmes Linux
Manipulation de la taille du segment de données #include <unistd.h> int brk(void* endds); void* sbrk(ptrdiff_t incr); • brk positionne la fin du segment de données à l’adresse spécifiée • endds doit être supérieur à la fin du segment de texte et 16ko avant la fin de la pile • sbrk incrémente l’espace de données du programme de incr octets • sbrk n’est pas un appel système, juste une fonction de la bibliothèque C • Remarques : • brk et sbrk ne sont pas définis dans le C standard et sont volontairement exclus des standards POSIX • Il ne faut pas les utiliser conjointement aux fonctions d’allocation standard : malloc, calloc, realloc, free
Manipulation de la valeur de nice • La valeur de nice d’un processus indique la priorité du processus pour l’ordonnancement • Plus la valeur est petite, plus le processus est prioritaire • La valeur de de nice est comprise entre 0 et 39 • Seul le super utilisateur peut spécifier une valeur de nice négative #include <unistd.h> int nice(int valeur); • La commande shell renice permet de changer le « nice » d’un processus actif
Manipulation de la valeur umask • L’appel umask permet de spécifier quels droits doivent être interdits en cas de création de fichiers #include <unistd.h> int umask(int mask); • umask fixe le masque de création de fichiers à la valeur :mask & 0777 • Les bits contenus dans le masque sont éliminés de la valeur 0666 pour créer les nouvelles permissionsex : mask=0022 0666 & ~0022 = 0644 = rw-r--r-- • Cet appel système n’échoue jamais et la valeur précédente du masque est renvoyée • La commande umask existe également en shell
L’appel système fork • L’appel système fork permet la création d’un processus clône du processus courant. #include <unistd.h> pid_t fork(void); • DEUX valeurs de retour en cas de succès ! • Dans le processus père, la valeur de retour est égale au pid du fils • Dans le processus fils, la valeur de retour est égale à zéro • Si échec (seul le processus père existe), la valeur de retour est égale à –1 • Les pid et ppid sont les seules informations différentes entre les deux processus • Les deux processus poursuivent leur exécution à l’instruction qui suit le fork • Utilisation courante : pid_t pid; pid=fork(); if (pid>0) {/* processus père */ } else if (pid==0) {/*processus fils*/} else {/* Traitement d’erreur */}
Les appels systèmes exec (1) #include <unistd.h> int execl(char* path,char* arg0,char* arg1, …, NULL); int execv(char* path,char* arg[]); int execle(char* path,char* arg0,…, NULL, char* envp[]); int execve(char* path,char* arg[],char* envp[]); int execlp(char* file,char* arg0,char* arg1, …, NULL); int execvp(char* file,char* arg[]); • Toutes ces fonctions sont « identiques » et diffèrent simplement par le mode de passage des paramètres • Les différents noms des fonctions exec sont mnémoniques : • l : liste d’arguments • v : arguments sous forme de vecteur • p : recherche du fichier avec la variable d’environnement PATH • e : transmission d’un environnement en dernier paramètre, en remplacement de l’environnement courant
Les appels systèmes exec (2) • La famille des fonctions exec remplace l’image mémoire du processus en cours par un nouveau processus. • Informations conservées par le processus : pid, ppid, pgid, ruid, suid, euid, nice, groupe d’accès, répertoires courant et racine, terminal de contrôle, utilisation et limites des ressources, umask, masques des signaux, signaux en attente, table des descripteurs de fichiers, verrous, session • Valeur de retour : en cas de succès AUCUNEle code ayant fait l’exec n’existe plus, en cas d’échec –1
Lancement d’une commande #include <stdlib.h> int system(const char * commande); • Crée un nouveau processus « /bin/sh » qui exécute la commande (/bin/sh –c commande) • Le processus appelant cette fonction reste bloqué jusqu’à la fin de l’exécution du processus • Ce mécanisme est très coûteux et n’est pas un appel système • La valeur de retour est 127 si l’appel système execve pour /bin/sh échoue, -1 si une autre erreur, ou le code de retour de la commande sinon
Terminaison d’un processus • _exit : primitive de terminaison de processus bas niveau #include <unistd.h> void _exit(int valeur); • elle ferme les descripteurs ouverts par open, opendir ou hérités du processus père • la valeur est fournie au processus père qui la récupère après l’appel système wait • cette valeur est le code de retour de processus en shell • cette primitive est automatiquement appelée à la fin de la fonction main (sauf en cas d’appels récursifs de main) • Exit : fonction de terminaison de processus de stdlib #include <stdlib.h> void exit(int valeur); • elle lance les fonctions définies par atexit • ferme l’ensemble des descripteurs ouverts grâce à la bibliothèque standard (fopen) • détruit les fichiers fabriqués par la primitive tmpfile • appelle _exit avec valeur
Console shell $ gcc –o atexit atexit.c $ atexit $ atexit unargument cuicui coucou $ La primitive atexit • Cette primitive (non système) permet de spécifier des fonctions à appeler en fin d’exécution, elles sont lancées par exit dans l’ordre inverse de leur positionnement par atexit #include <stdlib.h> void atexit(void (*fonction) (void)); • Exemple : void bob(void){printf(“coucou\n”);} void bib(void){printf(“cuicui ”);} main(int argc) { atexit(bob);atexit(bib); if (argc – 1) exit(0); else _exit(O); }
Attente de la mort d’un fils #include <sys/types.h> #include <sys/wait.h> pid_t wait (int* status) pid_t waitpid(pid_t pid, int* status, int options) • La fonction wait suspend l’exécution du processus courant jusqu’à ce qu’un fils se termine (ou jusqu’à réception d’un signal) • Si un processus fils est déjà mort (il est zombie) la fonction revient immédiatement • Toutes les ressources utilisées par le fils sont libérées • La fonction waitpid suspend l’exécution du processus courant jusqu’à ce que le processus fils de numéro pid se termine (ou jusqu’à réception d’un signal) • La valeur de pid peut être : • < -1 : attente la fin de n’importe quel processus fils appartenant au groupe d’ID pid • -1 : attendre n’importe quel fils (identique au wait) • 0 : attendre la fin de n’importe quel processus fils du même groupe appelant • > 0 : attendre la fin du processus de numéro pid • options, la plus utile : WNOHANG qui permet de ne pas bloquer si aucun fils n’est mort • Si status NULL, les informations sur la terminaison du fils y sont stockées • WIFEXITED(status) : est non nul si le fils s’est terminé normalement • WEXITSTATUS(status) : donne le code retour du fils tel qu’il l’a mentionné lors de l’appel _exit(retour)[valide uniquement si WIFEXITED(status)0]
Gestion des erreurs • Dès lors que l’on effectue des appels systèmes, il est important de contrôler TOUTES les valeurs de retour • Nous sommes aidés dans cette tâche par la bibliothèque C <errno.h> #include <errno.h> perror(const char *s) • La fonction perror affiche un message sur la sortie standard des erreurs, décrivant la dernière erreur rencontrée durant un appel système • La chaîne s est imprimée en premier suivie d’un : et du message d’erreur approprié • Le numéro de l’erreur est obtenu à partir de la variable externe errno qui contient le code d’erreur lorsqu’un problème survient, mais qui n’est PAS effacé lorsqu’un appel réussi