Vous êtes sur la page 1sur 29

Ecole Nationale Polytechnique d’Oran- Maurice AUDIN

Département de Génie des Systèmes


Option: Ingénierie et Management des Systèmes d’Information
(IMSI)

Intitulé de la matière: Programmation Système

Gestion des processus


(1ère partie)
Mme Nawel BENDIMERAD

Année universitaire 2020/2021


1
Définition
• Le processus est un concept clé dans un système d’exploitation.
Un processus est un programme en cours d’exécution. C’est-à-
dire, un programme à l’état actif.
• Un processus représente l’image de l’état du processeur et de la
mémoire au cours de l’exécution d’un programme : le programme
est statique et le processus représente la dynamique de son
exécution.
• Un seul processus est exécuté à la fois sur un processeur. Comme
le processeur commute entre les différents processus, on a une
impression de parallélisme. Le compteur ordinal permet de garder
en mémoire la prochaine instruction à exécuter.
• Exemples: Courier électronique, la compression d’un fichier, le
navigateur web.
2
Processus et boot

• Le kernel (noyau du SE) est chargé à partir du disque lors du


démarrage du système.
– Bootstrapping = initialisation du système et définition de
l'environnement pour exécuter des processus.
• Un premier processus (init) est lancé,
• S'exécute (réside) jusqu'à l'arrêt du système,
• Les autres processus sont alors créés à partir d'init (ou d'un de
ses descendants).

3
Image mémoire d’un processus
• L’image mémoire (Memory Map) d’un processus représente l’espace
mémoire alloué, qui est composé en plusieurs parties nécessaires au
bon fonctionnement du processus.
• Chaque processus a sa propre image mémoire contenant les
informations suivantes :

– Code: instructions du programme.


– Données: constantes et variables déclarées.
– Tas: espace mémoire qui peut être alloué
dynamiquement en cours d’exécution.
– Pile: valeurs des registres (compteur ordinal,
pointeur de pile),variables locales et paramètres
de fonction, etc.
4
Structure de données associée au processus (BCP)

• Le système gère une table de structures appelée table des


processus. Chaque processus y est représenté par une structure
de données que l’on nomme le bloc de contrôle des processus
(BCP).
• Cette structure de données contient des informations sur un
processus.
• Ces informations sont dans une zone mémoire accessible
uniquement par le noyau du SE.
• Pour obtenir des informations sur les processus, il est donc
nécessaire de passer par des appels systèmes, tels que:
getpid(), getppid(), getuid(), getgid(), etc.

5
Structure de données associée au processus
(BCP)
Chaque structure de données contient les informations suivantes:
• Identifiant du processus.
• Etat du processus,
• Contexte du processus (compteur ordinal, registres de l’UC,
etc.),
• Informations sur le scheduling (ordonnancement),
• Informations sur la gestion de la mémoire,
• Information sur l’état des E/S (liste de périphériques d’E/S
alloués au processus, fichiers ouverts, etc.)
• Informations de Comptabilisation (utilisation des ressources
par le processus, temps CPU consommé, etc.)
6
Création de processus
Sous Windows

• Appel système CreateProcess : permet de créer un processus


dont les caractéristiques sont données en paramètres de l'appel
système.
• Exemple :

7
Création de processus
Sous Linux
• Appel système fork() : permet de créer un processus dont les
caractéristiques sont identiques à celles du processus courant
(seuls le PID et le PPID changent).
– retourne une valeur différente dans le processus père et le
processus fils.
• Exemple :

8
Description de la fonction fork( )
• La fonction fork() permet à un processus (programme en cours
d’exécution) de créer un nouveau processus.
• Le nouveau processus (fils) se voit attribuer un PID qui lui est
propre et s’exécute de manière concurrente avec le processus
père qui l’a créé et dont le PID reste inchangé.
• Le processus père et le processus fils ont le même code source
mais ne partagent pas leurs variables au cours de l’exécution.
• Tout processus Linux (excepté le processus racine) est créé à
l’aide de cet appel système.

9
Description de la fonction fork( )
• A l’issu de l’exécution de l’appel fork() par le processus père,
chaque processus reprend son exécution au niveau de
l’instruction suivant le fork().
• Afin de différencier les traitements à réaliser dans le cas du
processus père et du processus fils, on utilise la valeur de
retour de la fonction fork() :
– Si l’appel à fork() échoue, le processus fils n’est pas créé et
la valeur de retour est −1.
– Dans le processus fils, la valeur de retour vaut 0.
– Dans le processus père, la valeur de retour vaut le PID du
processus fils qui vient d’être créé.

10
Début et fin d’un processus
 Un processus a généralement un début et une fin
 Début : création par un autre processus - par fork()
• il existe un processus “primitif” créé à l’origine du système
 Fin
• auto-destruction (à la fin du programme) - par exit()
• destruction par un autre processus - par kill()
• certains processus ne se terminent pas (réalisant des fonctions du
système)
 Dans le langage de commande
 un processus est créé pour l’exécution de chaque commande
 on peut créer des processus pour exécuter des commandes en
