Synchronisation de Processus
Les sémaphores

      On souhaite gérer l'écriture dans un fichier unique, de deux processus, en mettant en œuvre un dispositif de sémaphore. On souhaite que les deux processus écrivent chacun leur tour dans le fichier (alphabet minuscule pour l'un et majuscule pour l'autre). Le droit (tour) d'écriture sera donné par le(s) sémaphore(s).
      Nous présenterons ici les cas correspondant à l'utilisation d'un sémaphore et de deux sémaphores.




A) Cas d'un seul sémaphore:

              1- Programme:

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<unistd.h>
#include<stdio.h>
 
 main()
{
                int pid_P2,clef,sema;
                char i;
                ushort tab_sema[1];                        // tab_sema est un tableau d'un sémaphore (non utilisé pour 1 sémaphore)
                struct sembuf operation [1];          // operation est une structure de type sembuf qui contient 3 champs
               
                FILE *p ;
                p=fopen("fic2.txt","w");                     // p est le descripteur du fichier fic2.txt
                if (p==NULL)                {                perror("pb ouverture de fichier");
                                                               exit(0);                }
 
                /*                calcul de la clef universelle du sémaphore                */
                clef=ftok("fic2.txt",8);
 
                /*                initialisation de la structure "sembuf operation"*/
                /*                nous utiliserons toujours le même sémaphore, il suffit donc de déclarer son numéro une seule fois */
                /*                ensuite à chaque opération, nous ne modifierons que le champ sem_op (voir boucle for)                */
                operation[0].sem_num=0;                 // sémaphore numéro 0
                operation[0].sem_flg=0;                    // opération bloquante
 
                pid_P2=fork();                      //on duplique un processus pour en créer un second
               
                /*                création du sémaphore                */
                sema=semget(clef,1,IPC_CREAT|0600);
                if (sema==-1)                {                perror("pb semget");
                                                               exit(0);                }
 
                /*                initialisation du sémaphore                 */
                semctl(sema,0,SETVAL,1);
 
                if (pid_P2==0)                {                              /* code du fils */
                                                                              for (i='a';i<'z'+1;i++) {
                                                                             
                                                                                              /*                prise du sémaphore                */
                                                                                              operation[0].sem_op=-1;                // reviens à décrémenter le sémaphore
                                                                             
                                                                                              /*                validation de la prise du sémaphore                */
                                                                                              /*                prise en compte des 3 champs de la structure operation*/
                                                                                              semop(sema,operation,1);
                                                                                              /*                écriture dans le fichier                */

fprintf(p,"%c",i);

                                                                               fflush(p);

 
                                                                                              /*                libération su sémaphore                */
                                                                                              operation[0].sem_op=+1;                // reviens à incrémenter le sémaphore
                                                                             
                                                                                              /*                validation de la libération du sémaphore                */
                                                                                              /*                prise en compte des 3 champs de la structure operation*/

                                                semop(sema,operation,1);
                              

                                }                // fin boucle for
                                                                              exit(0);
                                                                              }
 
                                               else                {                /* code du père */
                                              
                                                                              for (i='A';i<'Z'+1;i++) {
                                              
                                                                                              /*                prise du sémaphore                */
                                                                                              operation[0].sem_op=-1;
                                                                             
                                                                                              /*                validation de la prise du sémaphore                */
                                                                                              /*                prise en compte des 3 champs de la structure operation*/

                                                                               semop(sema,operation,1);
               

                                                                               /*                écriture dans le fichier                */
                                                                                              fprintf(p,"%c",i);
                                                                                              fflush(p);
 
                                                                                              /*                libération su sémaphore                */
                                                                                              operation[0].sem_op=+1;
                                                                             
                                                                                              /*                validation de la libération du sémaphore                */
                                                                                              /*                prise en compte des 3 champs de la structure operation*/
                                                                                              semop(sema,operation,1);
                                                                             
                                                                              }                // fin boucle for
                                                                             
                                                                              sleep(2);                                // endors le père pendant 2 s
                                                                              exit(0);
                                                                              }
                fclose(p);             
}


              2- Commentaires et Analyse:

      Pour utiliser un sémaphore sous LINUX, plusieurs opérations sont à effectuer. Il faut tout d'abord créer une clef d'identification pour le sémaphore (ftock), créer le sémaphore à proprement (semget) dit puis l'initialiser (structure sembuff et semctl) avant de l'utiliser (strucure sembuff et semop).

      On constate dans le fichier de sortie " fic2.txt " que les minuscules et majuscules ne sont pas enchevêtrées comme nous le souhaitions, mais qu'elles sont toutes écrites d'un bloc (toutes les majuscules et minuscules à la suite). L'explication est donnée par le mode de fonctionnement du système LINUX qui fonctionne en temps partagé.

      LINUX attribue à chaque processus une durée bien précise qui n'est pas proportionnelle au travail à réaliser par le processus (ceci pour assurer, entre autre, le multi-tâches). Dans notre cas, avec deux processus (et un seul sémaphore), cette durée est si grande, qu'elle permet à chacun d'entres eux de prendre le sémaphore, de réaliser l'écriture et de vendre le sémaphore d'une traite pour toutes les lettres de l'alphabet (validité de la boucle for) sans laisser entre temps la main à l'autre processus.

