Vous êtes sur la page 1sur 93

Chapitre 5

Les Processus :
La Synchronisation avec
les sémaphores

Mr B. SOUICI – Mme L. BOUZAR


© 2018-2019
-1-
1. L’Exclusion Mutuelle
2. Synchronisation des processus avec sémaphores
3. Outils de synchronisation de haut niveau:
Les moniteurs

-2-
Synchronisation des processus avec sémaphores
1. Introduction
2. Défintion
3. Comment exprimer les contraintes de synchronisation ?
4. Spécification de la synchronisation
5. Les problèmes types
6. Exemples
6.1 Allocation d’une imprimante
6.2 Rendez-vous
6.2.1 Rendez-vous de deux processus
6.2.2 Rendez-vous de trois processus
6.2.3 Rendez-vous de n processus
6.3 Le modèle des lecteurs/rédacteurs
6.3.1 Ordre d’accès au fichier est quelconque
6.3.2 Ordre d’accès au fichier est FIFO
-3-
6.4 Communication par variables communes
6.4.1 Définitions
6.4.2 Propriétés de la communication
6.4.3 Schéma général du producteur-consommateur
6.4.4 Producteur-consommateur avec un tampon à un seul élément
6.4.5 Producteur-consommateur avec un tampon à n éléments
6.4.6 Plusieurs producteurs et plusieurs consommateurs
6.4.7 Producteur-consommateur avec un tampon à éléments alloués
dynamiquement
7. La communication interprocessus : IPC Unix System V
7.1 Introduction
7.2 Identification d’une IPC
7.3 Les segments de mémoire partagée
7.3.1 Utilisation d’un segment de mémoire partagée
7.3.2 Fonctions de manipulation des segments de mémoire partagée
7.3.3 Exemple : Création et d’utilisation d’un segment de mémoire
partagée -4-
7.4 Les Sémaphores Unix System V
7.4.1 Utilisation des sémaphores Unix system V
7.4.2 Création d’un ensemble de sémaphores
7.4.3 Initialisation d’un ensemble de sémaphores
7.4.4 Opérations sur un ensemble de sémaphores
7.4.5 Destruction d’un sémaphore
7.4.6 Exemple
7.5 Gestion des IPC avec des commandes shell

-5-
La Synchronisation des processus

1. Introduction
Exemple :
➢ Soient deux Processus concurrents P1 et P2 qui coopèrent pour lire et
imprimer le contenu d’un fichier f.
➢ Le fichier f est composé d’un ensemble d’articles.
➢ On dispose de deux procédures :
• Lire(f, article): permet de lire un article du fichier f et le ranger
dans la zone utilisateur d’adresse « article ».
• Ecrire(article) : permet d’afficher/imprimer le contenu de la zone
utilisateur « article » sur l’écran/l’imprimante.
➢ On dispose en mémoire centrale d’un seul tampon qui est partagé par
les deux processus.
-6-
-7-
tampon T init(‘’’’); /* T: initialiser à vide */

Processus P1 Processus P2
Structure … artcle1; Structure … artcle2;
Début Début
Lire(article1,f);
Tantque (non(fin(f)) faire Tantque (T # ‘’fin’’) faire
Déposer (article1,T); Retirer(article2,T);
Signaler(dépôt); Signaler(retrait);
Lire(article1,f); Ecrire(article2);
Finfaire; Finfaire;
T=‘’fin’’; Fin;
Fin;

-8-
Déposer(article,T) Retirer(article,T)
Début Début
Si tampon T est plein alors Si T est vide alors
Attendre; Attendre;
T = article; Article = T;
Fin; Fin;

1) Comment faire attendre ces processus?


✓ P1 attend que le tampon soit vide.
✓ P2 attend que le tampon soit plein.

2) Comment signaler le dépôt et le retrait d’un article?


-9-
2. Définition
❖ « La synchronisation consiste à définir et à réaliser des mécanismes
assurant le respect des contraintes liées à la progression des
processus.
Ces mécanismes doivent être capable de faire attendre un processus
qui ferait des actions ne respectant pas ces contraintes».

• Un mécanisme de synchronisation (ou technique de synchronisation)


doit être conçu et réalisé indépendamment des vitesses d’exécution
des processus et permet à un processus actif :
✓ de se bloquer (attendre une ressource ou un signal d’un autre
processus);
✓ d’activer un autre processus en lui envoyant un signal.
• Le signal d’activation envoyé par un processus actif peut être
mémorisé ou non.
- 10 -
❖ Synchronisation directe et indirecte
Un mécanisme de synchronisation peut offrir deux types
d’opérations ou de primitives qui permettent à un processus d’agir
de manière directe ou indirecte sur d’autres processus :

✓ Le processus envoie un signal à un autre processus en le


désignant par son identificateur (ou par son nom) : 
Synchronisation directe;

✓ Le processus envoie un signal(sans désigner un processus), un


ou plusieurs processus en attente de ce signal sont réveillés : le
processus ne connaît pas l’identité du ou des processus
réveillés :  Synchronisation indirecte.
Exemple : Opération V sur un sémaphore.

- 11 -
3. Comment exprimer les contraintes de
synchronisation ?
1. Imposer un ordre de précédence dans le temps logique, pour l’exécution
des actions des processus .
Exemples:
a) La précédence
Exécuter l’action i du processus P1 avant l’action j du processus P2.
b) Le rendez-vous
Deux étudiants de fixent un rendez-vous pour travailler ensemble.

Etudiant 1 Etudiant 2
Début Début
. . . . . .
Attendre étudiant 2; Attendre étudiant1;
Travailler ensemble; Travailler ensemble;
Fin; Fin;
- 12 -
La forme d’un programme de rendez-vous
Debut i

. . .
Rendez-vous i; *
* Suite i;
Fin i
* i, j : fin(rendez-vous i) avant début(suite j) 
Aucun processus i ne peut exécuter la séquence suite i que si tous
les autres processus sont arrivés à leur point de rendez-vous.

2. Un processus peut être représenté à l’aide de la trace de l’exécution


de son programme.
Imposer une condition de franchissement de certains points de
leur trace temporelle.
Ces points sont appelés points de synchronisation. - 13 -
4. Spécification de la synchronisation
➢ La spécification d’un problème de synchronisation consiste à:
✓ Définir pour chaque processus ses points de
synchronisation.
✓ Associer à chaque point de synchronisation une condition de
franchissement.
➢ Les conditions sont exprimées au moyen des variables d’état du
système:
✓ Etat d’une ressource : libre ou occupée;
✓ Nombre de ressources libres;
✓ Nombre de ressources occupées;
✓ Nombre d’exécution d’une fonction d’un processus;
✓ Nombre de processus en attente d’une ressource donnée;
✓ ….
- 14 -
❖ Spécification de la synchronisation de l’exemple précédent

✓ P1 s’arrête dés qu’il y a fin de fichier.


✓ P2 s’arrête si le tampon T contient ‘‘fin’’.