(pseudo)-parallèle :
Exemple:
$ prog1 & prog2 &
(deux processus sont créés pour exécuter prog1 et prog2 )

11
Synchronisation entre un processus
père et ses fils
• Le processus fils termine son exécution par exit(statut), où
statut est un code de fin.
• La terminaison du processus père n’entraine pas la
terminaison de ses fils.
• Lorsqu’un processus fils se termine avant son père, il passe à
l’état zombie.
• Un processus zombie ne peut plus s’exécuter, mais consomme
encore des ressources (tables). Il faut éviter de conserver des
processus dans cet état.

12
Synchronisation entre un processus père et
ses fils

• Afin de terminer complètement un processus fils


zombie et donc libérer les ressources associées, le
processus père peut faire appel à une fonction de
synchronisation de type wait() ou waitpid().

13
La fonction wait()
• pid_t wait(int *status) suspend l’exécution du processus père
(appelant) jusqu’à ce que l’un de ses fils se termine.
• La synchronisation du processus père et de l’ensemble de ses fils
nécessite donc autant d’appel à wait() que de processus fils.
• pid_t waitpid(pid_t pid, int *status, int options) permet de
spécifier le PID du fils dont la fin est attendue.
• Le second argument de waitpid() permet de récupérer une
information relative à l’exécution du fils dont on attend la
terminaison.
• Cette information peut être transmise par le processus fils au
moyen de la fonction void exit(int status) qui permet la
terminaison normale du processus.

14
La fonction wait(int *status)
• Valeur de retour :
Si aucun processus fils => -1 ;
Sinon, pid du processus fils s'utilise du coté du processus
père
• Sémantique :
 si aucun des processus fils n'est terminé, le processus père
se bloque.
 la fin du blocage arrive soit par la fin d’exécution du
processus fils, soit par une « interruption » du processus père
(réception d'un signal)

15
La fonction waitpid(pid_t pid, int
*status, int options)
• valeur de retour :
• -1 : erreur (pas de fils)
• 0 : échec (il n’existe aucun processus fils terminé)
• >0 : pid d'un fils (zombie) s'il existe
• argument pid (sélection processus à attendre) :
– Si le processus fils mentionné par pid s’est déjà terminé avant
l’appel à cette fonction, il deviendra "zombie"
• options :
• 0 : bloquant
• 1 : non bloquant

16
La fonction exit(int status)
• un appel à la primitive exit() provoque la terminaison du processus
effectuant l’appel avec un code retour status.
• Si le processus à des fils lorsque exit est appelé, ils ne sont pas
modifiés mais comme le processus père prend fin, le nom de leur
processus père est changé en 1, qui est l’identifiant du processus
init.
• Le père du processus qui effectue un exit reçoit son code retour à
travers un appel à wait.
• Ce code est
- soit constitué à partir de la valeur passée en paramètre à exit par le
fils (terminaison normale du fils (utiliser WEXITSTATUS(status)))
- soit un code d'erreur fabriqué par le SE (terminaison anormale du
fils) et indiquant la raison de cette terminaison (par exemple
processus tué)
17
Interprétation de la valeur renvoyée par exit
(int status)
• L’interprétation de la valeur récupérée doit, pour des raisons de
portabilité être réalisée par l’intermédiaire de macro-fonctions
prédéfinies dans le fichier d’en-tête <sys/wait.h>.
• Elles sont appelées avec la valeur fournie au retour de l’appel à
wait ou waitpid.
• Exemples:

18
Recouvrement de processus sous Linux
• Le recouvrement de processus permet de remplacer par un autre code le
code exécuté par un processus.
• Le programme et les données du processus sont alors différents, mais
celui-ci garde le même pid, le même père et les même descripteurs de
fichiers.
• C’est ce mécanisme qui est utilisé lorsque, par exemple, nous tapons la
commande ls, ou que nous lançons un programme après l’avoir compilé:
 le terminal de commande dans lequel cette commande est tapée fait un
fork() ;
 le processus ainsi créé (le fils du terminal de commande) est recouvert par
l’exécutable désiré ;
 pendant ce temps, le terminal de commande (le père) attend que le fils se
termine grâce à wait() si la commande n’a pas été lancée en tâche de fond.

19
La fonction exec
• Si on souhaite que le processus fils exécute un autre
programme que le processus père, on utilise une variante de la
fonction exec (qui remplace le programme courant par un
autre programme).
• Exemple :

20
La fonction exec
• Les primitives de recouvrement constituent un ensemble
d’appels système (de type exec*()) permettant à un processus
de charger en mémoire un nouveau code exécutable.
• Le code de remplacement, spécifié comme argument de
l’appel système de type exec*(), écrase le code du processus
en cours (lui même éventuellement hérité au moment de la
création du processus par fork()).
• Des données peuvent être passées au nouveau code exécutable
qui les récupère via les arguments de la fonction exec*()
utilisée.

