Vous êtes sur la page 1sur 49

Système d’exploitation 2

• Communication entre processus

•Modèle de Représentation de Processus

• Synchronisation des Processus

Multi-tâches, Multi-processeurs: exécution de plusieurs programmes en même


temps sur un ou plusieurs processeurs
Support des communications Inter-processus (tubes, IPC, socket)
Gestion des signaux
Les processus

2
Introduction
• Un processus est l'abstraction d'un programme exécute en mémoire.
• Les processus sont des entités a priori indépendantes.
• Chaque processus dispose de ses données et de sa pile d’exécution est exécute un code
non modifiable qui peut être partagé.

Processus 1 Processus 2 Processus N

code code code


Données Données Données

Adresse virtuelles Adresse Adresse


non utilisées virtuelles ……. virtuelles
non utilisées non utilisées

Pile Pile Pile

3
Les informations attachees a un processus
L'OS dispose de renseignement concernant un processus :
• le pid i.e. numéro identificateur du processus ;
• le ppid i.e. numéro identificateur du processus père ;
• l'uid i.e. numéro identificateur du propriétaire du processus ;
• le numéro du groupe auquel appartenait le propriétaire du processus (il
peut appartenir a plusieurs groupe) ;
• l'heure et la date de création du processus ;
• des statistiques sur les ressources (processeur, etc.) utilisées ;
• la taille mémoire actuellement utilisée par le processus (en distinguant
chaque composante : swap, tas, code, données, etc).
Mais aussi,
• un masque indiquant la sensibilité aux signaux ;
• son état (en cours d‘exécution, zombie, arrête, suspendu, etc.) ;
• la priorité d'exécution ;

4
Les informations attachees a un processus
• Chaque processus est représente dans l'OS par un bloc de contrôle de processus
contenant les informations suivantes :
• l‘état du processus (nouveau, prêt, en cours d'exécution, en attente, arrête,
etc.) ;
• les numéros associes au processus ;
• l‘état des registres du microprocesseur associes au processus et notamment :
– les différents numéros de segments (mémoire, code, pile, etc.) ;
– un pointeur sur la prochaine instruction a exécuter ;
– les registres a usage généraliste.
– la liste des fichiers ouvert et l'ensemble des informations associées aux
entrées-sorties ;
– des statistiques sur l'utilisation des ressources de la machine (temps
d'occupation du processeur, etc.) ;
– les limites maximales de la mémoire (début et fin des ces espaces).

5
Une vision abstraite d'un processus
• Son graphe de transitions d‘état

Etat prêt : Exécution suspendue ;


Etat actif : Exécution effective du programme ;
Etat bloque : Exécution bloquée sur condition logique.
6
Primitives de gestion des processus
• Créer un processus :
– CreateProcess de Windows,
– fork d'Unix
• Terminer un processus :
– ExitProcess de Windows,
– exit d'Unix
• Bloquer/debloquer un processus :
– masquées dans d'autres primitives
• Commuter de processus.

7
Clonage d'un processus : l'appel systeme fork
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h> /* La structure de ce code est typique du */
#include <unistd.h> /* clonage d'un processus. */
#include <stdlib.h>
int main(void){
int status,pid = 0 ;
pid = fork() ; /* L'appel système permettant le clonage */
if (pid == 0){
printf("fils :%d\n",getpid()) ; /* le code du processus fils */
exit(1) ; /* un appel permettant la terminaison de ce processus */
}
else { /* le code du processus père qui utilise */
printf("pere :%d\n",getpid()) ; /* un appel pour connaitre son pid */
printf("mon fils :%d\n",pid) ;
wait(&status) ; /* Un appel permettant au père attend la mort de son fils avant de terminer. */
exit(++status);
}
return 0 ; /* cette instruction n'est jamais exécutée */
}
8
Clonage d'un processus : l'appel systeme fork
L'appel pid_t fork(void); provoque le clonage du pere en fils :
les segments de mémoire sont dupliques (code, donnée, etc) ;
cela implique que père et fils partagent les données définies avant le
clonage (variables mais aussi flux ouvert, etc.) ;
l'exécution des processus père et fils poursuivent leurs exécutions
juste après le clonage.
L'appel pid_t getpid(void); (resp. pid_t getppid(void);) retourne
l'identificateur (resp. du père) du processus courant.
L'appel void exit(int status); provoque la terminaison normale du
processus et retourne l'entier status au père. Dans l'exemple
précédant le fils envoit 1 au père qui retourne 2 au shell.
L'appel pid_t wait(int *status); force le père a attendre la
terminaison d'un processus de sa parente (qui lui envoi un signal
stocke dans l'espace pointe par status) dont le pid est retourne par
l'appel.