Processus P1 Processus P2
Lire(article1,f);
tantque(non(fin(f))faire Tantque T  ‘’fin’’ faire
Déposer (article1,T); Retirer(article2,T);
Signaler(dépôt); Signaler(retrait);
Lire(article1,f); Ecrire(article2);
Finfaire; Finfaire;
T=‘’fin’’;

•- 15 -
● Points de synchronisation :
Déposer (article1,T), Retirer(article2,T), Signaler(dépôt) et
Signaler(retrait).

• Variable d’état:
Etat du tampon (vide ou plein).

• Conditions de franchissement des ces points de


synchronisation:
✓ Déposer (article1,T) : Si Tampon est vide ;
✓ Retirer(article2,T): Si tampon est plein;
✓ Signaler(dépôt) et Signaler(retrait) : Conditions toujours vraies.

- 16 -
5. Les problèmes types
● Les problèmes de synchronisation rencontrés dans la pratique
peuvent, dans leur grande majorité, se ramener à des combinaisons
d’un petit nombre de situations élémentaires pour lesquelles des
schémas de solutions sont connus.
● Les problèmes types peuvent être regroupés en trois classes:
1) Exclusion Mutuelle(première partie de ce chapitre)

2) Partage de ressources communes par plusieurs processus


✓ Allocateur de ressources banalisées,
✓ Modèle des lecteurs-rédacteurs.

3) Communication entre processus


✓ Rendez-vous,
- 17 -
✓ Modèle des producteurs-consommateurs.
6. Exemples :
6.1 Allocation d'une imprimante
a) m processus se partagent une seule imprimante.
• L’imprimante est une ressource critique : elle doit être utilisée en
exclusion mutuelle;
• Schéma général d’un processus i :

Processus i
Début
Demander(imprimante); Demande d’entrée en section critique
<Utiliser l'imprimante > < section critique >
Restituer(imprimante); Sortie de la section critique;
Fin;
• L’imprimante a deux états libre ou occupé. - 18 -
● Solution avec les sémaphores
Soit slibre le sémaphore d’exclusion mutuelle protégeant cette
imprimante :
Sémaphore slibre init 1;
Processus i
Début
P(slibre);
<Utiliser l'imprimante ; >
V(slibre);
Fin;

- 19 -
b) m processus se partagent n imprimantes
Schéma général d’un processus i :
Processus i
Début
Demander(imprimante);
<Utiliser l'imprimante ; >
Restituer(imprimante);
Fin;
Il y a n imprimantes  n processus sur m peuvent travailler
(imprimer) simultanément : C’est une ressource critique à n points
d’entrée.

1) Les points de synchronisation


− Demander imprimante,
− Restituer l'imprimante. - 20 -
2) Les conditions de franchissement de ces points de
synchronisation
− Allocation d’une imprimante si nbre_imp_libre > 0 Ou
− Allocation d’une imprimante si nbre_imprimantes_allouées < n
3) Le programme d'un processus en utilisant une variable d’état
La variable d’état représente le nombre d’imprimantes libres : nlibre.
entier nlibre init n;
Demander(imprimante) Restituer(imprimante)
Début Début
nlibre = nlibre - 1; nlibre = nlibre + 1;
si nlibre < 0 alors signaler(libération_imprimante);
attendre ; /* si processus bloqué(s) en
finsi attente d’une imprimante :
réveiller un processus) */
Fin;
Fin; - 21 -
La ressource imprimante est une ressource banalisée composée de n
exemplaires. Chaque unité ou exemplaire est une ressource critique 
une imprimante ne peut être utilisée que par un seul processus à la fois.
Cette ressource à n exemplaires  n processus peuvent utiliser ces n
imprimantes en même temps(simultanement).
Solution à l’aide des sémaphores
On utilise un sémaphore représentant le nombre de ressources libres
(imprimantes libres) : slibre.

sémaphore slibre init n;

Demander(imprimante) Restituer(imprimante)

Début Début
P(slibre); V(slibre);
Fin Fin
- 22 -
4) Deux Variables d’état
Les variables d’état représentent :
• Le nombre de ressources(imprimantes) demandées : nbd,
• Le nombre de ressources(imprimantes) occupées : nbo.
• Soit nbimp une variable ou constante système contenant le nombre
d’imprimantes.
Entier nbimp init n;
Entier nbo init 0 ; Entier nbd init 0 ;

Demander(imprimante) Restituer(imprimante)
Début Début
nbd = nbd + 1; nbo = nbo - 1;
si nbo = nbimp alors attendre; Si nbd > 0 alors
Sinon nbd = nbd - 1;
nbd = nbd - 1; nbo = nbo + 1;
nbo = nbo + 1; Réveiller un processus;
finsi finsi;
Fin Fin
- 23 -
Solution à l’aide des sémaphores :

On utilise deux sémaphores et deux variables d’états:


• Les variables d’état sont partagées par tous les processus utilisant les
imprimantes  elles doivent être protégées :
On utilise un sémaphore d’exclusion mutuelle pour les protéger.

• Pour mettre les processus en attente et pour les réveiller, on utilise un


sémaphore de synchronisation initialisé à 0.

- 24 -
Entier nbimp init n; /* variable ou constante */
Entier nbo init 0; Entier nbd init 0;
Sémaphore s init 0;
Demander(imprimante) Restituer(imprimante)
Début Début
nbd = nbd + 1; nbo = nbo - 1;
Si nbo == nbimp alors Si nbd > 0 alors

nbd = nbd – 1;
nbo = nbo + 1;
/*Attendre */ /* Réveiller un processus */
P(s); V(s);
Sinon Finsi
nbd = nbd - 1;
nbo = nbo + 1;
Finsi
Fin Fin

• Les variables nbd et nbo sont des variables partagées(ressources


critiques) : elles doivent être utilisées en exclusion mutuelle 
• Un seul processus peut exécuter une procédure Demander ou Restituer.
- 25 -
Entier nbimp init n; /* variable ou constante */
Entier nbo init 0; Entier nbd init 0;
Sémaphore mutex init 1;
Sémaphore s init 0;
Demander(imprimante) Restituer(imprimante)
Début Début
P(mutex); P(mutex);
nbd = nbd + 1; nbo = nbo - 1;
Si nbo == nbimp alors Si nbd > 0 alors
/* Sortir de la SC. avant nbd = nbd – 1;
de se mettre en attente*/ nbo = nbo + 1;
V(mutex);
/*Attendre */ /* Réveiller un processus */
P(s); V(s);
Sinon Finsi
nbd = nbd - 1;
nbo = nbo + 1;
V(mutex); V(mutex);
Finsi
Fin Fin
- 26 -
6.2 Rendez-vous de processus
6.2.1 Rendez-vous de deux processus : P1 et P2
P1 P2
Début Début
… …
Signaler son arrivée à P2; Signaler son arrivée à P1;
Attendre P2; Attendre P1;
… …
Fin Fin

On utilise deux sémaphores de synchronisation initialisés à 0.


Sémaphore s1, s2 init 0 ;
P1 P2
Début Début
… …
V(s2); V(s1);
P(s1); P(s2);
… …
Fin; Fin; - 27 -
6.2.2 Rendez-vous de trois processus : P1, P2 et P3

