Vous êtes sur la page 1sur 45

Chap1.

Les processus

M. Ben Salah
Qu’est ce qu’un processus ?

Rappel sur la chaine de production d'un programme exécutable :

Programme source

Compilation Prog.o
Prog.c
gcc –c prog.c

gcc – o prog.exe prog.o


édition de lien
Exécutable en mémoire

Prog.exe Prog.exe
chargement

processus Exécutable sur disk 2


Rappel :
La transformation d’un programme écrit dans un langage de haut niveau en un programme dit exécutable,
écrit en langage machine s'effectue à l'aide de plusieurs étapes :
1- Création du fichier source : L'utilisateur écris son programme à l'aide d’un éditeur de texte qui crée un
fichier sur le Disque
2- Ce fichier source est ensuite compilé à l'aide d'un compilateur dépendant du langage de programmation
utilisé et dont le rôle est de vérifier que la syntaxe du langage est respectée et de générer alors l'équivalent
du programme source en langage machine : on obtient alors sur disque le fichier objet.
3- Ce fichier objet est ensuite soumis à l'éditeur de liens dont le rôle est de résoudre les références externes,
c'est-à-dire par exemple, d'associer les appels système inclus dans le programme à leur adresse dans le
système.
== > L'éditeur de liens produit sur disque le fichier exécutable.
4- Exécution à la demande de l’utilisateur :
Lors de cette étape le fichier exécutable est alors monté en mémoire centrale : c'est l'étape de
chargement. Le système alloue de la place mémoire pour placer le code et les données du Programme == >
Une fois chargé un programme donne naissance à une entité appelée processus (un même programme peut
être exécuté plusieurs fois simultanément == > plusieurs processus)

3
Rappel :
Le programme à exécuter est placé en mémoire centrale. Le processeur commence l'exécution du
programme : la première instruction de celui-ci a été chargée dans le registre instruction (RI) et le Compteur
Ordinal (CO) contient l'adresse de la prochaine instruction à exécuter. Lorsque l'instruction courante aura été
exécutée, le processeur chargera dans la registre RI l'instruction pointée par le CO et le compteur ordinal
prendra la valeur de l’adresse l’instruction suivante.
== > On voit donc qu'à chaque étape d'exécution du programme, le contenu des registres du compteur
ordinal évolue
Chaque processus (programme en cours d’exécution) présent dans le système occupe différentes régions en
mémoire.
Du point de vue de leur fonctions, on peut distinguer :
- une zone de texte (code de la commande),
-une zone de données statiques, contenant les données du programme connues dès sa compilation
(variables globales ou statiques du C par exemple),
- une Pile : Pour stocker les données obtenues en cours d'exécution, le système utilise alors un segment
nommé la pile (stack en anglais) :
- une pile utilisateur réservée aux variables dynamiques crées au
moment de l’exécution (variables locales en C),
- une pile système, utilisée lors de l’exécution de fonctions système,
- une zone de tas réservée à l’allocation dynamique (par exemple, la fonction new() en C++),
-un bloc de contrôle réunissant l’ensemble des informations
concernant le processus
4
Qu’est ce qu’un processus ?

● Définition :
Un processus est une instance d’un programme en cours d'exécution
auquel est associé un environnement processeur (RI,CO …) et un
environnement mémoire appelés contexte du processus.

● Le programme est statique et le processus représente la dynamique


de son exécution : Un processus est née suite à l’exécution d’un
programme, a une durée de vie, puis meurt.

● l'image de l'état du processeur et de la mémoire au cours de


l'exécution d'un programme.

● Lors de l'étape de chargement, le système alloue de la place mémoire


pour placer le code et les données du programme et crée la pile
d'exécution de ce qui est en train de devenir un processus 5
Contexte d’un processus

● Le contexte d'un processus (contexte d’exécution du processus) est


l'ensemble des informations (données) dynamiques qui représente l'état
d'exécution d'un processus. Ces données permettent de reprendre
l'exécution d'un processus qui a été interrompu.

6
Pourquoi un processus ?

● BUT : Partager un (ou plusieurs) processeur entre différents processus.


Le partage d’un CPU dans le temps permet d’effectuer plusieurs programmes (on parle
de processus) en pseudo-parallèle (faux parallélisme) car le CPU n'en effectue qu'un à la
fois.
== > possibilité de faire plusieurs activités ”en même temps”.
Remarque : les SE multiprocessus peuvent assez facilement fonctionner en vrai
parallélisme (plusieurs CPU).