9
Exemples 1
main () Le processus père exécutera le bloc 1. Puis il
{ créera un fils identique par fork et exécutera le
bloc 3 puisque fork lui retournera une valeur
int pid_fils; non nulle.
bloc1
if ((pid_fils = fork()) == 0) Le processus fils n'exécutera pas le bloc 1 car à
sa création, son compteur ordinal pointera sur
bloc2
la ligne contenant fork. Comme fork lui
else retourne 0, il exécutera le seul bloc2.
bloc3
}

L'affichage des résultats des blocs 2 et 3 peut être entrelacé, et pas nécessairement
de façon identique d'une exécution à une autre.

10
Exemples 2
main () Le processus père exécutera le bloc 1. Puis il
{ créera un fils identique par fork et exécutera le
bloc 2 puisque fork lui retournera une valeur
int pid_fils; non nulle.
bloc1
if (fork()) Le processus fils n'exécutera pas le bloc 1 car à
sa création, son compteur ordinal pointera sur
bloc2
la ligne contenant fork. Comme fork lui une
else valeur non nulle, il exécutera le seul bloc3.
bloc3
}

L'affichage des résultats des blocs 2 et 3 peut être entrelacé, et pas nécessairement
de façon identique d'une exécution à une autre.

11
Exemples 3
main ()
{
int m,n;
printf ("processus père. Fils non encore créé\n");
if (fork () == 0)
{
printf ("pid du fils %d\n", getpid());
exit (1);
}
else {
printf ("pid du père %d\n", getpid());
m = wait (&n);
printf ("fin du processus de pid %d avec valeur de retour de wait %d\n",m,n);
}
}

12
Mutation d'un processus : la famille d'appels
systeme exec
• Un processus existant peut subir une mutation par un appel
système en un processus construit a partir d'un exécutable
existant.
#include <unistd.h>
int main(void) {
execl("/bin/ls","ls",NULL) ;
return 1 ; /* Cette commande n'existe plus du fait de la mutation */
}

13
Mutation d'un processus : la famille d'appels
systeme exec
La famille des fonctions de recouvrement (exec)
• Il s'agit d'une famille de 6 fonctions permettant le lancement de
l'exécution d'un nouveau programme par un processus. Le segment de
texte est remplacé par celui du nouveau programme. Il y a recouvrement
en mémoire, mais pas de création d'un nouveau processus.
• Le retour est -1 en cas d'erreur.
• Les 6 fonctions sont execl, execv, execle, execve, execlp, execvp.

• exemple : int execv (char *nom, char * argv [])


– nom : pointeur sur le nom du programme à exécuter,
– argv : liste de pointeurs (terminée par NULL) sur les arguments transmis au
nouveau programme avec argv[0] : "nom" du programme pointé par nom

14
Mutation d'un processus : la famille d'appels
systeme exec
On peut les répartir en deux groupes :
• les primitives execl pour lesquels le nombre d'arguments du
programme lance est connu. Les arguments sont passes sous forme
de liste ;
– int execlp(const char *file, const char *arg, ...);
– int execle(const char *path, const char *arg , ..., char * const envp[]);
• les primitives execv pour lesquels le nombre d'arguments ne l'est
pas. Les arguments sont passes sous formes de tableau.
– int execv(const char *path, char *const argv[]);
– int execvp(const char *file, char *const argv[]);

15
Quelques règles régissant la communauté des
processus

• Au démarrage de la machine un premier processus est créer


par le super utilisateur. Ce processus porte le nom init et le
pid 1.
• Le processus init ne se termine qu'a l'extinction de la
machine.
• Si le père disparaît avant ses fils, l'OS fait adopter les
processus orphelins par le processus init. Un processus ne sait
que son père a disparu qu'a sa propre terminaison.
• Un processus fils crée un nouveau groupe en se retirant du
groupe auquel il appartient.

16
Quelques règles régissant la communauté des
processus
• L'appel system uid_t getuid(void) (resp. int setgid(gid_t gid);)
permet de déterminer le propriétaire (resp. le groupe) du
processus.
• L'appel système int setuid(uid_t uid) (resp. int setgid(gid_t
gid);) permet de modifier le propriétaire (resp. le groupe) d'un
processus.
• Le système d'exploitation lance un grand nombre de processus
dont la fonction est d'assurer les tâches de gestions, par
exemple :
– cups est un gestionnaire d'impression ;
– xscreensaver un gestionnaire pour économiser des
ressources ;
– gpm un gestionnaire de la sourie.
17
Les étapes de la mort d'un processus

L'appel système exit permet de terminer un processus :


• l'ensemble des descripteur de fichiers ouvert sont fermes ;
• un entier status est envoyé au père. Cet entier peut être récupéré par
ce dernier grâce a l'appel système wait. Cet appel bloque l‘exécution
du père.