P1 P2 P3
Début Début Début
… … …
Signaler son arrivée Signaler son arrivée Signaler son arrivée
à P2 et P3; à P1 et P3; à P1 et P2;
Attendre P2 et P3; Attendre P1 et P3; Attendre P1 et P2;
… … …
Fin Fin Fin
On utilise deux sémaphores de synchronisation initialisés à 0.
Sémaphore s1, s2, s3 init 0;
P1 P2 P3
Début Début Début
… … …
V(s2); V(s3); V(s1); V(s3); V(s1); V(s2);
P(s1); P(s1); P(s2); P(s2); P(s3); P(s3);
… … …
Fin; Fin; Fin; - 28 -
6.2.3 Rendez-vous de n processus : P0 à Pn-1
a) Solution avec une variable partagée(par les n processus) et deux
sémaphores :
● La variable c’est un compteur qui permet de compter les
arrivées des processus(nombre de processus arrivés).
● Deux sémaphores:
✓ Cette variable est partagée donc elle doit être protégée par
un sémaphore d’exclusion mutuelle initialisé à un.
✓ Un sémaphore de synchronisation permettant de mettre en
attente les processus; ce sémaphore est initialisé à zéro.

Entier cpt init 0;


Sémaphore mutex init 1;
Sémaphore s init 0;

- 29 -
1) Le dernier processus réveil les (n-1) processus précédents
Processus i (i=0 à n-1)
Début
P(mutex);
cpt=cpt +1;
Si cpt < n alors /* processus 0 à n-2 */
V(mutex); /* Sortir de la SC avant de se mettre en attente */
p(s); /* se mettre en attente */
Sinon /* dernier processus*/
cpt=0;
v(mutex); /* Sortir de la SC */
/* réveiller le (n-1) processus */
Pour j=1 à n-1 faire
v(s);
finpour
Finsi;
Fin
- 30 -
2) Réveil en ‘‘cascade ’’
Processus i (i=0 à n-1)
Début
P(mutex);
cpt=cpt +1;
Si cpt < n alors /* processus 0 à n-2 */
V(mutex); /* Sortir de la SC avant de se mettre en attente */
P(s); /* se mettre en attente */
V(s); /* Réveiller le processus suivant */
Sinon /* dernier processus*/
cpt=0;
V(mutex); /* Sortir de la SC */
V(s); /* réveiller le premier processus */
P(s); /* se mettre en attente */
/* le dernier processus est réveillé par l’avant dernier */
Finsi
Fin
- 31 -
6.3 Le modèle des lecteurs/rédacteurs
➢ Considérons deux classes de processus appelés : Lecteurs et
Rédacteurs.
➢ Ces processus se partagent un fichier f.

➢ Les lecteurs peuvent seulement consulter le fichier,


➢ les rédacteurs peuvent seulement écrire sur le fichier.
➢ Les processus de ces deux classes doivent respecter les contraintes
suivantes:
• Plusieurs lecteurs peuvent lire simultanément le fichier.
• Un seul rédacteur à la fois peut écrire sur le fichier.
• Un lecteur et un rédacteur ne peuvent pas utiliser en même temps
le fichier.
- 32 -
➢ Protocole utilisé par les processus
Lecteurs Rédacteurs
Demande de lecture Demande d'écriture
Lecture Ecriture
Fin de lecture (libérer le fichier) Fin d’écriture (libérer le fichier)

1. Les points de synchronisation


✓ Demande de lecture,
✓ Demande d’écriture,
✓ Fin de lecture (point non bloquant),
✓ fin d’écriture (point non bloquant).
2. Les conditions de franchissement
Soient
nl : nombre de lectures en cours,
ne : nombre d'écritures en cours (0 ou 1).
- Demande de lecture : si ne=0;
- Demande d'écriture : si ne=0 et nl=0; - 33 -
6.3.1 L’ordre d’accès au fichier f est quelconque
• Tous les processus (lecteurs et rédacteurs) ont la même priorité.

/* nl : le nombre de lecteurs utilisant le fichier f à un instant donné */


/* nl est une variable partagée par les lecteurs 
Utiliser un sémaphore d’exclusion mutuelle pour protéger nl */
Entier nl init 0;
Sémaphore mutex init 1;
/* Le fichier f est utilisé en exclusion mutuelle :
• Entre les deux classes de processus : lecteurs et rédacteurs;
• Entre les rédacteurs.
 Utiliser un sémaphore d’exclusion mutuelle pour le protéger */
Sémaphore sf init 1;

- 34 -
Sémaphore sf init 1;

Entier nl init 0;
Sémaphore mutex init 1;
Lecteurs Rédacteurs
Début Début
/* demande de lecture */ /* demande d'écriture */
p(mutex); p(sf);
nl=nl+1;
si nl=1 alors p(sf); < écrire >
v(mutex);
< lire > /* fin d'écriture */
/* fin de lecture */ v(sf);
p(mutex); Fin;
nl=nl-1;
si nl=0 alors v(sf);
Remarque : Risque de
v(mutex);
privation pour les rédacteurs.
Fin; - 35 -
6.2.2 Ordre d’accès au fichier f selon FIFO
Sémaphore sf init 1;
Sémaphore sfifo init 1; /*sémaphore imposant l’ordre d’accès FIFO */
Entier nl init(0);
Sémaphore mutex init 1;
Lecteurs Rédacteurs
Début Début
/* demande de lecture */ /* demande d'écriture */
P(sfifo); P(sfifo);
p(mutex); p(sf);
nl=nl+1; V(sfifo);
si nl=1 alors p(sf);
v(mutex); < écrire >
V(sfifo);
< lire > /* fin d'écriture */
/* fin de lecture */ v(sf);
p(mutex); Fin;
nl=nl-1;
si nl=0 alors v(sf);
v(mutex);
Fin; - 36 -
6.4 Communication par variables communes

6.4.1 Définitions
• Soit T un tampon(buffer) accessible à l’ensemble des processus
voulant communiquer.
• On distingue deux classes de processus :
1. Producteur : processus désirant déposer de l’information dans
un tampon T
2. Consommateur : un processus désirant retirer de l’information
d’un tampon T.

- 37 -
6.4.2 Propriétés de la communication
La communication par variable(s) commune(s) est :
✓ Indirecte : le producteur et le consommateur ne s’adressent pas
l’un à l’autre.

✓ Asynchrone : il n’est pas nécessaire que le producteur et le


consommateur entrent en communication en même temps
(simultanément).
Il suffit, simplement, que le producteur ait déposé l’information
dans le tampon avant que le consommateur ne la retire.

✓ Multiple : plusieurs producteurs peuvent envoyer(déposer) des


messages à plusieurs consommateurs.

- 38 -
➢ Deux types de problèmes à traiter dans la communication par
variables communes :
1. La synchronisation des processus
• Un producteur ne peut déposer un message que s’il y a de la
place disponible dans le tampon(buffer).
• Un consommateur ne peut retirer un message que s’il y en a de
disponible.
• Les producteurs et les consommateurs ne peuvent accéder
simultanément au tampon que selon certaines règles assurant
l’intégrité de l’information.

2. La transmission
C'est le moyen par lequel l’information est communiquée :
Elle concerne la gestion du tampon T.

- 39 -
6.4.3 Schéma général du producteur-consommateur
• Le système de communication : il est composé
✓ du tampon : T,
✓ du mécanisme de synchronisation : Sémaphores ,
✓ des primitives de communication :
Envoyer(message) et Recevoir(message).
• Soient
✓ Un producteur désirant envoyer un message Mp dans un système
de communication et
✓ Un consommateur désirant retirer un message Mc de ce même
système de communication.