B) Cas de deux sémaphores:

      Pour résoudre le problème de temps partagé, nous utilisons maintenant deux sémaphores. Il faut savoir que lorsqu'un processus souhaite faire une prise d'un sémaphore non disponible, alors le système d'exploitation met en attente ce processus et exécute le suivant. C'est comme cela que nous parviendrons à éviter l'écriture d'une seule traite et à assurer l'alternance.
              1- Programme:

#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/sem.h>
#include<unistd.h>
#include<stdio.h>
 
main()
{
                int pid_P2,clef, sema;
                char i;
                ushort tab_sema[2];                        // tab_sema est un tableau de deux sémaphores
                struct sembuf operation [1];          // operation est une structure de type sembuf
 
                FILE *p;
                p=fopen("fic3.txt","w");
                if (p==NULL)                {                perror("pb ouverture de fichier");
                                                               exit(0);                }
 
                /*                calcul de la clef d'identification de l'ensemble de 2 sémaphores                */
                clef=ftok("fic3.txt",4);
 
                /*                initialisation des deux sémaphores                */
                tab_sema[0]=1;                   // le sémaphore 0 est disponible
                tab_sema[1]=0;                   // le sémaphore 1 est indisponible
 
                /*                déclaration d'une variable sémaphore                */
                sema=semget(clef,2,IPC_CREAT|0600);
                if (sema==-1)                {                perror("pb semget");
                                                               exit(0);                }
 
                /*                initialisation du sémaphore avec les valeurs initiales  */
                semctl(sema,2,SETALL,tab_sema);
 
                /*                on duplique le processus en cours pour en créer un second                */
                pid_P2=fork();
 
                if (pid_P2==0)                {                              /* code du fils */
 
                                                                              for (i='a';i<'z'+1;i++) {
 
                                                                                              /*                prise du sémaphore 0                */
                                                                                              operation[0].sem_num=0;                 // sémaphore numéro 0
                                                                                              operation[0].sem_flg=0;                    // opération bloquante
                                                                                              operation[0].sem_op=-1;
 
                                                                                              /*                validation de la prise du sémaphore                */
                                                                                              semop(sema,operation,1);
 
                                                                                              /*                écriture dans le fichier                */
                                                                                              fprintf(p,"%c",i);
                                                                                              fflush(p);
 
                                                                                              /*                libération su sémaphore                1*/
                                                                                              operation[0].sem_num=1;                 // sémaphore numéro 1
                                                                                              operation[0].sem_flg=0;                    // opération bloquante
                                                                                              operation[0].sem_op=+1;
 
                                                                                              /*                validation de la libération du sémaphore                */
                                                                                              semop(sema,operation,1);
 
                                                                              }                // fin boucle for
 
                                                                              exit(0);
 
                                                                              }
                                               else                {                /* code du père */
 
                                                                              for (i='A';i<'Z'+1;i++) {
 
                                                                                              /*                prise du sémaphore 1                */
                                                                                              operation[0].sem_num=1;                 // sémaphore numéro 1
                                                                                              operation[0].sem_flg=0;                    // opération bloquante
                                                                                              operation[0].sem_op=-1;
 
                                                                                              /*                validation de la prise du sémaphore                */
                                                                                              semop(sema,operation,1);
 
                                                                              /*                écriture dans le fichier                */
                                                                                              fprintf(p,"%c",i);
                                                                                              fflush(p);
 
                                                                                              /*                libération su sémaphore                0*/
                                                                                              operation[0].sem_num=0;                 // sémaphore numéro 0
                                                                                              operation[0].sem_flg=0;                    // opération bloquante
                                                                                              operation[0].sem_op=+1;
 
                                                                                              /*                validation de la libération du sémaphore                */
                                                                                              semop(sema,operation,1);
 
                                                                              }                // fin boucle for
 
                                                                              sleep(2);                // endors le père pendant 2 s
                                                                              exit(0);
                                                                              }
                fclose(p);
}
 


              2- Commentaires et Analyse:

      Le mode opératoire pour mettre en place deux sémaphores (ici, un tableau de deux sémaphores est utilisé alors qu'il aurait été également possible de déclarer deux sémaphores bien distincts avec une clef différente pour chacun d'entre eux) est semblable à celui du cas précédent ; à quelques nuances près :

  • initialisation des sémaphores simultanément, grâce à un tableau de type unsigned short tab_sema
  • à chaque action sur un sémaphore, on déclare les 3 champs de la structure sembuff operation contre 1 seul précédemment
  • d'ou utilisation de SETALL au lieu de SETVAL dans semctl

      Le choix d'un sémaphore (pour passer du père au fils et inversement) se fait en positionnant le champ sem_num de la structure sembuff operation soit à 0, soit à 1.

      On constate que chacun des deux processus utilise les deux sémaphores. Le principe mis en œuvre est tel que l'un des processus prend le sémaphore 0 et vend le sémaphore 1, alors que l'autre processus fait l'inverse, c'est à dire prend le sémaphore 1 et vend le 0. Ce mécanisme assure une parfaite synchronisation entre les processus, à la condition évidemment que les sémaphores soient correctement initialisés (on détermine ainsi lequel des deux processus démarrera l'écriture dans le fichier). Cette initialisation de fait via tab_sema dans le programme.

      Il faut néanmoins prendre garde à la manière dont chaque processus écrit dans le fichier. Nous utilisons pour cela l'instruction fprintf() qui est une instruction bufférisée (f…()), c'est à dire qu'avant d'écrire dans le fichier, cette fonction passe par un tampon (buffer) qui sert d'intermédiaire. Afin de vider ce tampon et donc d'écrire effectivement dans le fichier, nous utilisons l'instruction fflush() après chaque écriture. Les lettres sont alors immédiatement recopiées du tampon vers le fichier. On peut ajouter que cette fonction est indispensable dans ce cas par rapport au cas à un sémaphore, sans quoi le tampon ne serais vidé qu'à la fin de l'écriture des minuscules et majuscules ; le fichier de sortie ne contenant alors pas le résultat souhaité.

      Le résultat dans le fichier est conforme à ce que l'on attendait. Les minuscules et majuscules sont enchevêtrées (aAbBcCdDeEfF…).


Remarque : dans les deux cas présentés ici, nous n'avons testé que quelques erreurs seulement puisque les programmes implémentés étaient d'une complexité limité. En toute rigueur, il faudrait faire la récupération des erreur pour chaque fonction utilisée.

Franck HEURTAULT - DESS AEII
Octobre-Novembre 2000




© La Maison du Kineton - 2001
Tous droits de reproduction réservés