Gérer les processus
Lancer un nouveau processus (remplace le processus courant !)
La fonction exec que je vous présente permet de lancer un nouveau processus, mais ...
Cette fonction
va transformer le processus appelant en un nouveau processus. L’appel
exec écrase le programme par les nouvelles instructions. Ce nouveau programme
sera toujours affilié au père (En fait, se sont les segments qui
seront redirigés sur le nouveau programme).
Pour ne pas écraser
le processus courant, il faudra utiliser l'instruction fork
qui permet de faire un double en mémoire du processus courant. Ce double
devenant un processus fils de celui d'origine et héritant d'informations,
... du père.
Après un exec, le nombre de processus n’est pas modifié.
Les signaux positionnés pour être interceptés mettront fin
aux programmes appelés.
Les données de l'ancien programme sont accessibles seulement pour la
copie des arguments.
Certaines caractéristiques du processus écrasé sont conservées:
- priorité du processus.
- GPID.
- les fichiers restent ouverts.
- sémaphores.
- identificateur du groupe tty.
- drapeau de traçage.
- temps restant jusqu'à un signal d'alarme.
- répertoire de travail et répertoire racine.
- masque de création des droits d'accès aux fichiers.
- limite de taille fichier.
- les temps du processus.
- les verrous sur fichiers.
La commande exec se décline en plusieurs fonctions. L'ensemble de ces fonctions exec autorisent le remplacement du code exécutable courant par un programme et des arguments qui seront précisés en paramêtres.
int execl (char *path , char *arg0,... ,argn-1, (char *)NULL)
int execlp (char *file, char *arg0, ..., argn-1, (char *)NULL)
int execle (char *path, char *arg0,..., argn-1, (char *)NULL,char ** envp)
int execv (char *path , char **argv)
int execvp (char *file, char **argv)
int execve (char *path, char **argv, char **envp)
En entrée:
path pointe sur le chemin complet du fichier exécutable (y compris le nom du fichier exécutable).
file pointe sur le nom du fichier exécutable recherché grâce à PATH.
arg0 pointe sur le chemin ou le fichier exécutable.
arg1..n-1 pointe sur des arguments passés au programme
argn Un pointeur NULL termine la liste des arguments. Il est conseillé de mettre aujourd'hui: (char *)NULL comme dernier argument plutôt que 0 ou (void*) 0.
argv est un tableau de pointeurs dont le premier pointe sur le chemin ou le
fichier exécutable. 1 à n-1 sur des arguments passés au
programme, et le nième qui est un pointeur NULL pour terminer la liste
des arguments.
envp est un tableau de pointeurs sur une liste de variables d'environnement.
Un pointeur NULL termine la liste des variables d'environnement.
En sortie:
En cas d'erreur exec, retourne - 1 et l'erreur est indiquée dans errno.
Créer un processus fils
La commande fork() crée un double du programme en cours
d’exécution. Ce programme aura son propre handle et sera l’image
exacte du père (identique à 100% ) avant l’exécution
du fork.
Le programme fils ne s’exécutera pas au début du processus,
mais juste après la commande fork. Les fichiers ouverts dans le programme
père avant la commande fork seront connus et accessibles par le fils
(héritage).
Remarque : seul le processus 0, qui est un processus créé lors du boot n’est pas généré par un fork.
Le noyau lors d’un fork :
- Alloue 1 élément de la table des processus au nouveau processus.
- Affecte un numéro identificateur unique au fils
- Copie logique du contexte du père
- Le niveau de filiation n’est pas limité
- Il incrémente les compteurs pour tous les fichiers associer à
la table des fichiers et la table des inodes. (cf. La table des fichiers).
- Retourne le numéro d’identificateur au père et 0 au fils
(pas d’erreur).#include <unistd.h>
pid_t fork(void);
En sortie:
fork retourne le handle d’un processus fils dans le programme père et 0 dans le programme fils. C’est ce qui permettra de différencier les deux programmes.
En cas d'échec -1 est renvoyé dans le contexte du parent, aucun
processus fils n'est créé, et errno
contient le code d'erreur ENOMEM
ou EAGAIN.
Le
père est censé être à l'écoute (wait()) des
signaux de son fils, dont celui qui indique la mort de ce fils. Sans cette
écoute, le fils passe et reste dans un état zombie. Càd qu'il a bien libéré
toutes ses ressources, mais il reste mappé jusqu'à ce que son père traite le
signal ou meurt.
Terminaison d’un processus
La fonction exit() permet la terminaison d'un processus et le passage du processus en mode zombie.
- Les fichiers sont fermés.
- La mémoire partagée est détachée.
- Les sémaphores sont détachés et eventuellement corrigés.
- Les verrous sur processus sont levés.
- Le processus est enregistré dans le fichier d'audit (acct).
- Si PPID=PID=PPID du tty alors un signal SIGHUP est envoyé aux autres
processus ayant le même PPID.
- Un signal SIGCLD est envoyé au processus pêre.(mort d'un fils)
- La zone u du processus est libérée.
- Le processus est zombie (defunct) si le pêre n'a pas pris en compte
la mort du fils ou si le pêre ignore le signal SIGCLD.
void exit ( int status)
En entrée:
status dont la valeur sera renvoyée au processus pêre qui le récupère avec wait() (voir chapitre sur les signaux).
Consultation et modification des caractéristisques d’un processus
int getpid() permet d'obtenir l'identifiant du processus courant.
int getppid() retourne l'identifiant du processus père
du processus courant.
int getpgrp() retourne l'identifiant du groupe du processus
courant.
int setpgrp() l'identifiant du groupe du processus courant
est initialisé à l'identifiant du processus. (création
d'un groupe)
int getuid() retourne le numéro de propriétaire
réel du processus.
int geteuid() retourne le numéro du propriétaire
effectif du processus.
int getgid() retourne le numéro de groupe propriétaire
réel du processus.
int getegid() retourne le numéro de groupe propriétaire
effectif du processus.
L'identificateur d'utilisateur réel identifie l'utilisateur responsable
du processus en exécution. L'identificateur d'utilisateur effectif permet
la vérification des permissions pour les accès aux fichiers, et
pour l'émission de signaux.
Si le setuid bit d'un fichier est positionné l'utilisateur effectif est
le propriétaire du fichier, l'opération peut être obtenu
aussi avec la fonction setuid(int uid) où uid est le propriétaire
du fichier ou l'utilisateur réel.
Dans le cas où le fichier appartient au super utilisateur, uid est utilisateur
réel et effectif.
Exemples de programme en C
But : Vérifier qu'une commande exec remplace bien le processus. La commande execlp() va lancer "ls -la". Un message placé après la commande execlp() ne s'affichera jamais !
But : Utilisation de la commande fork(). Les processus vont alors déterminer qui est le programme père et le programme fils. Chacun retournant des informations: Le père retourne le n° PID du fils. Le fils affichera son PID et aussi le PID du père.
But: Retour sur le premier programme, mais en utilisant la commande fork(). Le fils, ainsi créé, va lancer un execlp sur "ls -la". Le processus père attendra la fin du fils pour afficher un message.
Un autre exemple est disponible dans le chapitre tube anonyme pour synchroniser des processus avec la commande wait().