processus

publicité
Systèmes d’exploitation
Les processus
Introduction
Concept de processus est le  important dans un SE
Processus = abstraction d’un programme en cours d’exécution
Tous les ordinateurs modernes peuvent exécuter plusieurs tâches à la fois
(ex : programme en cours de calcul + affichage sur un terminal + impression)
MAIS le processeur, à un instant donné, n’exécute réellement qu’un seul
programme
Dans un système multiprogrammé, le processeur passe d’un programme à un autre
en exécutant chaque programme pendant quelques dizaines de millisecondes 
impression de parallélisme
Ce parallélisme est qualifié de pseudo-parallélisme
Attention : ne pas confondre avec le parallélisme qui s’effectue au niveau matériel
(nécessite plusieurs processeurs)
Le contrôle de plusieurs activités en pseudo-parallélisme est une tâche difficile qui a
fait l’objet de nombreuses améliorations pour obtenir le modèle actuel
Le modèle des processus (1)
Un processus est un programme qui s’exécute et qui possède son
compteur ordinal, ses registres et ses variables
Conceptuellement, chaque processus a son propre processeur virtuel
En réalité, le processeur commute entre plusieurs processus
Cette commutation rapide est appelée multiprogrammation
Un seul compteur ordinal
A
B
4 compteurs ordinaux
Commutation
de processus
C
D
Multiprogrammation
de 4 processus
A
C
D
B
Modèle conceptuel de
4 processus séquentiels
indépendants
processus
D
—
—
C
—
—
B
—
—
A —
—
Temps
Un seul programme
actif à un instant donné
Le modèle des processus (2)
Comme le processeur commute entre les processus, la vitesse
d’exécution d’un processus ne sera pas uniforme et variera
vraissemblablement si les mêmes processus sont exécutés à nouveau
Attention : Ne pas confondre programme et processus
(ex : confection d’un gâteau et piqûre d’abeille)
L’idée clé est qu’un processus est une activité d’un certain type qui
possède un programme, des données, en entrée et en sortie, ainsi
qu’un état courant
Un seul processeur peut être partagé entre plusieurs processus en se
servant d’un algorithme d’ordonnancement qui détermine quand il faut
suspendre un processus pour en servir un autre
Hiérarchie entre les processus
Les SE qui font appel au concept de processus doivent permettre de
créer et détruire dynamiquement les processus
2 exemples : Unix et MS-DOS
Unix :
– Les processus sont créés par l’appel système fork
– Le fork crée une copie conforme du processus appelant
– À la suite du fork, le processus père continue à s’exécuter en « parallèle »
avec son fils
– Le processus père peut créer d’autres fils et ces processus fils peuvent euxmêmes avoir des fils  Arborescence de processus  hiérarchie
MS-DOS :
– Un appel système pour charger un fichier binaire en mémoire et l’exécuter en tant
que processus fils
– Contrairement à Unix, MS-DOS suspend le père jusqu’à ce que le fils ai terminé
son exécution  pas de pseudo-parallélisme
Les différents états d’un processus
Les processus, bien qu’étant des entités indépendantes doivent parfois interagir avec
d’autres processus (ex : cat fic1 fic2 | grep mot)
L’ordre et la vitesse d’exécution des processus peut amener un des processus à se
bloquer (ex : le grep est prêt à s’exécuter mais ne peut le faire faute de données)
Un processus se bloque lorsqu’il ne peut pas, pour une raison logique, poursuivre son
exécution (ex : attente de données)
Un processus élu peut être arrêté et mis en situation d’attente (on dit qu’il est prêt),
même s’il peut poursuivre son exécution (ex : le SE décide d’allouer le processeur à
un autre processus)
Ces 2 situations sont totalement différentes :
– Dans le premier cas, la suspension est inhérente au problème
– Dans le second cas, il s’agit d’une caractéristique technique du système
1. Élu (en cours d’exécution)
3 états possibles : 2. Prêt (suspendu pour permettre l’exécution d’un autre processus)
3. Bloqué (attendant un événement extérieur pour pouvoir continuer)
Les transitions entre les états
4 transitions peuvent avoir lieu entre les états
Élu
1
2
1. Le processus se bloque en attente de données
2. L’ordonnanceur choisit un autre processus
3
3. L’ordonnanceur choisit le processus initial Bloqué
Prêt
4
4. Les données deviennent disponibles
La transition 1 se produit lorsqu’un processus ne peut plus poursuivre son exécution
La transition 2 a lieu lorsque l’ordonnanceur décide que le processus en cours s’est
exécuté pendant une durée suffisante ; cette opération est appelée réquisition
La transition 3 se produit lorsque tous les processus ont obtenu leur quota de temps
et qu’il faut relancer le premier processus
La transition 4 est réalisée si l’évènement extérieur attendu par un processus se produit
Les transitions 2 et 3 sont provoqués par l’ordonnanceur de processus (module du SE)
Le rôle de l’ordonnanceur est très important : il choisit le processus à exécuter ainsi
que le moment où il faut le lancer ; il constitue le niveau le plus bas du SE
Il existe  stratégies d’ordonnancement, qui seront étudiées dans la suite du cours
Le modèle des processus (suite)
Le modèle des processus permet de mieux comprendre ce qui se
passe à l’intérieur du SE :
– Une partie des processus exécutent les programmes des utilisateurs
– Les autres processus font partie du système et gèrent les tâchent telles que les
requêtes au gestionnaire de fichiers ou les opérations sur disques
Processus
n-1 n
0 1
…
Le nouveau modèle obtenu :
Ordonnanceur (scheduler)
L’ordonnanceur constitue le niveau le + bas du SE
Il est surmonté d’une multitude de processus
La gestion des interruptions, la suspension et la relance des processus
sont déportées dans l’ordonnanceur
Le reste du SE est structuré sous forme de processus
La réalisation des processus
Pour mettre en œuvre pratiquement le modèle de processus, le SE a une table
(tableau de structures) : la table des processus (TP) dont chaque entrée correspond à
un processus particulier
Chaque entrée comporte des informations sur :
–
–
–
–
–
–
l’état du processus
son compteur ordinal
son pointeur de pile
son allocation mémoire
l’état des fichiers ouverts
… et tout ce qui doit être sauvé lorsqu’un processus passe de l’état élu à l’état prêt
Le contenu de la table des processus varie d’un système à un autre
En général, les informations concernent la :
– La gestion du processus
– La gestion mémoire
– La gestion des fichiers
La table des processus Unix
Quelques champs typiques de la table des processus
Gestion des processus
Gestion de la mémoire
Gestion des fichiers
Registres, compteur ordinal
Mot d’état du programme
Pointeur de pile
État du processus
Date de lancement du processus
Temps CPU utilisé
Temps CPU des fils
Date de la prochaine alarme
Pointeurs sur files de message
Bits des signaux en attente
Identificateur de processus (pid)
Pointeur sur le segment de code
Pointeur sur segment de données
Pointeur sur segment BSS
Statut de fin d’exécution
Statut de signal
Identificateur de processus (pid)
Masque UMASK
Processus père
Groupe du processus
uid réel, uid effectif
gid réel, gid effectif
Paramètres des appels systèmes
Divers indicateurs
Divers indicateurs
Table de bits des signaux
Divers indicateurs
Répertoire racine
Répertoire de travail
Descripteurs de fichiers
uid effectif
gid effectif
Les processus Unix
Un processus Unix se décompose en :
– Un espace d’adressage (visible par l’utilisateur/le programmeur)
– Le bloc de contrôle du processus (BCP) lui-même décomposé en :
• Une entrée dans la TP du noyau struct proc définie dans <sys/proc.h>
• Une struct user appelée zone u définie dans <sys/user.h>
Les processus Unix apportent
– La multiplicité d’exécution (plusieurs processus peuvent être l’exécution d’un même prog.)
– La protection des exécutions (un processus ne peut exécuter que ses instructions propres
et ce de façon séquentielle)
Les processus Unix communiquent entre eux et avec le reste du monde grâce aux
appels systèmes
P1
noyau
P5
la TP est interne au noyau :
TP
P5
P1
les processus
Création d’un processus Unix
Cette création est réalisée par l’appel système :
int fork(void);
Chaque processus est identifié par un numéro unique, son PID
Tous les processus (sauf le processus de pid=0) sont créés par un appel à fork
Le processus qui appelle le fork est appelé processus père
Le nouveau processus est appelé processus fils
Tout processus a 1 seul père mais peut avoir 0 ou plusieurs fils
Le processus de pid=0 est créé « manuellement » au démarrage de la machine
–
–
–
ce processus joue un rôle spécial pour le système (swappeur,gestionnaire de pages)
pour le bon fonctionnement des prog. utilisant fork, ce processus reste toujours utilisé
ce procéssus crée grâce à un appel fork, le processus init de pid=1
Le processus de pid=1 est l’ancêtre de tous les autres processus (le processus 0
ne réalisant plus de fork)
–
il a notamment pour rôle de recueillir tous les processus orphelins de père (ceci afin de
collecter les informations à la mort de chaque processus)
Format d’un fichier exécutable
Les compilateurs permettent de créer des fichiers exécutables
Ces fichiers ont un format particulier qui permet au noyau de les
transformer en processus
–
Une en-tête qui décrit l’ensemble du fichier, ses attributs et sa carte des
sections
Une section TEXT qui contient le code (en langage machine)
Une section DATA codée en langage machine qui comporte 2 parties :
–
–
•
•
–
Une partie pour les données initialisées
Une autre pour les données non initialisées (BSS)
Éventuellement d’autres sections:
•
•
•
•
•
Table des symboles pour le débuggeur
Images
Icones
Tables des chaînes
…
Chargement/changement d’un
exécutable
L’appel système execve change l’exécutable du processus courant en chargeant un
nouvel exécutable
Pour chaque section, de l’exécutable une région mémoire est alloué;
soit au moins :
– les régions code et données initialisées
– mais aussi les régions des piles et du tas
La pile est une pile de structures qui sont empilées et dépilées lors de l’appel ou le
retour de fonction
– le pointeur de pile, un des registres de l’UC donne la profondeur courante de la pile
– le code du programme gère les extensions de pile, c’est le noyau qui alloue l’espace
nécessaire à ces extensions
– un processus Unix pouvant s’exécuter dans 2 modes (noyau, utilisateur), une pile privée
sera utilisée dans chaque mode
Le tas est une zone où est réalisée l’allocation dynamique avec les fonctions :
Xalloc()
Structure interne des processus
Adresse haute =
0xFFFFFFFF
argc, argv, envp
Tas