P1
P2
Processeur ?
P3

PN

● Plusieurs processus sont prêts à être exécutés, le SE doit faire un choix


Ordonnancement (chapitre ordonnancement des processus) 7
Ordonnanceur

● L'ordonnanceur (scheduler) désigne le composant du noyau du SE qui


définit l'ordre dans lequel les processus prêts détient le processeur et la
durée d'utilisation, en utilisant un algorithme d'ordonnancement

8
Ordonnanceur
-L'ordonnanceur permet à tous les processus :
- de s'exécuter suivant une politique (ordonnancement)
Algorithme d’ordonnacement
- d'utiliser le processeur de manière optimale du point de vue de l'utilisateur.
comment ? : à intervalles réguliers, le système appelle une procédure
d'ordonnancement qui élit le processus à exécuter.

préemption
ordonnanceur

B B B B B Classement selon une politique


CPU C C C C C d’ordonnancement
P P P P P
P1 P4 P2 P5 P3
fil d’attente processus prêts

B B B B B
C C C C C déblocage
blocage
P P P P P 9
fil d’attente processus bloqués
9
Ordonnanceur

● L'ordonnanceur (scheduler) désigne le composant du noyau du SE qui


définit l'ordre dans lequel les processus prêts détient le processeur et la
durée d'utilisation, en utilisant un algorithme d'ordonnancement

10
Algorithme d'ordonnancement

Il existe deux familles d'algorithmes :

- Sans réquisition = non préemptif : un processus est exécuté


jusqu’à la fin

Le choix d'un nouveau processus ne se fait que sur blocage ou


terminaison du processus courant (utilisé en batch par
exemple)

inefficace et dangereux (ex: exécution d’une boucle sans fin…)

- Avec réquisition = préemptif : à intervalle régulier,


l'Ordonnanceur reprend la main et élit un nouveau processus
actif

11
Etat d’un processus
● Un processus a une durée de vie limitée. Lors de son exécution,
un processus est caractérisé par un état définit par son activité
courante
● Les différents état d’un processus :
Nouveau : Le processus est en cours de création.
Prêt (ou éligible) : Le processus attend d’être sélectionné. C’est
l’état d'attente du processeur
Elu : Le processus est sélectionné par l’ordonnanceur. Il a été affecté
a un processeur et s'exécute == > L'état élu est l'état
d'exécution du processus.
´ Bloqué (en attente) : Le processus attend qu’un ´événement
extérieur se produise. C’est l'état d'attente d'une ressource autre
que le processeur.
Exemple : demande à accéder à une ressource qui n’est pas
immédiatement disponible (réalisation d'une entrée/sortie …)
Terminé : Le processus a fini son exécution. 12
Etat d’un processus

Graphe de transition des états d’un processus :

terminé
En exécution
zombie

(éligible) 0
En attente de ressources
nouveau

*) Zombie : processus s’est terminé mais son père n’a pas encore lu le code de retour 13
Etat d’un processus

La signification des transitions :


0. Un processus est crée. Son code est chargé. Les données sont initialisées pour
l’ordonnanceur.
1. Bloqué en attente d’un événement externe ( ressource, données…)
L’ordonnanceur averti par le processus bloqué élection d’un autre processus
2. Réquisition du processeur : le processus a épuisé le quantum de temps qui lui a
été attribué par exemple ou terminé son exécution, l’ordonnanceur choisit un
nouveau processus.
3. L’Ordonnanceur élit ce processus parmi les prêts
4. L’événement attendu se produit : La ressource ou les données deviennent
disponible

14
Etat d’un processus

Exemple pour chaque transition .

Prêt-- > élu processus attend le processeur, une fois il l’a eu il s’exécute
Elu --- > bloqué attente d’une entrée sortie (E/S).
Bloqué -- > prêt fin d’une E/S.
Elu --- > prêt fin d’un quantum, ou arrivé d’un processus plus prioritaire.

15
Causes de changement d’état d’un processus

● Le changement d'état d'un processus peut être provoqué par :


- un autre processus (qui lui a envoyé un signal, par exemple)
- le processus lui-même
exemple : Le passage de l’état actif à l’état bloqué peut être provoqué par attente d’une E/S
bloquante du type scanf, opérations sleep ou wait sous UNIX.