#include <stdlib.h> #include <sys/types.h>


void exit(int status); #include <sys/wait.h>
pid_t wait(int *status);

18
Les étapes de la mort d'un processus
• Considérons le code

#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void){
int res = 0, status = 0 ;
: res = fork() ; /* clonage */
if(!res) exit(status) ; /* le fils termine */
else wait(&status) ; /* le père attend la fin du fils */
return 0 ;
}

19
Les étapes de la mort d'un processus
• A la terminaison d'un processus fils, un lien avec le père demeure jusqu'a
ce que ce dernier se termine a son tour ou appelle wait.
• L'entrée du processus fils dans la table des processus ne se libère pas
immédiatement a sa terminaison. Bien qu'inactif, le processus fils demeure
dans le système en vue d'un éventuel appel wait. C'est un processus
zombie.

20
Notion de signal
• Un signal est émis par un processus vers un autre (ou un
groupe d’autres) processus de même propriétaire (sauf root).
• Le signal peut être rattrapé par le processus destinataire, ce
qui provoque (en général) un déroutement et le lancement
d’une routine spécifique de traitement.
• Après le traitement, le processus reprend où il a été
interrompu.
• En UNIX : la commande kill permet d’envoyer des signaux.

21
Exercice
• Ecrire un programme qui réalise une chaîne
de n processus, Faire imprimer le numéro de
chaque processus et celui de son père.

22
Communication entre processus

23
Communication entre processus
(Unix)
• La communication entre processus (Inter-Process
Communication, ou IPC) est l’un des aspects les plus
importants (et aussi les plus délicats) de la
programmation de systèmes.

24
Communication entre processus
(Unix)
• Dans Unix, la communication entre processus peut se faire
de plusieurs manières différentes :
• Communication asynchrone au moyen de signaux
• Communication par fichiers ou par tubes (pipes, FIFOs)
• Communication par files de messages
• Communication par mémoire partagée et par sémaphores
• Communication par sockets, utilisés dans les réseaux, mais
aussi en local

25
Signaux
• Un signal est "un message très court" qu’un
processus peut envoyer à un autre processus,
pour lui dire qu’un événement particulier est
arrivé.
• Un signal peut être émis par un processus ou
par le système d’exploitation.
• Le processus pourra mettre en oeuvre une
réponse décidée et pré-définie à l’avance
(handler).

26
Signaux vs interruption

• Un signal est analogue à une interruption : un


processus destinataire réagit à un signal en
exécutant un programme de traitement, ou
traitant (handler).
• La différence est qu’une interruption s’adresse
à un processeur alors qu’un signal s’adresse à
un processus.

27
Signaux!
• Les signaux sont un mécanisme de bas niveau.
Ils doivent être manipulés avec précaution car
leur usage recèle des pièges (en particulier le
risque de perte de signaux).
• Ils sont néanmoins utiles lorsqu’on doit
contrôler l’exécution d’un ensemble de
processus (exemple : le shell) ou que l’on
traite des événements liés au temps.

28
Fonctionnement des signaux
processus p
retour au point où le processus a
été interrompu (sauf si le traitant
a pour effet de tuer le processus)

exécution d’un traitant


(handler) associé au signal

arrivée d’un signal


(du système ou d’un autre processus)

29
Fonctionnement des signaux
• Il existe différents signaux, chacun étant identifié par
un nom symbolique (ce nom représente un entier)
• Chaque signal est associé à un traitant par défaut
• Un signal peut être ignoré (le traitant est vide)
• Le traitant d’un signal peut être changé (sauf pour 2
signaux particuliers)
• Un signal peut être bloqué (il n’aura d’effet que
lorsqu’il sera débloqué)
• Les signaux ne sont pas mémorisés

30
Quelques exemples de signaux

• Il existe NSIG signaux (65 sous linux). Chaque


signal est identifie par un nombre et un nom
symbolique ;

31
Les effets de la réception
de ces signaux sur un
processus sont :
Term : le processus se
termine ;
Core : Term + copie de la
mémoire dans un fichier
core ;
Stop : suspend
l'exécution du processus.

32
États d’un signal
Qu’est-ce qui empêche que le signal soit
immédiatement traité dès qu’il est reçu ?
• Le signal peut être bloqué, ou masqué (c’est à
dire retardé) par le destinataire. Il est délivré
dès qu’il est débloqué
• En particulier, un signal est bloqué pendant
l’exécution du traitant d’un signal du même
type ; il reste bloqué tant que ce traitant n’est
pas terminé

33
Les primitives de gestion des signaux

• L'appel système :

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);

permet d'envoyer le signal sig au processus


