Fonctions système Linux de gestion des processus

Complément de cours

Fonctions

fork()
kill()
killpg()
pause()
wait()
wait3()
wait4()
waitpid()

execve()

getpid()
getppid()
getuid()
geteuid()
getgid()
getegid()

setuid()
setgid()

getpgid()
setpgid()


fork()

Nom
fork - créer un processus fils
Syntaxe
 
#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 :

     Les fichiers ouverts par le père sont partagés par le fils. Celui-ci reçoit une copie de la table des descripteurs des fichiers : s'il ferme le fichier, celui-ci est encore accessible par le processus père. En revanche, ils partagent le même pointeur de fichier.

Quelques éléments ne sont pas hérités par le processus fils :

    Tous les processus existant à un instant donné sous un système Unix ont été créés par un appel de la fonction fork(), que ce soit par l'utilisateur dans son programme, par le shell (qui crée un processus pour exécuter chaque commande qu'il reçoit) ou par le système lui-même lors de son démarrage.

    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    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

Diagnostic d'erreur

    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()

Nom
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()

Nom
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()

Nom
getuid : renvoie le numéro du propriétaire réel du processus
Syntaxe
 
#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()

Nom
geteuid - renvoie le numéro du propriétaire effectif du processus
Syntaxe
 
#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()

Nom
getgid - renvoie le numéro du groupe du propriétaire réel du processus
Syntaxe
 
#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()

Nom
getegid - renvoie le numéro du groupe du propriétaire effectif du processus
Syntaxe
 
#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()

Nom
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 :

Valeur retournée

    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()

Nom
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()

Nom
getpgid - renvoie le numéro de groupe de processus
Syntaxe
 
#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()

Nom
setpgid - modifie le numéro de groupe de processus
Syntaxe
 
#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()

Nom
kill [1]   – envoie un signal à un processus
Syntaxe
 
#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()

Nom
killpg - envoie un signal à un groupe de processus
Syntaxe
 
#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()

Nom
pause - suspend le processus pendant une durée indéterminée
Syntaxe
 
#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()

Nom
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 :

    En revanche, les caractéristiques suivantes sont modifiées : Remarque : lorsque le flag d'un fichier est positionné en "fermeture en exec", le fichier est refermé automatiquement avant le lancement de la commande. Pour positionner ce flag, utiliser la fonction fcntl() avec la requête F_SETFD et l'argument FD_CLOEXEC (fichier <fcntl.h>). La requête F_GETFD permet de savoir si ce flag est positionné.

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 :

    C'est ce tableau qui est directement passé comme paramètre argv de la fonction main() du programme lancé.

    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()

Nom
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);
pid_t wait3   (int * status, int options, struct rusage * rusage);
pid_t wait4   (pid_t pid, int * status, int options, 
               struct rusage * rusage);
pid_t waitpid (pid_t pid, int * status, int options);

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>).
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).
Description

    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.


figure 2

    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 :

Remarques :

    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) :
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.
    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".

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

Diagnostic d'erreur

    Les fonctions wait(), wait3(), wait4() et waitpid() renvoient -1 et la variable globale errno est positionnée. Parmi les cas possibles :




[1] voir aussi killpg()
[2] voir le cours "Système de Gestion des Fichiers" sous Linux : "Les démons de VFS"
[3] dans certaines implémentations d'Unix, la fonction pause() renvoie le numéro du signal reçu et n'a aucun diagnostic d'erreur.