- une interruption (fin de quantum, terminaison d'entrée-sortie, ...)


Le passage de l’état actif (élu) à l’état prêt est provoqué par le système en fonction de sa
politique d’ordonnancement (fin de quantum, préemption du processus actif si un processus
plus prioritaire devient prêt dans le cas des politiques préemptives, …),

16
Table de processus

● Pour gérer les processus, le système mit en place une table de structures appelée table
des processus.

● Chaque processus représenté dans cette table par une structure de donnée destinée
à regrouper toutes les informations du contexte du processus que l’on nomme le bloc de
contrôle des processus (BCP).

17
Bloc de contrôle de processus (BCP)

●Lorsqu’un processus est temporairement suspendu, il faut qu’il puisse


retrouver l’état où il se trouvait au moment de suspension, il
faut que toutes les informations dont il a besoin soient sauvegardées
pendant sa mise en attente. Le bloque de contrôle d’un processus (BCP)
contient les information nécessaires à la reprise d’un processus

18
Bloc de contrôle de processus (BCP)

● BCP : contient plusieurs informations concernant un processus


spécifique, comme par exemple:
- un identificateur unique du processus (un entier) : le PID
- l'état courant du processus (élu, prêt, bloqué)
- le compteur ordinal ou compteur d’instruction qui indique l’instruction
suivante devant être exécutée par ce processus,
- la valeur des autres registres du processeur
- le contexte mémoire : ce sont des informations mémoire qui
permettent de trouver le code et les données du processus en
mémoire centrale
- Informations sur l’ordonnanceur de la CPU: information concernant la
priorité du processus
- Informations sur l’état des E/S : Les fichiers ouverts …


19
Commutation de contexte

La commutation du processeur sur un autre processus nécessite la sauvegarde de


l’état de l’ancien processus et de charger l’état sauvegardé par le nouveau processus.
Cette tâche est connue sous le nom de commutation de contexte
.

20
Identité d’un processus : PID

● Un processus est identifié de manière unique dans le système par


un numéro qui est l’identifiant du processus : PID

● Le système alloue un PID unique à chaque nouveau processus

● Le processus de PID=0 est créé au démarrage de la machine. Ce


processus a toujours un rôle spécial pour le système (surtout pour la
gestion de la mémoire)
Le processus de PID=0 crée, grâce à un appel de fork , le processus
init dont le PID =1
● Le processus de PID=1 est l’ancêtre de tous les autres processus

21
Identité d’un processus : PID

● Chaque processus possède un père (sauf le processus de PID=0) et


chaque processus peut avoir plusieurs fils organisation hiérarchique

22
Identité d’un processus : PID

Dès sa création, un processus reçoit les paramètres suivants :


PID : identificateur du processus (numéro unique)
PPID : identificateur du processus père
UID : identificateur de l’utilisateur qui a lancé le processus
GID : identificateur du groupe de l’utilisateur qui a lancé le
processus

23
Identité du processus : PID

● Pour connaitre le PID d’un processus on utilise l’appel système getpid() :


#include<unistd.h>
pid_t getpid (void);

Utilisation :

#include<unistd.h>
int main(){
pid_t pid;
pid = getpid();
printf("pid=%ld ",(long) pid);

24
Identité du processus parent : PPID

● Pour connaitre le PPID d’un processus on utilise l’appel système getppid() :


#include<unistd.h>
pid_t getppid (void);

Utilisation :

#include<unistd.h>
int main(){
pid_t ppid;
ppid = getppid();
printf("pid=%ld ",(long) ppid);

25
Propriétaire d’un processus

● UID identifiant utilisateur :


int uid = getuid(void);

● GID groupe d’appartenance du processus

int gid = getgid();

26
Visualiser les processus

Visualiser les processus sous Linux :


ps

Exemple : ps -ef

27
Création d’un processus : fork()

● Pour créer un nouveau processus on utilise la primitive fork() :

#include <unistd.h>
pid_t fork();

0 pour le processus fils


● fork() retourne
PID du fils pour le processus père
-1 en cas d’erreur

● Le processus fils peut exécuter un nouveau code à l'aide des primitives


de recouvrement exec

28
Création d’un processus : fork()

● Le processus créateur est le père, le processus crée est le fils


La création de processus se fait par clonage : Le processus créateur
(le père) par un appel à la primitive fork crée un processus fils qui est
une copie exacte de lui-même (code et données).
== > le système duplique le contexte du processus parent : le code,
les données, et la pile
● Le fils hérite notamment de son père :
- le code
- les données (duplication)
- les descripteurs de fichiers ouverts par son père et partage donc
ces fichiers avec lui.

29
Création d’un processus : fork()

Ecrire un programme en C qui permet au processus père de


créer un fils. Le processus père affiche je suis le père et le
processus fils affiche « je suis le fils »

30
Processus de PID =2222 Processus de PID =2229
#include <stdio.h> #include <stdio.h>
#include <unistd.h> #include <unistd.h>
int main(){ int main(){
pid_t pid_fils, pid_fils2; pid_t pid_fils, pid_fils2;
printf("Le fils n'est pas encore né \n"); printf("Le fils n'est pas encore né \n");
pid_fils=fork(); pid_fils=fork();
if(pid_fils==-1) perror("text"); if(pid_fils==-1) perror("text");
if(pid_fils==0){ if(pid_fils==0){
printf(“je suis le fils“ ); printf(“je suis le fils“ );
printf(“mon PID=%ld“, (long) getpid()); printf(“mon PID=%ld“, (long) getpid());
printf(" PPID =%ld, long) getppid()); printf(" PPID =%ld, long) getppid());
}else{ }else{
printf(“je suis le père: mon PID=%ld“, (long) getpid()); printf(“je suis le père: mon PID=%ld“, (long) getpid());
printf(" PID de mon fils =%ld, (long)pid_fils); printf(" PID de mon fils =%ld, (long)pid_fils);
printf(" PPID =%ld, long) getppid()); printf(" PPID =%ld, long) getppid());
return 0; return 0;
} }

PID= 2222
PID= 2229
PPID= celui du shell
PPID= 2222
PID de mon fils= 2229
31
Tables des descripteurs
processus P1 (le père) :
processus P1 (le père) : fork() Table de
fichiers Table i-nœuds
ouverts en mémoire
Table de
Table de
fichiers Table i-nœuds
descripteurs
ouverts en mémoire …
… ….
1 …
desc … 1 …
… … … .
… … … . … … …
… après fork()
.1 . .
rw cv . … … …
…. .2 2 . cv
. processus P2 fils de P1 :
wr
.
.
…….
desc .
…….
.
.
.
…….
. … …….
. compteur de
….
descripteurs Pointeur sur
Compteur de desc i-noeud
références .
.
.
32
Synchronisation de processus : primitive sleep

Sleep : Permet la mise en sommeil d’un processus

- sleep(n) : suspend l’exécution du processus appelant pour une


durée de n secondes.
utiliser <unistd.h>

Exercice : ordonnancer 2 processus : le processus fils doit s’exécuter avant


le processus père

33
Terminaison d'un processus Unix :
primitive exit

● Un appel à la primitive exit provoque la terminaison du processus


effectuant l'appel avec un code retour valeur :
void exit (int valeur)

== > provoque la terminaison du processus appelant

● Un processus qui se termine passe dans l'état zombi et reste dans


cet état tant que son père n'a pas pris en compte sa terminaison.

34
Synchronisation de processus : primitive wait

● Lorsqu'un processus se termine, le système démente tout son


contexte (libère ces ressources, sa mémoire …), sauf l'entrée de la
table des processus le concernant.

● Tant que l’entrée n’est pas libéré l’ID du processus ne peut pas être
utilisé (cas de processus Zombie).

● Cette entrée est libérée par le processus père quand il est avisé de
la terminaison de son fils.
== > Le processus père doit être au courant de la terminaison de ces
fils afin de libérer leurs entrée dans la table des processus
== > besoin d’un mécanisme permettant au processus père de :
- connaitre le moment de terminaison du processus fils
- le statu de terminaison du processus fils ainsi que le code de
sortie 35
Synchronisation de processus : primitive wait

● l’appel wait() permet au processus parent :


- de s’informer auprès de son processus enfant.
- de suspendre l'exécution du processus père jusqu'à ce qu’un de ses
fils se termine
- de connaitre le statut de terminaison et le code de retour du fils
- récupère la mort de son fils et de détruire l'entrée de la table des
processus concernant le processus fils terminé (défunt). Le
processus fils disparaît complètement ce qui permet d’éviter les
processus orphelin et zombie
● La communication entre le fils zombi et le père s'effectue par le biais
d'un signal transmis du fils vers le père (signal SIGCHLD ).
== > SIGCHLD permet de réveiller un processus dont un des ces fils
vient de se terminer (mourir).
● Si le père n'attend pas la mort de son fils par un appel à la primitive
wait alors la table des processus n'est pas libérée et il y a risque de
36
saturation de cette table.
Synchronisation de processus : primitive wait

- Primitive WAIT
#include <sys/wait.h>
pid_t wait (int *status)
L’argument de la fonction wait est un pointeur sur un entier
devant recevoir l’état du processus fils.

37
Synchronisation de processus : primitive wait

PID du processus fils


wait (&status) return qui s’est terminé

- 1 s’il y a pas de
processus fils

l'appel wait se termine instantanément en retournant la valeur -1 si


le processus n'a aucun fils,

Remarques :
- si aucun fils ne se termine, l'appel wait est bloquant.
- l'appel se termine dès qu'un fils est dans l'état zombie, et retourne le PID de ce
fils. Le paramètre status contient le code de terminaison du fils (paramètre de
l'appel exit( ) du fils)

38
38
Synchronisation de processus : primitive wait
Exemple :
# include <stdio.h>
#include <unistd.h> Qui ce qu’on obtient à
#include <sys/wait.h> l’affichage pour le fils et pour
int main() { le père ?
int status; int i = 0; int pid;
pid=fork();
if(pid==0) {
Printf("\n je suis le fils... ");
printf("\n Chez moi, i=%d ",i);
i++; printf("\n Maintenant, chez moi, i=%d \n" ,i);
} else{
Printf("\n je suis le pere... ");
printf("\n Chez moi, i=%d \n ",i);
wait(&status);
}
return 0;
}
39
39
Synchronisation de processus : primitive wait

Au programme précédent on peut ajouter le contrôle de la valeur retournée par wait


Exemple :
int main(){
Int status; pid_t wid;
pid_t pid_fils, pid_fils2;
printf("Le fils n'est pas encore né \n");
pid_fils=fork();
if(pid_fils==-1) perror("text");
if(pid_fils==0){
printf(“je suis le fils“ );
printf(“mon PID=%ld“, (long) getpid());
printf(" PPID =%ld, long) getppid());
}else{
printf(“je suis le père: mon PID=%ld“, (long) getpid());
printf(" PID de mon fils =%ld, long)pid_fils);
printf(" PPID =%ld, long) getppid());
wid=wait(&status);
if(wid==-1){
printf("erreur",);
}else{

printf("le status de sortis du fils de pid= %ld est %d, (long) pid_fils,
status);
}
return 0; État de sortie 40
}
Synchronisation de processus : primitive waitpid

waitpid() : Permet au processus appelant d’attendre de manière


sélective la terminaison d’un de ses fils

pid_t waitpid(pid_t pid, int *status, int option)

Exemple d’appel :
waitpid(pid_fils, &status, 0)

41
Recouvrement d’un processus

La création d'un processus se faisant par fork, le processus fils est


un clone du père. La seule différence réside dans la valeur
retournée par fork

Parfois on a besoin que le processus fils soit un programme


totalement différent du père pour cela il faut recouvrir son code par
celui d'un autre programme
== > primitive exec

42
Recouvrement d’un processus : exec

● Un processus peut exécuter un nouveau code à l'aide des primitives


de recouvrement exec
== > un processus fils peut charger un nouveau code par un appel
système à exec :
- code et données remplacés
- pointeur d’instruction réinitialisé

43
Recouvrement d’un processus : exec

● execl et execlp : Liste des arguments : le nom du


int execl(const char *path, const char *arg, ...); programme (le fichier), les
arguments du programme ,
int execlp(const char *file, const char *arg, ...); dernier argument =NULL

Exemple :
NULL = argument de fin de
execl("/bin/ls","ls","-l",NULL);
la ligne de commande
execlp ("ls","ls","-l",NULL); Ps : Utilisez la commande which

● execv et execvp : Table des arguments contenant :


int execv(const char *path, char *const argv[]); le nom du programme (le
int execvp(const char *file, char *const argv[]); fichier), les arguments du
programme , dernier élément
Exemple :
=NULL
char *args[]={"ls","-l",NULL}
execv("/bin/ls",args);
Ou :
execvp("ls",args);
Utilsez la commande which 44
#include <stdlib.h>
#include <unistd.h>
int main(void)
{
/* Tableau de char contenant les arguments (ici aucun) + le nom du programme et
NULL qui sont obligatoires) */
char *args[] = { "ps", NULL };
/* Lancement de la commande */
if (execv("/bin/ps", args) == -1) {
perror("execv");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}

45

Vous aimerez peut-être aussi