- 40 -
Producteur Consommateur
Répéter Répéter
Produire(Mp); Recevoir(Mc);
Envoyer(Mp); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;
• Décomposant les procédures Envoyer et Recevoir en séparant le
problème de synchronisation et de celui de la transmission :

Producteur Consommateur
Procédure Envoyer(Mp) Procédure Recevoir(Mc)
Demande de dépôt ; Demande de retrait;
Déposer(Mp); Retirer(Mc);
Signaler dépôt; Signaler retrait;
Fin; Fin;
• Synchronisation : Demande de dépôt, Signaler dépôt,
Demande de retrait, signaler retrait.

• Transmission(ou gestion du tampon) : Déposer, Retirer.


- 41 -
6.4.4 Producteur-Consommateur avec un tampon à un seul
élément
• Un Tampon de taille égale à un élément ne peut contenir qu'une
seule information.
➢ Un producteur ne peut déposer une information dans le tampon
que si le consommateur a retiré la précédente.
➢ Un "Tampon à un seul élément" possède donc deux états :
plein et vide.
Remarque: Elément : variable simple ou structure.
✓ Producteur : Déposer si tampon est vide.
✓ Consommateur : Retirer si Tampon est plein.

• Réalisation avec les sémaphores


On associe deux sémaphores splein et svide au tampon T
initialisés respectivement à zéro et un.
- 42 -
Producteur Consommateur
Procédure Envoyer(Mp) Procédure Recevoir(Mc)
Demande de dépôt ; Demande de retrait;
Déposer(Mp); Retirer(Mc);
Signaler dépôt; Signal er retrait;
Fin; Fin;
Tampon T;
Sémaphore splein init 0;
Sémaphore svide init 1;
Procédure Demande_de_dépôt Procédure Demande_de_retrait
P(svide); P(splein);
Fin; Fin;
Procédure Signaler_dépôt; Procédure Signaler_retrait;
V(splein); V(svide);
Fin; Fin;
Procédure Déposer(Mp); Procédure Retirer(Mc);
T=Mp; Mc=T;
Fin; Fin;
- 43 -
Tampon T;

Sémaphore splein init 0;


Sémaphore svide init 1;
Producteur Consommateur
Début Début
Répéter Répéter
Produire(Mp) P(splein);
P(svide); Mc=T;
T=Mp; V(svide);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;

- 44 -
6.4.5 Producteur-Consommateur avec un tampon à n éléments
• Nombre fixe d’éléments du tampon égal à n.
• n est connu par le producteur et le consommateur.
• Un producteur ne peut déposer une information dans un élément
du tampon que si le consommateur a retiré la précédente.
• Les éléments du tampon sont consommés dans le même ordre que
leur production.
• Chaque élément du tampon à deux états : plein et vide.
• Producteur : Déposer s’il y a un élément vide dans le tampon.
• Consommateur : Retirer s’il y a un élément plein dans le
Tampon.

- 45 -
• Réalisation avec les sémaphores
a) Synchronisation
✓ On associe deux sémaphores splein et svide au tampon T.
✓ splein et svide initialisés respectivement à zéro et n. 
✓ Les procédures de synchronisation ne changent pas : elles sont les
mêmes , on ne change que l’initialisation du sémaphore svide.
Sémaphore splein init 0;
Sémaphore svide init n;
Producteur Consommateur
Début Début
Répéter Répéter
Produire(Mp) P(splein);
P(svide); Retirer(Mc)
Déposer(Mp) V(svide);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;
- 46 -
b) Gestion du tampon à n éléments
● Allocation statique du tampon.
● Le tampon sera géré de manière circulaire: On l'appelle tampon
circulaire ou buffer circulaire.
● Les éléments du tampon sont numérotés de 0 à n-1.
● Dans un tampon circulaire, on considère l'élément 0 comme le
successeur de l'élément n-1.

b.1) Un seul producteur et un seul consommateur


Pour parcourir le tampon, chaque classe de processus (producteur ou
consommateur) aura son propre pointeur:
✓ In : pointeur (ou index) utilisé par le producteur.
✓ Out : pointeur (ou index) utilisé par le consommateur.
✓ In et out sont deux variables locales respectivement au
producteur et consommateur et elles sont initialisées à 0(zéro).
- 47 -
T: tableau[0..n-1] de tampon;
Sémaphore splein init 0;
Sémaphore svide init n;
Producteur Consommateur
Entier in init 0; Entier out init 0;
Début Début
Répéter Répéter
Produire(Mp) P(splein);
P(svide); Retirer(Mc)
Déposer(Mp) V(svide);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;

Déposer(Mp) Retirer(Mc)
T[in]:= Mp; Mc := T[out];
in :=(in +1) modulo n; out:=(out +1) modulo n;
Fin; Fin; - 48 -
T: tableau[0..n-1] de tampon;
Sémaphore splein init 0;
Sémaphore svide init n;
Producteur Consommateur
Entier in init(0); Entier out init(0);
Début Début
Répéter Répéter
Produire(Mp) P(splein);
P(svide); Mc := T[out];
T[in]:= Mp; out:=(out +1) modulo n;
in :=(in +1) modulo n; V(svide);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;

- 49 -
6.4.6 Plusieurs producteurs(P) et plusieurs consommateurs(C)
Ce cas pose un problème d'exclusion mutuelle au niveau de la gestion
du tampon (ou dans les procédures Déposer et Retirer):

➢ Exclusion mutuelle entre producteurs


Plusieurs producteurs ne doivent pas déposer en même temps de
l'information dans un même élément du tampon.

➢ Exclusion mutuelle entre Consommateurs


Plusieurs consommateurs ne doivent pas retirer (consommer) un
même message(un message n'est consommé qu'une seule fois par
un seul processus).

- 50 -
➢ Pour parcourir le tampon, chaque classe de processus (producteurs ou
consommateurs) aura son propre pointeur:
● In : pointeur (ou index) utilisé par les producteurs, c’est une
variable commune aux producteurs : ils doivent l'utiliser en
exclusion mutuelle.
● Out : pointeur (ou index) utilisé par les consommateurs. C’est une
variable commune aux consommateurs : ils doivent l'utiliser en
exclusion mutuelle.
● Les deux variables sont initialisées à 0(zéro).
● Pour protéger les variables communes in et out, on aura besoin de
deux sémaphores d'exclusion mutuelle sin et sout.
● sin et sout sont initialisés à 1.

- 51 -
Sémaphore sin init (1); Sémaphore sout init (1);

Déposer(Mp) Retirer(Mc)
p(sin); p(sout);
T[in]:= Mp; Mc := T[out];
in :=(in +1) modulo n; out:=(out +1) modulo n;
v(sin); v(sout);
Fin; Fin;

