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.
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.
1- Programme: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.
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.
Tous droits de reproduction réservés |