d'identicateur pid.

34
Les primitives de gestion des signaux

• La fonction sigaction permet d'indiquer le handler associe a


un signal. Elle utilise la structure sigaction pour spécifier le
comportement d'un signal lors de la délivrance d'un signal.
#include <signal.h>
int sigaction (int sig, const struct sigaction *new_action,
struct sigaction *old_action);
struct sigaction{
void (*sa_handler) ();
sigset_t sa_mask;
int sa_flags; }

35
Exemple 1 : traitement d’une interruption
du clavier
• Il suffit de redéfinir le traitant de SIGINT (qui par défaut tue le
processus interrompu)

void handler(int sig) { /* nouveau traitant */


printf("signal SIGINT reçu !\n");
exit(0);
}
int main() {
Signal(SIGINT, handler); /* installe le traitant */
pause () ; /* attend un signal */
exit(0);
}
36
La communication par tubes

• Les tubes sont un mécanisme de


communication qui permet de réaliser des
communications entre processus sous forme
d'un flot continu d'octets.

37
La communication par tubes
• Un tube est matérialisé par deux entrées de la table
des ouvertures de fichiers, une de ces entrées est
ouverte en écriture (l'entrée du tube), l'autre en
lecture (la sortie du tube).

• Uniquement entre proc. père et fils ou bien de même


père
• Taille mémoire limitée

38
popen() et l’exécution de programmes
distants
• Exemple de commande Unix :
rsh machine com params
– lance sur la machine distante la commande com et
l'on veut récupérer le résultat (sortie standard).

FILE *fp = popen("rsh machine com params","r");

retour = fread(buf,1,nbre_octets,fp);

39
La communication par tubes(c)
• Création de tubes ordinaires (pipes)
Un processus ne peut utiliser que les tubes qu'il a créés
lui-même par la primitive pipe ou qu'il a hérités de
son père grâce à l'héritage des descripteurs à travers
fork() et exec().
# include <unistd.h>
int pipe (int p[2]);
p[0] correspond au descripteur en mode lecture
p[1] correspond au descripteur en mode écriture

40
La communication par tubes
Lecture dans un tube
On utilise l'appel système read.
int nb_lu; /* nb de caractères lus */
nb_lu = read(p[0], buffer, TAILLE_READ);

Ecriture dans un tube


On utilise l'appel système write.
int nb_ecrit; /* nb de caractères écrits */
nb_ecrit = write(p[1], buf, n);

41
Java et les tubes : création

• // créer une sortie puis une entrée


• // reliée à cette sortie… ou l'inverse!

PipedWriter tubout = new PipedWriter();

PipedReader tubin = new PipedReader(tubout);

• //puis création des threads…

42
La communication par sockets
• Un Socket est un point de communication bidirectionnelle entre
applications
• Introduit dans Berkeley Unix 4.2 (BSD) en 1981
• Représentation point à point d’un flux de données Un programme peut
– Écrire des données
– Lire des données

43
Schéma de communication des sockets
Un socket est
associé à un port de
communication pour
différencier les
applications réseau
sur une même
machine

44
Fonctions d’un sockets
• 4 fonctions principales:
• Se connecter à une machine distante
• Envoyer des données
• Recevoir des données
• Fermer la connexion
• Un socket ne peut se connecter qu’à une seule
machine
• Un socket ne peut pas être se reconnecter après la
fermeture de connexion

45
Java pour Socket
L’API java fourni un package java.net pour
l’utilisation des packages
• java.net.Socket pour l’implémentation coté
client
• java.net.ServerSocket pour l’implémentation
coté serveur

46
Instanciation d’un socket
• La création d’un socket se fait à l’aide de l’un de ces
constructeurs
• public Socket(String host, int port) throws
UnknownHostException, IOException
• public Socket(InetAddress address, int port) throws
IOException
• public Socket(String host, int port, InetAddress localAddr,int
localPort) throws IOException
• public Socket(InetAddress address, int port,
InetAddresslocalAddr, int localPort) throws IOException
• Ces constructeurs créent un objet Socket et ouvre
uneconnexion réseau avec la machine distante ‘host’
• Tous ces constructeurs lèvent une exception en cas
d’impossibilité de connexion

47
Connexion d’un socket
• Pour qu’un socket puisse se connecter à une
machine distante, cette dernière doit écouter les
requêtes des clients sur le port spécifié
• les ports TCP d’une machine peuvent aller de 0 à
65535
• Il faut définir au minimum l’adresse de la machine
distante et son port d’écoute pour pouvoir se
connecter
• On peut utiliser les constructeurs pour déterminer
les ports d’écoute d’une machine

48
Quelques services standards

49

Vous aimerez peut-être aussi