Pile
Données non initialisées (BSS)
initialisé par exec
Données initialisées
Texte
Adresse basse = 0
lu par exec
zone u et table des processus
Tous les processus sont associés à une entrée dans la TP qui interne au noyau
De +, le noyau alloue pour chaque processus une structure appelée zone u qui
contient les données privées du processus (uniquement manipulable par le noyau)
La TP, permet d’accéder à la table des régions par processus (TRP)
Ce double niveau d’indirection permet le partage des régions
Dans une organisation en mémoire virtuelle, la TRP est matérialisée logiquement
dans la table des pages
Les structures de régions dans TRP contiennent des infos sur le type, les droits
d’accès et la localisation (adresses en mémoire ou adresses sur disque) de la
région
Seule la zone u du processus courant est manipulable par le noyau, les autres sont
inaccessibles
L’adresse de la zone u est placée dans le mot d’état du processus
Le contexte d’un processus
Le contexte d’un processus est l’ensemble des données qui permettent de reprendre
l’exécution d’un processus qui a été interrompu
Il est constitué de :
–
–
son état
son mot d’état, en particulier :
•
•
–
–
–
–
–
la valeur des registres actifs
le compteur ordinal
les valeurs des variables globales statiques ou dynamiques
son entrée dans la TP
sa zone u
les piles utilisateurs et systèmes
les zones de code et de données
Le noyau et ses variables ne font partie du contexte d’aucun processus !
L’exécution d’un processus se fait dans son contexte
Quand il y a changement de processus courant, il y a réalisation d’une commutation
de mot d’état et d’un changement de contexte
Contexte d’unité centrale
Fonctions de très bas niveau, fondamentales pour pouvoir programmer un SE
Pour être exécuté et donner naissance à un processus, un programme et ses
données doivent être chargées en mémoire centrale de l’UC
L’UC comprend des circuits logiques qui effectuent les instructions mais aussi des
mémoires appelés registres :
– L’accumulateur (reçoit le résultat d’une instruction)
– Le registre d’instruction (contient l’instruction en cours)
– Le compteur ordinal (adresse de l’instruction en mémoire)
– Le registre d’adresse
– Les registres de données (utilisées pour lire/écrire une donnée à une adresse spécifiée)
– Les registres d’état du processeur (actif, mode (user/sys), vecteur d’interruptions, …)
– Les registres d’état du processus (droits, adresses, priorités, …)
Ces registres forment le contexte d’unité centrale d’un processus
Commutation de mot d’état
Pour pouvoir exécuter un nouveau processus, il faut
sauvegarder le contexte d’unité centrale du processus courant
(mot d’état), puis charger le nouveau mot d’état
Cette opération est appelée commutation de mot d’état
Cette commutation doit se faire de manière non interruptible !
Cette « super-instruction » utilise 2 adresses :
– l’adresse de sauvegarde du mot d’état
– l’adresse de lecteur du nouveau mot d’état
Le compteur ordinal faisant partie du mot d’état, ce changement
provoque l’exécution dans le nouveau processus
Les fonctions setjmp/longjmp permettent de sauvegarder
et de réinitialiser le contexte d’unité central du processus
courant, en particulier le pointeur de pile
Les interruptions
Une interruption est une commutation de mot d’état provoquée par un
signal produit par le matériel
Ce signal est la conséquence d’un évènement extérieur ou intérieur, il
modifie l’état d’un indicateur qui est régulièrement testé par l’UC
Une fois le signal détecté, on utilise le vecteur d’interruptions pour
identifier la cause et réaliser la tâche demandée
3 grands types d’interruptions :
– Externes (indépendantes du processus) : pannes, interventions de l’utilisateur
– Déroutements : erreur interne du processeur (débordement, div/0, …)
sauvegarde sur disque de l’image mémoire : core dumped
– Appels systèmes : accès disque, demande d’E/S, …
Il existe plusieurs niveaux d’interruptions, ce qui permet au système de
sélectionner l’interruption à traiter en priorité
Les interruptions sous Unix
6 niveaux d’interruptions :
Nature de l’interruption
Fonction de traitement
0 horloge
clockintr
1 disques
diskintr
2 console
ttyintr
3 autres périphériques
devintr
4 appel système
sottintr
5 autre interruption
otherintr
L’horloge est la plus prioritaire dans un système Unix
La cascade d’interruptions !
Si durant le traitement d’une interruption, une autre
interruption se produit et que ceci se répète durant le
traitement de la nouvelle interruption : le système ne fait plus
progresser le processus en cours, ni les interruptions !
Nécessité de pouvoir retarder ou annuler une interruption
 2 mécanismes : le masquage et le désarmement d’un