21
La fonction exec
Il existe 6 primitives de recouvrement de type exec*().
• Les deux fonctions de base sont :
– int execl(const char *path, const char *arg0, const char *arg1, ... , const char
*argn, NULL): path est le chemin d’un exécutable à partir du répertoire courant,
arg0, arg1, ... , argn, NULL est la liste des arguments à passer au nouveau
programme.
– int execv(const char *path, const char *argv[]): Le premier paramètre est le
même que celui d’execl et le deuxième paramètre argv[] est un tableau contenant les
arguments à passer au nouveau programme.

• Nous pouvons utiliser d’autres fonctions telles que :


– int execle(const char *path, const char *arg0, const char *arg1, ... , const char
*argn, NULL, const char *envp[]): même fonctionnement qu’execl() avec en plus le
chargement d’un nouvel environnement. L’argument envp représente un pointeur sur
un tableau de pointeurs sur des chaînes de caractères définissant l’environnement.
– int execve(const char *path, char *const argv[], const char *envp[]) : même
fonctionnement qu’execv() mais elle prend la description de l’environnement comme
argument supplémentaire. 22
La fonction exec
– int execlp(const char *file, const char *arg0, const char *arg1, ...
, const char *argn, NULL): même fonctionnement qu’execl(), mais
si le nom du fichier n’est pas un nom complet, le système utilisera le
chemin de recherche des commandes pour trouver dans quel
répertoire se trouve le programme.
– int execvp(const char *file, const char *argv[]): même
fonctionnement qu’execv(), mais si le nom de fichier n’est pas un
nom complet, le système recherche dans quel répertoire se trouve le
programme.
• Leur différence principale réside dans la manière de passer le code
de remplacement.
• De manière générale, il s’agit d’une chaine de caractères
constituant le chemin vers l’exécutable correspondant.
• L’utilisation de ces primitives nécessitent l’inclusion de la
bibliothèque « unistd.h ». 23
Exemple d’execv()
• int execv(const char *path, char *const argv[]) est une des
primitives de recouvrement.
• Cette fonction recouvre le processus avec l’exécutable indiqué par
la chaîne de caractère path (path est le chemin d’accès à
l’exécutable).
• argv[] est un tableau de chaînes de caractères contenant les
arguments à passer à l’exécutable.
• argv[0] contient la chaine de caractères correspondant au nom du
fichier exécutable,
• argv[1] est la chaine de caractères correspondant au premier
argument, ...
• Le dernier élément du tableau doit contenir NULL.

24
États d'un processus
• Dans un système multitâches, plusieurs processus peuvent se
trouver simultanément en cours d'exécution : ils se partagent
l'accès au processeur, ce qui donne lieu à plusieurs
commutations de contexte.
• Si un système informatique ne comporte qu'un seul processeur,
alors, à un instant donné, un seul processus aura accès à ce
processeur. En conséquence, un programme en exécution peut
avoir plusieurs états.
Remarque :
• La multiprogrammation a été introduite en partie pour éviter
qu'un programme en attente de fin d'entrées-sorties ne
monopolise le processeur.

25
États d'un processus
Les trois principaux états d’un processus sont les suivants :
• Etat actif ou élu (running): le processus est en exécution, il
utilise alors le processeur.
• Etat prêt ou éligible (ready): le processus attend la libération du
processeur pour s’exécuter, il pourrait utiliser le processeur s’il
était libre et si c’est son tour.
• Etat en attente ou bloqué : le processus attend une ressource
physique ou logique autre que le processeur pour s’exécuter
(exemple : fin d'une entrée-sortie).

26
Transitions entre états d’un processus

Les transitions entre états provoquent le passage d'un état à un


autre. 27
Transitions entre états d’un processus
Les changements d’états peuvent avoir lieu dans les différents cas
suivants :
1. Un nouveau processus est créé, son code est chargé en mémoire.
2. Le quantum de temps a expiré ou un processus plus prioritaire a été
admis (réquisition du processeur).
3. Le processus a été sélectionné par l’ordonnanceur (allocation du
processeur).
4. Le processus est en attente d’une ressource.
5. La ressource ou les données deviennent disponibles, donc le
processus est réveillé, il passe dans la file d’attente des processus
prêts.
6. Le processus sort des files d’attente de l’ordonnanceur.

28
Exemple
• L’état d’un processus est défini par l’activité courante de celui-
ci. Les processus peuvent interagir entre eux.
• Exemple:
Commande linux: who | wc -l
• Le premier processus liste les utilisateurs connectés et envoie
ses résultats dans un tube.
• Le second compte le nombre de lignes lui parvenant par le tube.
• Ces deux processus sont lancés de manière concurrente.
• Le processus wc peut être prêt a s’exécuter mais il est obligé
d’attendre les informations provenant du processus who. Dans
ce cas le processus wc se bloque.

29