Fonctions
fork()
kill()
killpg()
pause()
wait()
wait3()
wait4()
waitpid()
fork - créer un processus filsSyntaxe
#include <unistd.h> // contient le
prototype de fork()
pid_t fork (void); |
Description
La fonction fork() crée un processus (appelé processus fils) qui est une "copie" pratiquement exacte du processus qui a appelé la fonction fork() (appelé processus père). Il y a aussi création d'un nouveau bloc de contrôle de processus (PCB : Process Control Block)
Après l'exécution de la fonction fork(), le père et le fils partagent le même code (réentrance), et le fils reçoit une copie des segments de données utilisateur et des segments des données système (qui sont dupliqués, ce point sera précisé lors du cours système "gestion mémoire"). On dit que le processus fils hérite de la presque totalité du contexte de son père. Il hérite du père :
Quelques éléments ne sont pas hérités par le processus fils :
La commande ps permet de connaître les processus en cours, ainsi que de nombreuses informations les concernant :
duo/users/prof/mathieu/> ps –Al | less
F S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD 100 S 0 1 0 0 60 0 - 288 load_e ? 00:00:02 init 040 S 0 2 1 0 60 0 - 0 do_mkn ? 00:00:00 kflushd [2] 040 S 0 3 1 0 60 0 - 0 get_fr ? 00:00:00 kpiod 040 S 0 4 1 0 60 0 - 0 unregi ? 00:00:00 kswapd 140 S 1 429 1 0 79 0 - 287 load_e ? 00:00:02 portmap |
Seul le processus 0, le premier créé, n'a pas de père et ne peut donc être créé de cette façon. Le processus 1, init, est immédiatement rattaché au processus 0. On peut considérer que c'est lui qui lance effectivement le système et que c'est l'ancêtre de tous les autres processus.
Démon
On appelle démon (daemon en anglais) un processus tournant en background et rattaché au processus init. En réalité, il faut le considérer plutôt comme un "ange" ou un "bienheureux", compte tenu d'une part de sa proximité de l'origine de tout, d'autre part de son caractère bénéfique : un daemon n'est pas maléfique !...
Si le père se termine avant le fils, celui-ci est directement rattaché au processus 1 (init). C'est donc la technique qui doit être utilisée pour créer un démon.
Valeur retournée
La fonction fork() renvoie
En cas d'erreur, la fonction fork() renvoie -1 et le code de l'erreur est dans la variable errno. Le seul cas d'erreur possible correspond à une impossibilité de créer un processus. L'utilisateur n'en est évidemment pas responsable. Les causes peuvent être soit un trop grand nombre de processus existants au total ou pour l'utilisateur (table des processus pleine), soit une place insuffisante sur disque pour la zone de swap.
getpid - renvoie le pid du processus qui l'appelle.Syntaxe
#include <unistd.h> // permet d'accéder au prototype
de getpid()
// et au type pid_t pid_t getpid (void); |
Description
La fonction getpid() renvoie le pid du processus qui l'appelle.
Valeur retournée
La fonction getpid() renvoie le pid du processus qui l'appelle.
Diagnostic d'erreur
Aucun
getppid - renvoie le pid du père du processus qui l'appelle.Syntaxe
#include <unistd.h> // permet d'accéder au prototype
de getppid()
// et au type pid_t pid_t getppid (void); |
Description
La fonction getppid() renvoie le pid du père du processus qui l'appelle.
Valeur retournée
La fonction getppid() renvoie le pid du père du processus qui l'appelle.
Diagnostic d'erreur
Aucun.
getuid : renvoie le numéro du propriétaire réel du processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype
de getuid()
// et au type uid_t uid_t getuid (void); |
Description
La fonction getuid() renvoie le numéro du propriétaire réel du processus. C'est le numéro de l'utilisateur qui a lancé le processus.
Valeur retournée
La fonction getuid() renvoie le numéro du propriétaire réel du processus
Diagnostic d'erreur
Il n'y a pas d'erreur possible.
geteuid - renvoie le numéro du propriétaire effectif du processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype
de geteuid()
// et au type uid_t uid_t geteuid (void); |
Description
La fonction geteuid() renvoie le numéro du propriétaire effectif du processus. En général, le propriétaire réel est aussi le propriétaire effectif. Le processus est donc exécuté avec les droits du propriétaire réel. Cependant, si le bit set-uid du fichier exécuté est positionné, le propriétaire effectif est le propriétaire du fichier exécutable. Dans tous les cas, les droits du processus sont ceux du propriétaire effectif. Ce dispositif est nécessaire pour permettre de conférer provisoirement à un utilisateur des droits étendus. C'est ainsi que le bit set-uid du fichier exécutable /usr/bin/passwd est positionné. Quand un utilisateur lance ce programme, il hérite du droit de modifier le fichier /etc/passwd, ce qu'il ne peut pas faire avec ses simples droits (il faut être super-utilisateur).
Valeur retournée
La fonction geteuid() renvoie le numéro du propriétaire effectif du processus.
Diagnostic d'erreur
Il n'y a pas d'erreur possible.
getgid - renvoie le numéro du groupe du propriétaire réel du processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype
de getgid()
// et au type gid_t gid_t getgid (void); |
Description
La fonction getgid() renvoie le numéro du groupe du propriétaire réel du processus.
Valeur retournée
La fonction getgid() renvoie le numéro du groupe du propriétaire réel du processus
Diagnostic d'erreur
Il n'y a pas d'erreur possible.
getegid - renvoie le numéro du groupe du propriétaire effectif du processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype
de getegid()
// et au type gid_t gid_t getegid (void); |
Description
La fonction getegid() renvoie le numéro du groupe du propriétaire effectif du processus.
Valeur retournée
La fonction getegid() renvoie le numéro du groupe du propriétaire effectif du processus. En général, le groupe du propriétaire réel et le groupe du propriétaire effectif ne font qu'un sauf si le bit set-guid est positionné.
Diagnostic d'erreur
Il n'y a pas d'erreur possible.
setuid - modifie les numéros du propriétaire réel et effectif du processus.Syntaxe
#include <unistd.h> // permet d'accéder au prototype
de setuid()
// et au type uid_t int setuid (uid_t id); |
Description
La fonction setuid() donne aux deux numéros de propriétaire, réel et effectif, la valeur id. Il faut cependant pour cela :
La fonction setuid() renvoie 0 en cas de succès.
Diagnostic d'erreur
En cas d'erreur, la fonction setuid() renvoie -1 et la variable globale errno est positionnée.
setgid - modifie les numéros du groupe réel et effectif du processus.Syntaxe
#include <unistd.h> // permet d'accéder au prototype
de setgid()
// et au type uid_t int setgid (gid_t id); |
Description
La fonction setgid() a sur les numéros de groupe de propriétaires le même rôle que la fonction setuid() sur les numéros de propriétaires.
Valeur retournée
La fonction setgid() renvoie 0 en cas de succès.
Diagnostic d'erreur
En cas d'erreur, la fonction setgid() renvoie -1 et la variable globale errno est positionnée.
getpgid - renvoie le numéro de groupe de processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype de getpgid()
// et au type pid_t pid_t getpgid (pid_t pid); |
Description
La fonction getpgid() renvoie le numéro du groupe de processus auquel appartient le processus identifié par pid. Si pid est nul, la fonction renvoie le numéro du groupe du processus qui appelle la fonction getpgid().
Valeur retournée
La fonction getpgid() renvoie le numéro du groupe de processus en cas de succès.
Diagnostic d'erreur
En cas d'erreur, la fonction getpgid() renvoie -1 et la variable globale errno est positionnée.
setpgid - modifie le numéro de groupe de processusSyntaxe
#include <unistd.h> // permet d'accéder au prototype de setpgid()
// et au type pid_t int setpgid (pid_t pid, pid_t pgid); |
Description
La fonction setpgid() affecte le numéro du groupe de processus pgid au processus identifié par pid. Si pid est nul, la fonction affecte le numéro du groupe au processus qui appelle la fonction setpgid(). Si pgid est nul, la fonction utilise le pid du processus qui appelle la fonction setpgid() comme numéro du groupe de processus.
Valeur retournée
La fonction setpgid() renvoie 0 en cas de succès.
Diagnostic d'erreur
En cas d'erreur, la fonction setpgid() renvoie -1 et la variable globale errno est positionnée.
kill [1] – envoie un signal à un processusSyntaxe
#include <signal.h> // contient le prototype de kill()
// et la définition de pid_t int kill (pid_t pid, int sig); |
Description
La fonction kill() envoie le signal sig au processus identifié par pid.
Valeur retournée
La fonction kill() renvoie 0 si l'opération s'est bien déroulée.
Diagnostic d'erreur
En cas d'erreur (pid inaccessible ou sig inexistant), la fonction kill() renvoie -1 et la variable globale errno est positionnée.
killpg - envoie un signal à un groupe de processusSyntaxe
#include <signal.h> // contient le prototype de killpg()
int killpg (int pgrp, int sig); |
Description
La fonction killpg() envoie le signal sig au groupe de processus identifié par pgrp. Le processus qui appelle la fonction killpg() doit avoir le même UID effectif que le groupe ou être super-utilisateur. Si pgrp est nul, le signal est envoyé à tout le groupe auquel appartient le processus qui appelle la fonction killpg().
Valeur retournée
La fonction killpg() renvoie 0 si l'opération s'est bien déroulée.
Diagnostic d'erreur
En cas d'erreur (groupe inexistant ou sig invalide), la fonction killpg() renvoie -1 et la variable globale errno est positionnée.
pause - suspend le processus pendant une durée indéterminéeSyntaxe
#include <unistd.h> // contient le prototype de la fonction pause();
int pause (); |
Description
La fonction pause() suspend l'exécution du processus jusqu'à réception d'un signal non ignoré quel qu'il soit.
Valeur retournée
La fonction pause() renvoie toujours -1 [1].
Diagnostic d'erreur
La variable errno est toujours positionnée à EINTR.
execve - exécute un programme (voir Comparaison des fonctions de la famille exec..()).Syntaxe
#include <unistd.h> // permet d'atteindre le prototype de execve()
int execve (const char * file, char * const argv[], char * const envp []); |
Description
La fonction execve() recouvre la pile d'exécution, les segments du code et des données du processus en cours d'exécution par ceux du programme désigné par file.
Les caractéristiques suivantes sont inchangées entre l'ancien et le nouveau processus :
Paramètres
Le premier paramètre file doit soit désigner un programme exécutable dans le répertoire courant (ou un script), soit être le chemin d'accès à un fichier exécutable (ou à un script).
Le paramètre argv est un tableau de pointeurs vers des chaînes de caractères C (NTCTS) constantes :
Le paramètre envp est un tableau de pointeurs vers des chaînes de caractères C (NTCTS) constantes, terminé par un pointeur nul, représentant la liste des
variables d'environnement
du programme à exécuter.
Chaque ligne a la forme :
nom=valeur |
par exemple :
HOME=/usr/etud/taralf |
Valeur retournée
En cas d'appel réussi, la fonction execve() ne peut renvoyer de valeur au programme en cours puisque son code n'existe plus et a été remplacé par le nouveau code.
Diagnostic d'erreur
En cas d'erreur, la fonction execve() renvoie la valeur -1 et la variable globale errno est positionnée.
Voir aussi
Linux propose plusieurs fonctions C wrappers qui simplifient l'utilisation de la fonction système execve() : execl(), execle(), execlp(), execv() et execvp().
wait, wait3, wait4, waitpid – attendent la fin d'un processus fils s'il existe.Syntaxe
#include <sys/wait.h> // contient les prototypes des fonctions
#include <sys/resource.h> // struct rusage pid_t wait (int * status);
|
Paramètres
status : pointe sur une zone qui contient l'état de terminaison du processus fils, (comme défini dans le fichier <sys/wait.h>).Description
options : modifie le comportement de la fonction. Les seules valeurs du paramètre options qui seront utilisées ici sont WNOHANG et WUNTRACED
rusage : pointe sur une structure de type struct rusage qui contient des informations concernant les processus fils qui se sont terminés. Cette structure n'est pas remplie si la valeur du pointeur est nulle.
pid : numéro du processus dont le père attend la mort (les valeurs de pid –1, 0 et –n ont une signification particulière : cf. le man).
Les fonctions wait() et wait3() attendent la fin d'un processus fils qui provoque la réception du signal SIGCHLD. Si aucun fils n'existe au moment de l'appel, les fonctions ne sont pas bloquantes. De plus l'utilisateur peut savoir les conditions dans lesquelles le fils s'est terminé grâce au paramètre résultat status.
Lorsque le processus fils se termine par l'appel à la fonction exit(m) (ou return m) le signal SIGCHLD est renvoyé au père en même temps que la valeur de m qui est placée dans l'octet de gauche du paramètre pointé par status tandis que l'octet de droite est mis à zéro (figure 2). Attention : si les entiers de type int sont codés sur plus de 2 octets, seuls les deux octets de faible poids ont un sens.
Lorsque le processus fils se termine lui-même par la réception d'un signal, le signal SIGCHLD est renvoyé au père en même temps que la valeur du signal qui a interrompu le fils est placée dans les 7 bits de poids faible de l'octet de droite tandis que le bit 7 est positionné si un fichier core a été créé.
Cas particulier : si le fils a été stoppé (mais pas tué) par un signal SIGSTOP ou SIGTSTP, et à condition que la fonction wait3() (ou waitpid() ou wait4()) ait été appelée avec l'option WUNTRACED, alors les 7 bits de poids faible de l'octet de droite sont mis à 1 et l'octet de gauche contient le numéro du signal :
Il est donc important d'utiliser correctement la fonction exit(m) (ou return m) dans tout programme, afin que son processus père (souvent le shell) soit exactement informé des conditions de terminaison du processus fils. C'est en particulier indispensable pour exécuter une commande qui enchaîne plusieurs traitements.
En cas de retour des fonctions par terminaison d'un processus fils, la valeur pointée par status ne peut être nulle que dans les deux cas suivants :
Un processus fils qui se termine disparaît presque complètement : la plupart de ses ressources sont libérées (segments de code et des données, fermeture des fichiers, etc.). Cependant, il ne disparaît pas de la table des processus (il est alors appelé processus zombie). C'est seulement après que le père a pris connaissance de sa mort (par l'appel à l'une des fonctions wait()) que le fils est complètement éliminé. Si le fils se termine avant l'appel de la fonction wait() par le père, les informations ne sont pas perdues et le père en prendra connaissance lors de l'appel de la fonction. Il ne sera alors pas bloqué.
Même en état d'attente, un processus est normalement interrompu par tout signal non préalablement détourné (sauf pour le signal SIGCHLD). Le signal SIGCHLD peut lui-même être détourné. Si tel est le cas, la fonction wait() est interrompue (errno == EINTR) et renvoie –1, le paramètre status étant dénué de sens. Pour pouvoir à la fois détourner le signal SIGCHLD et récupérer normalement les indications concernant le fils, il faut commencer par positionner l'option SA_RESTART lors du déroutement de SIGCHLD par la fonction sigaction(). Ainsi, après exécution de la fonction de traitement du signal, le système relance automatiquement la fonction wait() qui ressort cette fois de façon normale.
Différentes macro-instructions peuvent être utilisées pour extraire les informations de la valeur pointée par status :
WIFEXITED(status) :Si les fonctions wait3(), wait4() ou waitpid() ont été appelées avec l'option WNOHANG, elles ne sont pas bloquantes si aucun processus fils n'est terminé. Elles renvoient la valeur 0 qui est sans ambiguïté. La fonction wait3() est très souvent utilisée pour purger les processus "zombies".vrai si le processus dont le status est récupéré s'est terminé normalement (par retour de la fonction main() ou par appel aux fonctions exit() ou _exit()).WEXITSTATUS(status) :renvoie la valeur passée en retour de la fonction main() ou en paramètre effectif des fonctions exit() ou _exit() (isole les 8 bits de fort poids de la valeur pointée par status), sinon renvoie -1. Ne peut être évalué que si WIFEXITED(status) est vrai.WIFSIGNALED (status) :vrai si le processus dont le status est récupéré s'est terminé par la réception d'un signal non détourné.WTERMSIG(status) :renvoie la valeur du signal qui a provoqué la fin du fils, sinon renvoie -1. Ne peut être évalué que si WIFSIGNALED(status) est vrai.WIFSTOPPED(status) :vrai si le processus dont le status est récupéré est stoppé. Ne marche que si l'option WUNTRACED est positionnée.WSTOPSIG(status) :renvoie le numéro du signal qui a stoppé le processus. Ne peut être évalué que si WIFSTOPPED(status) est vrai.
Valeur retournée
Les fonctions wait(), wait3(), wait4() et waitpid() renvoient le numéro de pid d'un ou du fils terminé. S'il y en a plusieurs, l'un d'eux est choisi par le scheduler de façon quelconque. Si aucun fils n'existe, la valeur -1 est renvoyée
Les fonctions wait(), wait3(), wait4() et waitpid() renvoient -1 et la variable globale errno est positionnée. Parmi les cas possibles :