niveau d’interruption
Masquer = ignorer temporairement un niveau d’interruption
Désarmer = rendre le positionnement de l’interruption
caduque (impossible pour les déroutements)
États d’un processus
(cas d’un système de gestion mémoire par swap)
Listes des états d’un processus :
1.
2.
3.
4.
5.
6.
7.
8.
9.
:Le processus s’exécute en mode utilisateur
Le processus s’exécute en mode noyau
Le processus est prêt
Le processus est endormi en mémoire centrale
Le processus est prêt en zone swap
Le processus est endormi en zone swap
Le processus passe du mode noyau au mode utilisateur mais est préempté (bien que
le processus soit prêt, il est retiré du traitement pour les autres processus progressent)
Naissance d’un processus, ce processus n’est pas encore prêt, il n’est pas encore
endormi, c’est l’état initial de tous processus sauf le swappeur
Zombie : le processus vient de réaliser un exit, il apparaît uniquement dans la TP où il
est conservé le temps que son père récupère le code de retour et d’autres informations
de gestion (ex : tps d’exécution)
L’état « zombie » est l’état final des processus, il reste dans cet état jusqu’à ce que
le père lise leur valeur de retour (status) à l’aide l’appel système wait()
Diagramme d’état des processus
Exécution en mode utilisateur
Appel système interruption
1
Examiner et
traiter les signaux
Gestion interruption
2
Exécution en mode noyau
exit
sleep
Examiner
les signaux
Zombie 9
Bloqué (endormi)
en mémoire 4
Mémoire centrale
Swap
Retour au mode utilisateur
préemption
Ordonnancement
du processus
7
3
wakeup
swapin
Préempté
Prêt en mémoire
Mémoire
suffisante
Création
swapout
fork
8
swapin
6
5
Bloqué en zone swap
Prêt en zone swap
Mémoire
insuffisante
La TP Unix
La TP est localisée dans la mémoire du noyau
TP = Tableau de struct proc (<sys/proc.h>)
Cette structure contient les informations qui doivent toujours être accessibles par le
noyau :
–
–
–
–
–
–
–
–
–
État (cf diagramme)
Adresse de la zone u
Adresses : taille et localisation en mémoire
pid et ppid : valeurs initialisées en état 8
uid
Événement : descripteur de l’événement attendu lorsque le processus est endormi (bloqué)
Priorités : utilisés par l’ordonnanceur
Vecteurs d’interruptions du processus : ensemble des signaux reçus mais non traités
Divers : compteurs (ex: tps CPU pour faire payer le temps de calcul), alarme
Ces informations peuvent être obtenues à partir de la commande shell ps ou par
consultation du pseudo système de fichiers /proc (et pour certaines d’entre elles,
par différents appels systèmes)
La zone u
La zone u est utilisée lorsque un processus s’exécute (mode noyau ou utilisateur)
Une unique zone u est accessible à la fois : celle du processus en cours d’exécution
(états 1 et 2)
La zone u est de type struct user définie dans <sys/user.h>, elle contient :
–
–
–
–
–
–
–
–
–
–
–
Pointeur sur la structure de processus de la TP
uid réel et effectif : détermine les privilèges donnés au processus
Compteurs de temps (user et system)
Terminal : terminal de contrôle du processus (s’il existe)
Erreur : dernière erreur rencontrée pendant un appel système
Retour : valeur de retour du dernier appel système
E/S : structures associées au E/S
Répertoires courant et racine (cf. chroot())
Table des descripteurs
Limites (cf. ulimit en Bourne Shell et limit en csh)
umask : masque de création de fichiers
Ces informations ne sont accessibles que par une réponse du processus lui-même et
donc par l’utilisation d’appels systèmes !
Les informations temporelles
#include <sys/time.h>
clock_t times (struct tms * buffer);
times remplit la structure pointée par buffer avec des informations sur le temps
machine utilisé en état 1 et 2
Struct tms {
clock_t tms_utime;
clock_t tms_stime;
clock_t tms_cutime;
clock_t tms_csutime;
};
/*
/*
/*
/*
user time */
system time */
user time, children */
system time, children */
Contient des temps indiqués en microsecondes, la précision dépend de l’ordinateur
Changement des répertoires
#include <unistd.h>
int chroot(const char* path);
Permet de définir un nouveau point de départ pour les
références absolues (commençant par /).
La référence .. de ce répertoire racine étant associée à luimême, il n’est donc pas possible de sortir du sous-arbre défini
par chroot
Les appels suivants permettent de changer le répertoire de
travail du processus :
– int chdir(const char* path);
– int fchdir(int fd);
Récupération du pid
#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);
pid_t getpgrp(void);
pid_t getpgrp2(pid_t pid);
L’appel à getpid retourne le pid du processus courant
getppid, le pid du processus père
getpgrp, le pid du groupe du processus courant
getpgrp2, le pid du groupe du processus pid
(si pid=0, alors identique à getpgrp)
Identification de l’utilisateur
L’uid d’un processus est l’identification de l’utilisateur exécutant le processus
Le système utilise 3 uid :
1.
2.
3.
euid : uid effective utilisé pour les tests d’accès
ruid : uid réelle
suid : uid sauvegardée, pour pouvoir revenir en arrière
#include <sys/types.h>
#include <unistd.h>
int setuid(uid_t uid)
Fonctionnement :
–
–
–
si euid=0 (root) alors les trois uid sont positionés à la valeur de uid,
sinon si uid=ruid ou suid alors euid prend la valeur uid (ruid et suid ne changent pas),
sinon RIEN ! (pas de changements)
La commande setreuid permet de changer le ruid, elle est utilisée
pendant le login, seul le super utilisateur peut l’exécuter avec succès
Pour les goupes : int setgid(gid_t gid)
Tailles limites d’un processus
#include <unistd.h>
long ulimit(int cmd, …);
La commande cmd est :
– UL_GETFIZE : retourne la taille maximum des fichiers en blocs
– UL_SETFSIZE : positionne cette valeur avec le deuxième argument
– UL_GETMAXBRK : valeur maximale pour l’appel d’allocation dynamique
de mémoire : brk.
Ces valeurs sont héritées du processus père
Remarque : cet appel système n’est pas implémenté dans les
noyaux 2.0 des systèmes Linux
Manipulation de la taille
du segment de données
#include <unistd.h>
int brk(void* endds);
void* sbrk(ptrdiff_t incr);
brk positionne la fin du segment de données à l’adresse spécifiée
endds doit être supérieur à la fin du segment de texte et 16ko avant la fin de
la pile
sbrk incrémente l’espace de données du programme de incr octets
sbrk n’est pas un appel système, juste une fonction de la bibliothèque C
Remarques :
– brk et sbrk ne sont pas définis dans le C standard et sont volontairement
exclus des standards POSIX
– Il ne faut pas les utiliser conjointement aux fonctions d’allocation standard :
malloc, calloc, realloc, free
Manipulation de la valeur de nice
La valeur de nice d’un processus indique la priorité du processus
pour l’ordonnancement
Plus la valeur est petite, plus le processus est prioritaire
La valeur de de nice est comprise entre 0 et 39
Seul le super utilisateur peut spécifier une valeur de nice négative
#include <unistd.h>
int nice(int valeur);
La commande shell renice permet de changer le « nice » d’un
processus actif
Manipulation de la valeur umask
L’appel umask permet de spécifier quels droits doivent être interdits en cas de
création de fichiers
#include <unistd.h>
int umask(int mask);
umask fixe le masque de création de fichiers à la valeur :
mask & 0777
Les bits contenus dans le masque sont éliminés de la valeur 0666 pour créer
les nouvelles permissions
ex : mask=0022  0666 & ~0022 = 0644 = rw-r--r--
Cet appel système n’échoue jamais et la valeur précédente du masque est
renvoyée
La commande umask existe également en shell
L’appel système fork
L’appel système fork permet la création d’un processus clône du processus courant.
#include <unistd.h>
pid_t fork(void);
DEUX valeurs de retour en cas de succès !
– Dans le processus père, la valeur de retour est égale au pid du fils
– Dans le processus fils, la valeur de retour est égale à zéro
Si échec (seul le processus père existe), la valeur de retour est égale à –1
Les pid et ppid sont les seules informations différentes entre les deux processus
Les deux processus poursuivent leur exécution à l’instruction qui suit le fork
Utilisation courante : pid_t pid;
pid=fork();
if (pid>0) {/* processus père */ }
else if (pid==0) {/*processus fils*/}
else {/* Traitement d’erreur */}
Les appels systèmes exec (1)
#include <unistd.h>
int
int
int
int
int
int
execl(char* path,char* arg0,char* arg1, …, NULL);
execv(char* path,char* arg[]);
execle(char* path,char* arg0,…, NULL, char* envp[]);
execve(char* path,char* arg[],char* envp[]);
execlp(char* file,char* arg0,char* arg1, …, NULL);
execvp(char* file,char* arg[]);
Toutes ces fonctions sont « identiques » et diffèrent simplement par le mode de
passage des paramètres
Les différents noms des fonctions exec sont mnémoniques :
–
–
–
–
l : liste d’arguments
v : arguments sous forme de vecteur
p : recherche du fichier avec la variable d’environnement PATH
e : transmission d’un environnement en dernier paramètre, en remplacement de
l’environnement courant
Les appels systèmes exec (2)
Primitives
Format d’argument
execl
execv
execle
execve
execlp
execvp
Liste
Tableau
Liste
Tableau
Liste
Tableau
Passage
d’environnement
Automatique
Automatique
Manuel
Manuel
Automatique
Automatique
Recherche
avec PATH
Non
Non
Non
Non
Oui
Oui
La famille des fonctions exec remplace l’image mémoire du processus en cours
par un nouveau processus.
Informations conservées par le processus : pid, ppid, pgid, ruid, suid,
euid, nice, groupe d’accès, répertoires courant et racine, terminal de contrôle,
utilisation et limites des ressources, umask, masques des signaux, signaux en
attente, table des descripteurs de fichiers, verrous, session
Valeur de retour : en cas de succès AUCUNE le code ayant fait l’exec n’existe
plus, en cas d’échec –1
Lancement d’une commande
#include <stdlib.h>
int system(const char * commande);
Crée un nouveau processus « /bin/sh » qui exécute la
commande (/bin/sh –c commande)
Le processus appelant cette fonction reste bloqué jusqu’à la fin
de l’exécution du processus
Ce mécanisme est très coûteux et n’est pas un appel système
La valeur de retour est 127 si l’appel système execve pour
/bin/sh échoue, -1 si une autre erreur, ou le code de retour
de la commande sinon
Terminaison d’un processus
_exit : primitive de terminaison de processus bas niveau
#include <unistd.h> void _exit(int valeur);
–
–
–
–
elle ferme les descripteurs ouverts par open, opendir ou hérités du processus père
la valeur est fournie au processus père qui la récupère après l’appel système wait
cette valeur est le code de retour de processus en shell
cette primitive est automatiquement appelée à la fin de la fonction main (sauf en cas
d’appels récursifs de main)
Exit : fonction de terminaison de processus de stdlib
#include <stdlib.h> void exit(int valeur);
–
–
–
–
elle lance les fonctions définies par atexit
ferme l’ensemble des descripteurs ouverts grâce à la bibliothèque standard (fopen)
détruit les fichiers fabriqués par la primitive tmpfile
appelle _exit avec valeur
La primitive atexit
Cette primitive (non système) permet de spécifier des fonctions à appeler en
fin d’exécution, elles sont lancées par exit dans l’ordre inverse de leur
positionnement par atexit
#include <stdlib.h>
void atexit(void (*fonction) (void));
Exemple :
void bob(void){printf(“coucou\n”);}
void bib(void){printf(“cuicui ”);}
Console shell
main(int argc)
$ gcc –o atexit atexit.c
{
$ atexit
atexit(bob);atexit(bib);
$ atexit unargument
if (argc – 1) exit(0);
cuicui coucou
else _exit(O);
$
}
Attente de la mort d’un fils
#include <sys/types.h> #include <sys/wait.h>
pid_t wait (int* status)
pid_t waitpid(pid_t pid, int* status, int options)
La fonction wait suspend l’exécution du processus courant jusqu’à ce qu’un fils se termine
(ou jusqu’à réception d’un signal)
Si un processus fils est déjà mort (il est zombie) la fonction revient immédiatement
Toutes les ressources utilisées par le fils sont libérées
La fonction waitpid suspend l’exécution du processus courant jusqu’à ce que le processus
fils de numéro pid se termine (ou jusqu’à réception d’un signal)
La valeur de pid peut être :
–
–
–
–
< -1 : attente la fin de n’importe quel processus fils appartenant au groupe d’ID pid
-1 : attendre n’importe quel fils (identique au wait)
0 : attendre la fin de n’importe quel processus fils du même groupe appelant
> 0 : attendre la fin du processus de numéro pid
  options, la plus utile : WNOHANG qui permet de ne pas bloquer si aucun fils n’est mort
Si status  NULL, les informations sur la terminaison du fils y sont stockées
– WIFEXITED(status) : est non nul si le fils s’est terminé normalement
– WEXITSTATUS(status) : donne le code retour du fils tel qu’il l’a mentionné lors de l’appel
_exit(retour)[valide uniquement si WIFEXITED(status)0]
Gestion des erreurs
Dès lors que l’on effectue des appels systèmes, il est important de contrôler
TOUTES les valeurs de retour
Nous sommes aidés dans cette tâche par la bibliothèque C <errno.h>
#include <errno.h>
perror(const char *s)
La fonction perror affiche un message sur la sortie standard des erreurs,
décrivant la dernière erreur rencontrée durant un appel système
La chaîne s est imprimée en premier suivie d’un : et du message d’erreur
approprié
Le numéro de l’erreur est obtenu à partir de la variable externe errno qui
contient le code d’erreur lorsqu’un problème survient, mais qui n’est PAS
effacé lorsqu’un appel réussi
Téléchargement