- 52 -
T: tableau[0..n-1] de tampon;
Sémaphore splein init 0;
Sémaphore svide init n;
Producteurs Consommateurs
Entier in init 0; Entier out init 0;
Sémaphore sin init 1; Sémaphore sout init 1;
Producteur i Consommateur i
Début Début
Répéter Répéter
Produire(Mp) P(splein);
P(svide); P(sout);
P(sin); Mc := T[out];
T[in]:= Mp; out:=(out +1) modulo n;
in :=(in +1) modulo n; V(sout);
V(sin); V(svide);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;
- 53 -
6.4.7 Producteur-Consommateur avec tampon à éléments alloués
• Les éléments du tampon sont alloués dynamiquement en faisant appel
à une fonction système(ou à un allocateur de mémoire).
• Le nombre d’éléments du tampon est variable il n’est pas connu à
l’avance.
• Dans ce cas, une partie du problème de synchronisation se trouve
reporter au niveau de "l'allocateur de la mémoire".
• L’allocateur de la mémoire a pour rôle :
✓ d'allouer aux producteurs qui lui demandent un élément de tampon,
✓ de libérer les éléments restitués par les consommateurs.

❖ Allocateur
Procédure allouer( élément);
Procédure restituer(élément);
Fin;
- 54 -
➢ Réalisation des procédures au moyen des sémaphores
● Considérons deux primitives allouer(élément) et
restituer(élément) permettant respectivement l'allocation et la
libération d'un élément du tampon.
● On utilise un seul sémaphore splein initialisé à zéro.
● Ce sémaphore permet d'une part, au producteur de signaler le
dépôt de messages et d'autre part, il permet au consommateur de
vérifier la présence de message dans le tampon.
Tampon *T; /* le tampon est une liste */
splein: sémaphore init 0;
Procédure Demande_de_dépôt Procédure Demande_de_retrait
Allouer(élément); P(splein);
Fin; Fin;

Procédure Signaler_dépôt Signaler_dépôt


v(splein); Restituer(élément);
Fin; Fin; - 55 -
Tampon *T;
Sémaphore splein init 0;

Producteur Consommateur
Début Début
Répéter Répéter
Produire(Mp) P(splein);
Allouer(élément); Retirer(T, Mc, élément);
Déposer(T, élément, MP); Restituer(élément);
V(splein); Consommer(Mc);
Jusqu’à faux; Jusqu’à faux;
Fin; Fin;

Remarque: allouer(élément) et restituer(élément) peuvent être


respectivement dans les procédures Déposer et Retirer

- 56 -
❖ Sémaphore binaire

La valeur e(s) d’un sémaphore peut prendre deux valeurs 0 ou 1.


Les opérations P et V sur les sémaphores binaires sont notées Pb et
Vb

Pb(s) Vb(s)
Début Début
Si s=1 alors Si f(s) est vide alors
s=0; s=1;
Sinon Sinon
Mettre le processus dans la Retirer un processus de la
file f(s); file f(s);
/*bloquer le processus*/ /*réveiller un processus*/
Finsi Finsi
Fin Pb; Fin Vb;

- 57 -
7. La communication interprocessus :
IPC Unix System V
7.1 Introduction
• Les processus concurrents sont créés pour participer à la réalisation
d’une application commune.
• Ces processus se partagent des informations communes qu’ils peuvent
consulter ou modifier au cours de leur exécution.
• Ces processus peuvent être crées dans un même programme(ils ont le
même père) ou créés dans des programmes différents.
• La communication interprocessus(IPC : InterProcess Communication)
permet à un ensemble de processus concurrents , s’exécutant sur une
même machine, de communiquer entre eux en respectant certaines
règles.

- 58 -
Types de communications IPC System V

Il existe trois types de communication interprocessus :


1. Les files de messages,
2. Les segments de mémoire partagée,
3. Les sémaphores.
• Ces mécanismes ont été introduits dans Unix System V en 1983.

• Dans ce cours, nous nous limitons à la communication par segments


de mémoire partagée et les sémaphores.

- 59 -
7.2 Identification d’une IPC
• Une IPC (segment de mémoire ou sémaphore) est identifiée à l’aide
d’une clé unique connue par tous les processus coopérants
utilisant cette ressource partagée.

• Une IPC peut être privée ou publique:


✓ IPC privée : Identifiée par une clé spéciale : IPC_PRIVATE.
Une IPC privée ne peut être utilisée que par le processus créateur
et ses descendants ou processus créés dans le même
programme. 
Tous les processus fils peuvent accéder à cette IPC par héritage.
Cet héritage est perdu lors d’un recouvrement de programme(
chargement d’un nouveau programme à l’aide d’une instruction
exec(execl, execlv, execve, …). 
Tous les accès aux segments de mémoire partagée et aux
sémaphores sont perdus. - 60 -
✓ IPC publique : Utilisée par des processus créés dans des
programmes différents(processus qui non pas de liens de parenté)
ou par des processus créés dans le même programme.
La clé est obtenue à partir d’un chemin d'accès d’un fichier ou
d’un répertoire existant et d'un numéro de projet, grâce à la
fonction suivante:
key_t ftok (char *chemin d’accès, char projet);
Remarque : Les systèmes actuels acceptent un entier pour « projet »;
L’octet de poids faible de cette valeur doit être différent de zéro(0).

Clé sur 32 bits


31 .. 24 23 .. 16 15 .. 0
N° Mineur
Projet & 0xFF I-node & 0xFFFF
périphérique & 0xFF

• Unix identifie chaque périphérique au moyen de 2 numéros ,


N° majeur : type de périphérique et N° mineur : Index dans le type.
• Un fichier ou répertoire est identifié à l’aide d’un nom et d’un
d’i-node (index node ou indexe nœud)
- 61 -
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok (char *chemin, char projet);
Cette fonction renvoie une clé unique de 32 bits en combinant le
numéro d'i-node du fichier ou du répertoire précisé avec le numéro
de projet.
Exemple:
key_t cle;
cle=ftok (/home/.  , ‘A’);
If(cle==-1) {
printf(\n erreur : la clé n’a pas été créée);
exit(1);
}
Si Cle = -1  erreur: la clé n’a pas été créée.
- 62 -
7.3 Les segments de mémoire partagée
• Le principe de la mémoire partagée consiste à définir une
zone(segment) en mémoire qui soit en dehors de la zone attribuée à
chaque processus. Un segment de mémoire partagée est indépendant
de tout processus.
• La mémoire partagée est la forme de communication interprocessus la
plus rapide car tous les processus se partagent la même mémoire.
• L'accès à cette mémoire partagée ne nécessite pas d'appel système.
• Ce mécanisme n’implique pas de copie ou duplication des données :
Tous les processus qui se partagent un segment de mémoire partagée
peuvent lire et modifier les données se trouvant dans ce segment.
• La synchronisation(coordination) des accès à la mémoire partagée est à
la charge de l’utilisateur.
• Cette synchronisation est réalisée à l’aide des sémaphores.

- 63 -
• Un segment de mémoire partagée doit être créé et détruit
explicitement par un des processus concurrents.
• Remarque : Après la terminaison des tous les processus utilisant un
segment de mémoire partagée, le système ne détruit pas ce segment.
7.3.1 Utilisation d’un segment de mémoire partagée
Un processus désirant utiliser un segment de mémoire partagée doit faire
les actions suivantes :
1) Obtenir une clé(publique ou privée) qui permet d’identifier le
segment,
2) Créer le segment de mémoire partagée,
3) Attacher le segment de mémoire partagée à son espace d’adressage,
4) Utiliser le segment de mémoire partagée,
5) Détacher le segment de mémoire partagée,
6) Détruire le segment(si dernier processus utilisant le segment et si
c’est le processus créateur).
- 64 -
7.3.2 Fonctions de manipulation des segments de mémoire
partagée

