1
Systèmes d’exploitation
Chap. 3
Les processus
2
Plan
Partie 1 : Les processus et le shell Partie 2 : Les primitives
Partie 3 : processus zombis et synchronisation
Chap. 3 – Les processus
3
Partie 1
Les processus et le Shell Unix
Chap. 3 – Les processus
4
1. Introduction aux processus
Un « programme » est une suite d’instruction que le système doit faire accomplir au processeur pour résoudre un problème
particulier.
Un « processus » correspond au déroulement (l’exécution) d’un programme par le système dans un environnement particulier.
programme
processus processus
processus processus
processus
Chap. 3 – Les processus
5
1. Introduction aux processus
• Un processus ne peut exécuter que ses instructions propres et ce de façon séquentielle; il ne peut pas exécuter des instructions
appartenant à un autre processus.
• Les processus sous UNIX communiquent entre eux et avec le reste du monde grâce aux appels système.
La table des processus est interne au noyau
Chap. 3 – Les processus
6
1. Introduction aux processus
• Un processus peut s’exécuter dans deux modes différents:
1. En mode « utilisateur » : le processus exécute des instructions du programme et accède aux données de son espace d’adressage.
2. En mode « noyau » : le processus exécute des instructions du noyau et a accès à l’ensemble des données du système (par exemple lors des appels systèmes)
Chap. 3 – Les processus
7
1. Introduction aux processus
• Chaque processus possède un espace d’adressage de données propres, plusieurs processus peuvent partager le même programme.
Chap. 3 – Les processus
Segment de pile Segment
de données Segment
de code
Mémoire vive
\x00FF
8
1. Introduction aux processus
• Tout processus peut crée de nouveaux processus (Père et fils)
• Tout processus (sauf le premier, init, id =1) est créé par appel à la primitive
« fork »
• La primitive « fork » a pour effet de dupliquer le processus appelant
• Les processus sont organisés en arborescence en fonction de leur processus créateur appelé « père »
• Le noyau a en charge la gestion des différents processus et le partage des ressources entre eux, en particulier « l ’ordonnancement » des processus:
choisir parmi les processus en attente celui qui va être activé
Chap. 3 – Les processus
9
2. Caractéristiques des processus
• Il possède un identifiant : « pid »
• Il possède l’identifiant du processus père : « ppid »
• Il possède les liens avec les utilisateurs : Propriétaire du processus
Groupe du processus
Remarque : Un processus ayant des droits privilégiés peut modifier ses propriétés et groupes (ex: procédure utilisée à la connexion d’un utilisateur)
Chap. 3 – Les processus
10
2. Caractéristiques des processus
• Il possède un répertoire de travail
• Le groupe de processus et la session à laquelle les processus appartiennent
• La date de création du processus
• Les temps CPU consommés par le processus en mode utilisateur et noyau ainsi que ses fils terminés
• Le masque de création des fichiers
• La table des descripteurs de fichiers (cf. transparent suivant)
• L’état du processus
• Etc..
Chap. 3 – Les processus
11
2. Caractéristiques des processus
Processus 1
Processus 2
Chap. 3 – Les processus
12
2. Caractéristiques des processus
a) La commande ps sous UNIX
La commande ps permet à un utilisateur quelconque d’obtenir la liste des
processus appartenant à un ensemble particulier ainsi que certaines de leurs caractéristiques
PID : identifiant du processus (de type : pid_t)
TTY : le numéro de terminal de contrôle du processus TIME : temps cumulé d’exécution
CMD : nom du fichier exécutable correspondant au proc
Chap. 3 – Les processus
13
2. Caractéristiques des processus
ps - l : liste les propriétés des processus
F/S : S pour actif, R pour endormi et T pour suspendu
UID : identité du propriétaire (0 est le root cf. /etc/passwd) PPID : identité du père (0 ne possède pas de père)
C/PRI/NI : des informations relatives à la propriété du proc.
ADDR/SIZE : adresse et taille du processus
WCHAN : raison de sa mise en sommeil s’il est endormi
Chap. 3 – Les processus
14
2. Caractéristiques des processus
b) États des processus
Au cours de sa vie, un processus passe par plusieurs états :
• Transitoire : à la création ou lors de la création d’un fils
• Prêt (R) : prêt à passer en mode exécution
• Actif (R) : en cours d’exécution (mode noyau ou utilisateur)
• Endormi (S ou D) : en attente d’un évènement ( attente d’entrée/sortie, d’un signal, de la terminaison d’un processus)
• Suspendu (T) : le temps qu’un autre processus est en cours d’exécution
• Zombi (Z) : processus terminé dont le père n’a pas encore pris connaissance de cette terminaison
Chap. 3 – Les processus
15
2. Caractéristiques des processus
c) Changements d’état des processus
(1)
(2)
(3) (4) (5)
(6)
(7) (7)
(8)
(9)
Chap. 3 – Les processus
16
2. Caractéristiques des processus
Les transitions ont lieu dans les conditions suivantes :
(1) Le processus a acquis les ressources nécessaires à son exécution.
(2) Le processus vient d’être élu par l’ordonnanceur, il y a alors changement de contexte.
(3) Le processus revient d’un appel système ou d’une interruption.
(4) Le processus a réalisé un appel système ou une interruption est survenue.
(5) Le processus se met en attente d’un évènement : appel système bloqué par un évènement interne au système (libération de ressource ou terminaison d’un processus) ou externe (terminaison d’une entrée/sortie).
(6) L’évènement attendu par le processus s’est produit.
(7) Délivrance d’un signal particulier (SIGSTOP ou SIGTSTP).
(8) Réveil d’un processus par un signal SIGCONT.
(9) Le processus se termine.
Chap. 3 – Les processus
17
2. Caractéristiques des processus
d) Interruption d’un processus
• Il est possible d’envoyer manuellement un signal à un processus particulier.
« kill -9 pid »
Chap. 3 – Les processus
18
2. Caractéristiques des processus
e) Enchaînement de processus
• Sous UNIX, il est possible d’enchaîner les commandes
• La redirection d’une commande composée nécessite des parenthèses
• Chaque processus est indépendant et exécuté séquentiellement
Chap. 3 – Les processus
19
2. Caractéristiques des processus
f) Communication par tube
• Les utilisateurs disposent d’un mécanisme leur permettant de lancer un certain nombre de processus de façon concurrente.
• Ces processus peuvent communiquer par le biais d’un « tube (pipe) ».
• La fonctionnement est simple : la sortie standard d’un processus est redirigée vers l’entrée standard de l’autre processus.
• Ainsi, les résultats d’un processus servent de données à l’autre (producteur/consommateur)
Commande1 | commande2 | … | commanden
commande 1 commande 2 commande n
1 1 0 1
2 2
0
2
0: stdin 1: stdout 2: stderr
Chap. 3 – Les processus
20
2. Caractéristiques des processus
Exemple: ps –l | grep wait | tr –s “ “| cut –d“ “ -f14
Chap. 3 – Les processus
21
2. Caractéristiques des processus
g) Lancement de processus en mode détaché
• Il est possible de soumettre des commandes à l’interprète shell pendant l’exécution d’un processus.
• Ces processus fonctionnent en « arrière-plan »
• Sous X-Window, l’utilisateur dispose de plusieurs terminaux, par conséquent cette fonction peut paraître inutile.
• Mais ce mécanisme évite la multiplication des fenêtres.
Chap. 3 – Les processus
22
Partie 2
Les primitives
Chap. 3 – Les processus
23
3. Les primitives
a) Les caractéristiques d’un processus
• getpid
• getppid
• getuid
• getgid
• setuid
• setgid
• chdir
• getcwd
• time
• nice
Identifiants des processus
Identifiants des propriétaires
Répertoire de travail
Temps et priorité
Chap. 3 – Les processus
24
getpid, getppid
#include <unistd.h>
pid_t getpid(void); /*identité du processus */
pid_t getppid(void); /*identité du processus père */
• Sous UNIX il s’agit d’un long (pid_t)
• pid_t est un type prédéfini dans le fichier <unistd.h>
Chap. 3 – Les processus
25
getuid
#include <unistd.h>
pid_t getuid(void);
• Propriétaire réel du processus
• Tous les processus lancés par un utilisateur au cours d’une session de travail auront comme propriétaire réel la valeur identifiant cet utilisateur.
Chap. 3 – Les processus
26
getgid
#include <unistd.h>
pid_t getgid(void);
• Groupe propriétaire réel du processus
• Tous les processus lancés par un utilisateur au cours d’une session de travail auront comme groupe propriétaire réel le groupe auquel le propriétaire
appartient par défaut.
Chap. 3 – Les processus
27
setuid
#include <unistd.h>
int setuid(uid_t uid);
• Permets de modifier le propriétaire réel du processus
• C’est de cette manière que sont initialisées les valeurs des propriétaires des processus « shells » créés au cours de la procédure « login » de connexion des utilisateurs
Chap. 3 – Les processus
28
setgid
#include <unistd.h>
int setgid(gid_t gid);
• Permets de modifier le propriétaire réel du processus
• C’est de cette manière que sont initialisées les valeurs des propriétaires des processus « shells » créés au cours de la procédure « login » de connexion des utilisateurs
Chap. 3 – Les processus
29
chdir
#include <unistd.h>
int chdir(const char *ref);
• Permets de changer le répertoire de travail
• Toutes les références relatives utilisées par un processus le sont par rapport à ce répertoire. Un processus peut en toutes circonstances faire référence à son répertoire de travail par la chaîne de caractère « . »
• Le répertoire de travail d’un processus est modifiable par un appel à la fonction « chdir » où « ref » pointe sur la référence du nouveau répertoire souhaité.
Chap. 3 – Les processus
30
getcwd
#include <unistd.h>
char *getcwd(char *buf, size_t taille);
• Permets de récupérer à l’adresse « buf » la référence absolue du répertoire de travail du processus
• Le paramètre taille correspond à la taille disponible à l’adresse « buf » pour récupérer la référence.
• En cas de réussite, la valeur retournée est « buf », en cas d’échec la valeur retournée est NULL
Chap. 3 – Les processus
31
time (1)
#include <sys/times.h>
clock_t times(struct tms * buf);
• Permets de récupérer le temps CPU consommé dans les deux modes (utilisateur et noyau) par le processus et ses fils.
• La structure tms, prédéfinie dans le fichier <sys/times.h> contient les champs suivants :
Chap. 3 – Les processus
32
time (2)
#include <sys/times.h>
clock_t times(struct tms * buf);
• clock_t tms_time: nombre de clics d’horloge « facturés » au processus en mode utilisateur
• clock_t tms_stime: nombre de clics d’horloge « facturés » au processus en mode système
• clock_t tms_cutime: nombre de clics d’horloge « facturés » aux processus fils en mode utilisateur qui ne sont pas zombi (ni terminés, ni attendus)
• clock_t tms_cstime: nombre de clics d’horloge « facturés » au processus fils en mode systèmes qui ne sont pas zombi (ni terminés, ni attendus)
Chap. 3 – Les processus
33
time (3)
#include <sys/times.h>
clock_t times(struct tms * buf);
• Pour convertir les résultats obtenus au retour de la fonction times dans l’objet *buf en des durées exprimées en secondes, il suffit de les diviser par le nombre de clics d’horloge par seconde
• Constante CLK_TCK prédéfinie dans le fichier <limits.h>
Chap. 3 – Les processus
34
nice
#include <unistd.h>
int nice(int incr);
• Chaque processus possède une priorité par rapport aux autres processus.
• A chaque processus est associé un nombre entier entre 0 et 39 représentant sa priorité courante.
• La valeur par défaut est 20.
• Plus le nombre est petit, plus la priorité est grande.
• La valeur renvoyée est la nouvelle valeur du champ diminuée de 20.
Chap. 3 – Les processus
35
3. Les primitives
b) Création de processus
• Les primitives « exec » pour la création d’un processus à partir d’un programme
• La primitive « fork » pour la création d’un nouveau processus qui est la copie du père
Chap. 3 – Les processus
36
execve (1)
#include <unistd.h>
int execve(const char*nomfichier, char * const argv[], char * const envp[]);
• La primitive execve() exécute un programme exécutable ou un script shell.
• Si l’appel système réussi, le processus appelant le exec se termine lors de la fin du processus qu’il a lancé.
• Les instructions qui suivent le exec ne sont pas exécuté si le exec échoue.
Nomfichier : est le nom du programme à exécuter
Argv : est un tableau contenant les arguments du programme Envp : est un tableau contenant les variables d’environnement.
Chap. 3 – Les processus
37
execve (2)
Il existe cinq variantes de cette fonction simplifiant l’appel système
int execl (const char* nomfichier, char* const arg, …) int execp (const char* nomfichier, char* const arg, …)
int exece (const char* nomfichier, char* const arg, …, char * const envp[]) execl : les paramètres sont donnés dans une liste terminée par NULL.
execp : indique que l’exécutable doit être cherché en utilisant la variable PATH.
exece : indique que le tableau des variables d’environnement suit la liste des paramètres.
int execv (const char* nomfichier, char* const argv[]) int execvp (const char* nomfichier, char* const argv[])
Pour les deux suivantes, les paramètres sont dans un tableau (v) execvp à le même sens que execp (PATH)
Chap. 3 – Les processus
38
Exemple
#include <stdio.h>
#include <unistd.h>
void main(){
execlp(“ls“, “ls“, NULL);
execlp(“ls“, “ls -l“, NULL);
// execlp(“lss“, “ls“, NULL); //erreur lss inconnu perror (“Erreur sur execlp”);
printf(“fin\n”);
}
Chap. 3 – Les processus
39
fork
#include <unistd.h>
pid_t fork(void);
• Cette primitive permet la création dynamique d’un nouveau processus qui s’exécute de façon concurrente avec le processus qui l’a créé.
• Tout processus UNIX, excepté le processus originel (de numéro 0), est créé à partir de cette primitive.
• Le processus créé est une copie exacte du processus « père », avec une grande part de ces attributs.
• La valeur de retour permet de différencier le père du « fils ».
- 0 dans le fils
- Le pid dans le père
Chap. 3 – Les processus
40
Exemple
Chap. 3 – Les processus
41
Chap. 3 – Les processus
Organisation de la mémoire
Le processus est composé de quatre segments de mémoire :
1) Les instructions : les instructions appartiennent à l’espace d’adressage du processus et peuvent être partagées entre plusieurs processus si le code est réentrant. C’est un espace de taille fixe pour un programme donné.
Segment de code
Mémoire vive
\x00FF
42
Chap. 3 – Les processus
Organisation de la mémoire
Le processus est composé de quatre segments de mémoire :
2) Les données : manipulées par le programme appartenant à l’espace d’adressage du processus et dont la taille varie au grès des allocations- mémoires.
Segment de données Segment
de code
Mémoire vive
\x00FF
43
Chap. 3 – Les processus
Organisation de la mémoire
Le processus est composé de quatre segments de mémoire :
La pile : dont la taille varie en fonction de l’imbrication des appels de fonctions. (variables locales, sauvegardes de contextes, etc.)
Segment de pile Segment
de données Segment
de code
Mémoire vive
\x00FF
44
Segment de contrôle
Chap. 3 – Les processus
Segment de pile Segment
de données Segment
de code
Mémoire vive
\x00FF
Organisation de la mémoire
Le processus est composé de quatre segments de mémoire :
4) Le bloc de contrôle : il contient les informations utiles au système
(descripteur de fichiers, signaux, identité, priorité, etc.). Il a une taille fixe quelque soit le processus.
45
Chap. 3 – Les processus
Exemple
46
Chap. 3 – Les processus
Exemple
47
Chap. 3 – Les processus
Exemple
48
Chap. 3 – Les processus
Exemple
49
Chap. 3 – Les processus
Exemple
50
Chap. 3 – Les processus
Exemple
51
Autre exemple
Chap. 3 – Les processus
52
Chap. 3 – Les processus
Autre exemple
53
Chap. 3 – Les processus
Autre exemple
54
Chap. 3 – Les processus
Autre exemple
55
Chap. 3 – Les processus
Autre exemple
56
Chap. 3 – Les processus
Autre exemple
57
Chap. 3 – Les processus
Autre exemple
58
Chap. 3 – Les processus
Autre exemple
59
Partie 3
Processus zombis et Synchronisation père/fils
Chap. 3 – Les processus
60
4. Processus zombis et synchronisation
a) Les processus zombis
• Nous avons vu que tout processus se terminant passe dans l’état zombi dans lequel il reste aussi longtemps que son père n’a pas pris connaissance de sa terminaison.
• Tout processus UNIX se terminant possède une valeur de retour à laquelle son père peut accéder (comme une fonction appelant une fonction avec une valeur de retour).
Dans le cas des processus, le père et le fils se déroulent « en parallèle ».
Comment synchroniser les processus ?
Un système doit fournir le moyen à un processus père d’accéder à tout moment au code retour de ses fils terminés. La réponse a ce problème est le processus zombi.
• La terminaison d’un processus provoque l’envoi d’un exemplaire du signal
SIGCHLD à son père. Et les seules informations conservées d’un processus zombi sont : son code de retour, ses temps d’exécution, son « PID » et le « PPID ».
Chap. 3 – Les processus
61
4. Processus zombis et synchronisation
Exemple de processus zombi :
Chap. 3 – Les processus
62
4. Processus zombis et synchronisation
Exécution de l’exemple précédent : # .\zombi &
Chap. 3 – Les processus
63
4. Processus zombis et synchronisation
• Sur l’exemple précédent, les processus 2830 et 2831 sont passés à l’état zombi à leur terminaison. Sur cet exemple, les processus zombis disparaissent en même temps que leur père.
• En fait, à la terminaison de leur père, les processus fils sont adoptés par le processus de numéro 1 qui est aussitôt avisé que ces fils sont terminés, ce qui à pour effet la suppression de ses processus zombis.
Chap. 3 – Les processus
64
4. Processus zombis et synchronisation
b) Les primitives wait et waitpid
• Ce sont ces primitives (et elles seules) qui permettent l’élimination des processus zombis
• Ce sont ces primitives qui permettent la synchronisation d’un processus sur la
terminaison de ses descendants avec récupération des informations relatives à cette terminaison.
Chap. 3 – Les processus
65
wait
#include <sys/stypes.h>
#include <sys/wait.h>
pid_t wait(int * pointeur_status);
• Si le processus appelant ne possède aucun fils, la primitive renvoie la valeur -1 et la variable
« errno » a comme valeur « ECHILD » (cf. perror()).
• Si le processus père possède au moins un fils (le système choisit lequel), et si l’adresse
« pointeur_status » est différent de « NULL », la valeur « pointeur_status » founit des
informations sur la terminaison de ce processus zombi. (qui disparaît effectivement des tables du système)
• La primitive « wait » supprime un zombi sans qu’il soit possible de choisir lequel.
• Si le processus appelant ne possède pas de fils zombi, le processus appelant est bloqué jusqu’à l’un de ses fils devienne zombi, ou l’appel système est interrompu par un signal « non
mortel ».
Chap. 3 – Les processus
66
4. Processus zombi et synchronisation
Exemple d’utilisation de la primitive « wait »:
Chap. 3 – Les processus
67
4. Processus zombis et synchronisation
Exécution de l’exemple précédent: # .\zombi&
Chap. 3 – Les processus
68
waitpid (1)
#include <sys/stypes.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int * pointeur_status, int options);
• Cette primitive permet de tester, en bloquant ou non le processus appelant, la terminaison d’un processus particulier ou appartenant à un groupe de processus donnés et de récupérer les
informations relatives à sa terminaison à l’adresse « pointeur_status ».
• Le « pid » permet de sélectionner le processus attendu de la manière suivante :
<-1 tout processus fils dans le groupe |pid|
-1 tout processus fils
0 tout processus fils du même groupe que l’appelant
>0 processus fils d’identité pid
Chap. 3 – Les processus
69
waitpid (2)
#include <sys/stypes.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int * pointeur_status, int options);
• Le paramètre « options » est une combinaison bit à bit des valeurs suivantes :
WNOHANG Le processus appelant n’est pas bloqué si le processus demandé n’est pas terminé.
WUNTRACED Si le processus concerné est stoppé et si cette information n’a pas encore été transmise, elle l’est à présent.
• La fonction renvoie : -1 en cas d’erreur 0 en cas d’échec
Le numéro du « pid » du processus fils zombi pris en compte
Chap. 3 – Les processus
70
4. Processus zombis et synchronisation
c) Interprétation de la valeur renvoyée
• La valeur de l’entier « * pointeur_status », au retour d’un appel réussi de l’une des primitives « wait » ou « waitpid », permet d’obtenir des informations sur la
terminaison ou l’état du processus (dans le cas de la primitive « waitpid » appelée avec l’indicateur « WUNTRACED »)
• Un processus qui s’est terminé normalement, c.-à-d. par une instruction
« return(n) », par un appel « _exit(n)» ou exit(n), l’octet de poids faible n est récupérer dans le second octet de « *pointeur_status ».
Octet 3 Octet 2 Octet 1 Octet 0
???????? ???????? 00000000
Entier n du processus fils
Entier n récupéré par le processus père
Retour: n = 14
Retour: n%255 = 14
Chap. 3 – Les processus
71
Exemple 1 : wait
Chap. 3 – Les processus
72
Exemple 1 : wait
Chap. 3 – Les processus
73
Exemple 2 : waitpid
Chap. 3 – Les processus
74