• shmget() : Permet à partir d'une clé d'obtenir l'identifiant d'un


segment de mémoire partagée existant ou d'en créer un au besoin.
• shmat() : Permet d'attacher le segment de mémoire partagée dans
l'espace d'adressage du processus.
• shmdt() : Permet de détacher le segment si on ne l'utilise plus.
• shmctl() : Permet de supprimer ou paramétrer un segment de
mémoire partagée.
Les constantes et les prototypes de ces fonctions sont définis dans
<sys/shm.h> 
#include < sys/shm.h >

- 65 -
1) Création d’un segment de mémoire partagée
int shmget(key_t cle, int taille, int options);
✓ cle : clé obtenue avec ftok() ou IPC_PRIVATE.
✓ taille : indique la taille en octets du segment de mémoire partagée.
Si le segment existe déjà, la taille doit être inférieure ou égale à
la taille effective du segment.
La taille est arrondie au multiple supérieur de la taille des
pages mémoire du système (4Ko sur un processeur Intel).
✓ options : OU (|) binaire entre IPC_CREAT, IPC_EXCL et les 9 bits
d'autorisation.
adr= shmget (cle, 4096, IPC_CREAT | 0666);
0 : pour indiquer que ‘666’ est une valeur octale.
‣ IPC_CREAT : Créer un nouveau segment de mémoire partagée s’il
n’y a pas de segment associé à la clé <cle>.
S’il existe déjà un segment associé à la clé indiquée,
la fonction renvoie l’identificateur de ce segment.
- 66 -
‣ IPC_EXCL : toujours utilisé avec IPC_CREAT, permet de créer
un nouveau segment. S’il existe déjà un segment
associé à la clé indiquée, abandonner la fonction
shmget (erreur : pas de création de segment).
‣ Bits d'autorisation : 9 bits; propriètaire(rw-), membres du
goupe(rw-), autres(rw-).
Remarques :
a) Le bit x(exécution) n’est pas utilisé(-): il n’ y a pas d’exécution
d’un segment de mémoire partagée(données).
b) Ne pas oublier de préciser les autorisations(permissions) d’accès
lors de la création d’un segment de mémoire partagée. Sinon le
segment aura la protection 000 (en octal).  Aucun accès.
• La fonction retourne un identificateur du segment de mémoire
partagée.
• Ce segment n’est pas encore utilisable, Le processus doit l’attacher à
son espace d’adressage à l’aide la fonction shmat().
- 67 -
2) Attachement d’un segment de mémoire partagée à l’espace
d’adressage d’un processus
char *shmat(int shmid, char *adr, int options);
✓ shmid : identificateur de segment de mémoire partagée créé avec
shmget.
✓ adr : adresse désirée de l'attachement, elle est rarement utilisée.
On met la valeur NULL ou 0 et c’est le système qui choisit un
emplacement libre(une adresse) dans l’espace d’adressage du
processus. Solution à utiliser dans les TP.
✓ options : SHM_RDONLY : l’attachement est réalisé en lecture seule,
0 : l’attachement est réalisé en lecture et écriture.
• La fonction retourne l’adresse du premier octet du segment ou -1 si
erreur.

Remarque : Pour les exercices et les TP, l’attachement est obligatoire.


On ne tient pas compte de l’héritage de fork(). - 68 -
3) Détachement d’un segment de mémoire partagée
int shmdt(char *adrseg);
Détachement du segment dont la demande d'attachement par shmat a
été réalisée à l'adresse adrseg (adrseg contient l’adresse retournée
par shmat).
4) Suppression d’un segment de mémoire partagée
int shmctl(int shmid, int opérations, struct shmid_ds *p_shm);
✓ Shmid : identificateur du segment de mémoire partagée.
✓ Opérations : Nous ne traitons que la valeur « IPC_RMID » qui permet
de supprimer un segment de mémoire partagée.
Pour les autres valeurs voir le man shmctl().
‣ IPC_RMID : Le segment est marqué « prêt pour la suppression ».
Un segment n’est effectivement détruit que s’il a été détaché par
tous les processus qui l’utilisent.
✓ p_shm : 0; non utilisé avec IPC_RMID.
• Remarque : La suppression des IPC est à la charge de l’utilisateur. - 69 -
7.3.3 Exemple : Création et d’utilisation d’un segment
de mémoire partagée
int main ()
{ int taille= 4096; /* taille d’une page mémoire sous linux */
int adrmemp; char *mem; cle key_t;

/* Créer une clé. On suppose que le fichier fexemple.c existe */


cle=ftok("./fexemple.c" ,’A’);
if(clé==-1) { printf(" \n erreur "); exit(0);}

/* Créer un segment de mémoire partagée. */


adrmemp = shmget (cle, taille, IPC_CREAT | 0666);
if(adrmemp==-1){ printf(" \n erreur "); exit(0);}

/* Attacher le segment de mémoire partagée. Adr=0 


c’est le système qui choisit l’adresse pour l’attachement*/
mem = (char *) shmat (adrmemp, 0, 0);
if(mem == NULL) { printf("\nErreur "); exit(13);}
- 70 -
/* Utiliser la mémoire partagée : Initialiser toute la mémoire avec ‘a’ */
for(int i=0; i<taille; i++) mem[i]=‘a’;

/* Détacher le segment de mémoire partagée. */


shmdt (mem);

/* Libèrer le segment de mémoire partagée. */


shmctl (adrmemp, IPC_RMID, 0);

return 0;
}

Remarque : Dans cet exemple, on a pas traité le problème des conflits


d’accès(synchronisation) à la mémoire par plusieurs processus.

- 71 -
7.4 Les sémaphores Unix system V
• Le partage de segment de mémoire partagée nécessite une
coordination(synchronisation) des actions des processus qui l’utilisent.
• Cette coordination(synchronisation) est assurée au moyen des
sémaphores Unix SystemV.
7.4.1 Utilisation des sémaphores Unix system V
• Obtenir une clé(publique ou privée) qui permet d’identifier un
ensemble de sémaphores(un ou n sémaphores),
key_t ftok (char *chemin d’accès, char projet);
• Créer un ensemble de un ou n sémaphores : semget(),
• Initialiser ce ou ces sémaphores : semctl(),
• Utiliser les sémaphores pour synchroniser les actions des processus
concurrents(opérations P et V ou autre opération) : semop(),
• Détruire le ou les sémaphores(si dernier processus utilisant ce(s)
sémaphore(s) et si c’est le processus créateur) : semctl().
- 72 -
7.4.2 Création d’un ensemble de sémaphores
La création est réalisée à l’aide la fonction semget().
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>

int semget (key_t cle, int nb_sem, int option);

✓ cle : Clé identifiant l’ensemble de sémaphores.


Il y a une clé par ensemble de sémaphores.
✓ nb_sem : Nombre de sémaphores de l'ensemble (1 à 64).
✓ options : OU (|) binaire entre IPC_CREAT, IPC_EXCL et les 9 bits
d'autorisation.
Adrs = semget (cle, 1 , IPC_CREAT | 0666);
0 : pour indiquer que ‘666’ est une valeur octale.
- 73 -
‣ IPC_CREAT : Créer un nouvel ensemble de sémaphores s’il n’y a
pas d’ensemble associé à la clé <cle>.
S’il existe déjà un ensemble de sémaphores associé à
la clé indiquée, La fonction renvoie l’identificateur de
cet ensemble.
‣ IPC_EXCL : toujours utilisé avec IPC_CREAT, permet de créer
un nouvel ensemble de sémaphores. S’il existe déjà
un ensemble associé à la clé indiquée, abandonner la
fonction semget() ; (erreur : pas de création de
sémaphores).
‣ Bits d'autorisation : Propriètaire(rw-), membres du goupe(rw-),
autres(rw-).

✓ La fonction retourne un identificateur de l’ensemble des sémaphores.

- 74 -
7.4.3 Initialisation d’un ensemble de sémaphores
Il est possible de réaliser certaines opérations de contrôle sur un ensemble
de sémaphores grâce à la primitive semctl() :
int semctl (int idf_sem, int num_sem, int commande, union semun arg);

✓ idf_sem : identifiant de l'ensemble de sémaphores. Cet identifiant est


retourné par la primitive semget().

✓ num_sem : Numéro du sémaphore sur lequel il faut réaliser


l’opération.
Dans le cas où l'opération concerne l'ensemble de tous les sémaphores
et non un sémaphore particulier, ce paramètre peut-être d'une valeur
quelconque(supérieure ou égale au nombre de sémaphores de
l’ensemble) car celle-ci ne sera pas utilisée; mais il faut y mettre une
valeur pour conserver l'ensemble des paramètres.

- 75 -
✓ commande : commande permettant de contrôler l'ensemble de
sémaphores.
‣ SETVAL : affectation d'une valeur précise dans le sémaphore
référencé par num_sem (ou dont le numéro se trouve dans
num_sem)
‣ SETALL : affectation d'une valeur précise dans tous les
sémaphores de l'ensemble.
✓ union semun {
int val;
struct semid_ds *buf; /* voir le man */
unsigned short *array;
} arg;
‣ arg.val : valeur du sémaphore utilisé avec SETVAL.
‣ arg.array : Adresse tableau des valeurs des sémaphores utilisé
avec SETALL.
- 76 -
7.4.4 Opérations sur un ensemble de sémaphores
La primitive semop() permet d’exécuter des opérations sur un groupe de
1 à n sémaphores de l'ensemble. Ce groupe d'opérations sera atomique,
c'est à dire qu'il sera exécuté sur tous les sémaphores du groupe ou sur
aucun.
int semop (int sem_id, struct sembuf *operation, unsigned int nb_op);

✓ sem_id : identifiant de l'ensemble de sémaphores.


✓ Operation : Pointeur sur une variable(ou un tableau) de type struct
sembuf définissant l’opération à effectuer sur un sémaphore.
struct sembuf{
short int sem_num; /* Numéro du sémaphore dans l’ensemble */
short int sem_op; /* Valeur à ajouter au sémaphore */
short int sem_flg; /* Attribut pour l’opération */
}
- 77 -
‣ opération est définie par une valeur entière de sembuf.sem_op qui
est interprétée comme suit :

Remarque : A chaque sémaphore est associée une structure:


struct sem{
unsigned short semval; /* Compteur du sémaphore */
short sempid; /* PID du dernier processus ayant effectué
une opération sur ce sémaphore*/
unsigned short semncnt; /* nbre de processus en attente que
semval > valeur courante */
unsigned short semzcnt; /* nbre de processus en attente que le
compteur semval soit égal à zéro */
}

❖ sembuf.sem_op > 0 : La valeur ‘sembuf.sem_op’ est ajoutée au


compteur du sémaphore(sem.semval) et réveille les processus en
attente.
78 -
❖ sembuf.sem_op < 0 :
Si sem.semval < valeur absolue de sembuf.sem_op :
- Mettre le processus en attente jusqu’à ce que
sem.semval ≥ valeur absolue de sembuf.sem_op.
Lorsque cette condition sera vérifiée :
- Ajouter la valeur sembuf.sem_op à sem.semval (décrémenter
sem.semval).
- Réveiller le processus.

❖ sembuf.sem_op = 0 :
Si compteur du sémaphore(sem.semval) n’est pas égal à zéro,
- Mettre le processus en attente jusqu’à ce que le compteur du
sémaphore soit égal à zéro(sem.semval=0).

79 -
✓ Attribut Sem_flg :
‣ En général, on utilise la valeur 0(sem_flag=0);
‣ SEM_UNDO : Restituer l’état initial après une fin anormale d’un
processus.
Le système sauvegarde toutes les opérations effectuées par chacun
des processus sur le sémaphore et en cas de terminaison anormale
d’un processus, le système exécute les opérations inverses de ce
processus sur ce sémaphore.
‣ IPC_NOWAIT : l’opération ne sera pas bloquante même si la valeur
du champ sembuf.sem_op est négative ou nulle.

✓ nb_op : nombre d'opérations à effectuer (1 à n).

80 -
7.4.5 Destruction d’un sémaphore
La fonction semctl() avec la ‘commande IPC_RMID’ permet de
supprimer un ensemble de sémaphores :
int semctl (int idf_sem, int num_sem, int commande, arg);

✓ idf_sem : identifiant de l'ensemble de sémaphores.


✓ num_sem : numéro du sémaphore que l’on veut supprimer.
Dans le cas où l'opération concerne l'ensemble de tous les
sémaphores et non un sémaphore particulier, ce paramètre peut-
être est une valeur supérieure au égale au nombre de sémaphores
de l’ensemble.
✓ Arg : 0 , non utilisé avec IPC_RMID.

- 81 -
7.4.6 Exemple : Sémaphores svide et splein initialisés à 0 et 10
#define splein 0 /* Numéro du sémaphore splein */
#define svide 1 /* Numéro du sémaphore svide */
/* Préparation des opérations P et V */
struct sembuf Psvide = {svide, -1, 0}; /* P sur svide */
struct sembuf Vsvide = {svide, 1, 0}; /* V sur svide */
struct sembuf Psplein = {splein, -1, 0}; /* P sur splein */
struct sembuf Vsplein = {splein, 1, 0}; /* V sur splein */
int sem; /* Identificateur des sémaphores */

- 82 -
int main()
{ union semun { int val; struct semid_ds *buf; unsigned short *array;
} svideinit, spleininit;
/* Création d'un tableau de 2 sémaphores splein et svide*/
sem = semget(IPC_PRIVATE, 2, IPC_CREAT|0666);
if(sem == -1) { printf("\nErreur de création des semaphore"); exit(1);}

/* Initialisation du semaphore splein à 0 */


spleininit.val = 0; /* Valeur initiale de splein */
semctl(sem, splein, SETVAL, spleininit);

/* Initialisation du semaphore svide à 10 */


svideinit.val = 10; /* Valeur initiale de svide */
semctl(sem, svide, SETVAL, svideinit);

/* Supprimer les sémaphores */
semctl(sem, 2, IPC_RMID, 0);
return 0;
}
- 83 -
7.5 Gestion des IPC avec des commandes shell
Deux commandes : ipcs et ipcrm.
1. La commande shell ipcs permet de consulter les tables IPC du
système.
➢ ipcs : consulter toutes les tables.
➢ ipcs -s : consulter la table des sémaphores
➢ ipcs -m : consulter la table des segments de mémoire partagée.
2. La commande shell ipcrm permet de supprimer un segment de
mémoire partagée ou un sémaphore.
Le segment ou le sémaphore à supprimer peut être désignée soit
- par la clé qui lui est associé (si clé différente de IPC_PRIVATE),
- par son identificateur interne( identificateur affiché par ipcs).
➢ ipcrm {-m, -s } identificateur du segment ou du sémaphore
➢ ipcrm {-M, -S } clé du segment ou du sémaphore
Seul le propriétaire de l'objet peut demander sa suppression.

N’oubliez pas de consulter les man’s des IPC’s, de IPC’s et de de ipcrm.


- 84 -
7.6 Exemple Lecteur-rédacteur avec ordre d’accès FIFO
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

#define indexfichier 0
#define indexnl 1

#define sf 0
#define sfifo 1
#define mutex 2

- 85 -
int memp; /* Idf du seg de memoire partagee fichier */
int sem; /* Idenfificateur des semaphores sf, sfifo et
mutex */

/* Préparation des opérations P et V */

struct sembuf Psf = {sf, -1, 0}; /* P sur sf */


struct sembuf Vsf = {sf, 1, 0}; /* V sur sf */
struct sembuf Pmutex = {mutex, -1, 0}; /* P sur mutex */
struct sembuf Vmutex = {mutex, 1, 0}; /* V sur mutex */
struct sembuf Psfifo = {sfifo, -1, 0}; /* P sur sfifo */
struct sembuf Vsfifo = {sfifo, 1, 0}; /* V sur sfifo */

- 86 -
void redacteur(void)
{ int pid; int *f; int *fichier;
pid=getpid();
printf("\n=== Début de la procédure rédacteur : %d ===\n",pid);
/* Attachement du segment */
f =(int *)shmat(memp,0,0);
if(f==NULL){ printf("\nErreur: memoire partagée non attachee");exit(1);}
fichier=f+indexfichier;
/* Demande d’écriture */
semop(sem, &Psfifo,1); /* p(sfifo)*/
semop(sem, &Psf,1); /* p(sf)*/
semop(sem, &Vsfifo,1); /* v(sfifo)*/
*fichier=pid; /* Ecrire sur le fichier */
printf("\n** Le rédacteur %d a écrit sur le fichier:%d **", pid, *fichier);
sleep(2); /* Attendre avant de libérer le fichier */
/* fin d’écriture */
semop(sem, &Vsf,1); /* v(sf)*/
/* Détachement du segment */
shmdt(f);
exit(0); } - 87 -
void lecteur(void)
{ int i; int a; int *f; int *fichier; int *nl; int pid;
pid=getpid(); printf("\n=== Début de la procédure lecteur %d ===\n",pid);
/* Attachement du segment */
f =(int *) shmat(memp, 0, 0);
if(f==NULL){ printf("\nErreur:mémoire partagée non attachée"); exit(0);};
fichier=f+indexfichier;
nl=f+indexnl; /* adresse de la variable nl dans la mémoire partagée*/
/* demande de lecture */
semop(sem, &Psfifo,1); /* P(sfifo) */
semop(sem, &Pmutex,1); /* P(mutex) */
*nl=*nl + 1;
if((*nl)==1) semop(sem, &Psf,1); /* P(sf) */
semop(sem, &Vmutex,1); /* V(mutex) */
semop(sem, &Vsfifo,1); /* V(sfifo)*/
/* lire sur le fichier */
a=*fichier;
printf("\n*** Le lecteur %d lit le fichier : valeur lue=%d ***",pid,a);
sleep(2); /* Attendre avant de liberer */
- 88 -
/* fin de lecture */
semop(sem, &Pmutex,1); /* P(mutex)*/
*nl=*nl-1;
if((*nl)==0) i=semop(sem, &Vsf,1); /* V(sf)*/
semop(sem, &Vmutex,1); /* V(mutex)*/

/* Détachement du segment */
shmdt(f);
exit(2);
}

- 89 -
int main (void)
{ int i; int p; int *f; int *fichier; int *nl;
union semun{ /* préparer les structures pour initialiser les sémaphores */
int val;
struct semid_ds *buf;
unsigned short *array;
} sfinit, sfifoinit, smutexinit;
/* Création d'un segment de mémoire partagée contenant 2 entiers */
memp = shmget(IPC_PRIVATE, 2*sizeof(int), IPC_CREAT|IPC_EXCL|0660);
if(memp==-1){printf("\nErreur de création de la mém partagée"); exit(1);}
/* Attachement du segment de mémoire partagée pour initiliser nl*/
f=(int *)shmat(memp,0,0);
if(f==NULL){printf("\n Erreur: mémoire partagée non attachée");exit(0);}
fichier=f+indexfichier;
*fichier=-1000;
/* Initialisation de nl à 0; */
nl=f+indexnl;
*nl=0;
/* Détachement du segment de mémoire partagée*/
shmdt(f); - 90 -
/* Création d'un tableau de 3 semaphores sf, sfifo et mutex*/
sem = semget(IPC_PRIVATE, 3, IPC_CREAT|IPC_EXCL|0660);
if(sem == -1) { printf("\nErreur de création des sémaphore"); exit(2);}
/* Initialisation des sémaphores */
sfinit.val = 1; semctl(sem, sf, SETVAL, sfinit);
/*semctl(sem, sf, SETVAL, 1);*/
semctl(sem, sfifo, SETVAL, 1);
semctl(sem, mutex, SETVAL, 1);
/* création de 10 processus : lecteurs et rédacteurs */
for(i=0; i<10; ++i){
p=fork();
if(p == -1){ printf("\nErreur de création de processus"); exit(5);}
if(p == 0) {
if(getpid()%3==0)
redacteur(); /* c'est un redacteur */
else
lecteur(); /* c'est un lecteur */
}
sleep(2);
} - 91 -
/* Attendre la terminaison des fils */
while( (p = wait(NULL))!=-1 ) printf("\n Fils %d termine", p);

/* Libérer la mémoire partagée*/


shmctl(memp,IPC_RMID, 0);

/* Supprimer les sémaphores */


semctl(sem,0, IPC_RMID, 0);

printf("\n===== Fin du processus père =====\n");


return 0;
}

- 92 -
• Initialisation d’un ensemble de sémaphores avec SETALL
• Dans l’exemple précèdent, l’ensemble est composé de trois sémaphores :
sf, sfifo et smutex.
Les trois sémaphore sont initialisés à 1.
• Soit sinit le tableau qui contient les valeurs initiales de ces 3 sémaphores.
unsigned short int sinit[3]={1,1,1};

union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;

/* Initialisation avec SETALL */


arg.array = sinit;
semctl(sem, 0, SETALL, arg);

- 93 -

Vous aimerez peut-être aussi