Concurrence dans les systèmes informatiques
Travaux Pratiques
Informations générales
|
Langage: C ou C++, Système: HP-UX (station HP série 700).
La programmation sera de style "orientée objet", c'est à dire que les structures de synchro-communication seront pré-déclarées en tant que type, et ne seront pas directement manipulées par le programme d'application, mais par l'intermédiaire des procédures associées (par exemple: structure de sémaphore, procédures p() et v() )
On fera nettement apparaître
deux couches de logiciel :
- La couche de service (structures et procédures)
- La couche application (démonstration s'appuyant sur la couche
de service).
Les exemples sont donnés en langage C et, à moins d'être très à l'aise en programmation C++, il est conseillé de réaliser l'ensemble des exercices en C. Il est habituel de profiter de ces TP pour apprendre le C, par différence avec le C++. Un résumé des principales différences est donné pour l'ANSI-C.
Outre ce texte de TP, vous devez lire les Informations importantes, la description de l'interface (Procédures spécialement écrites pour le TP), et vous pourrez également avoir besoin de la documentation Unix sur les appels système (man).
Contenu du rapport
A titre d'exemple, pour chaque exercice: l'énoncé, une présentation de la structure et des particularités de votre programme, sur son comportement pendant l'exécution, sur la réentrance dans vos procédures de service, les difficultés rencontrées, le source, la trace de l'exécution, les jeux d'essais, etc...
Exercice 1
Mise en évidence d'un conflit lors de l'accès à une ressource non partageable.
Ce programme servira de canevas pour les exercices suivants.
Ecrire un programme composé de deux processus appelés: Ecrivain et Lecteur, et d'une variable (chaîne de caractères) commune (ce sera la ressource non partageable), appelée Buffer.
Pour aller plus vite, recopiez le fichier canevas.c dans votre répertoire et modifiez le corps de l'écrivain et du lecteur.
Un programme écrit d'après ce canevas doit être arrêté par CONTROL-C. Il est donc préférable d'utiliser canevas2.c qui s'arrête de lui-même au bout de 30 secondes, et qui permet d'afficher des valeurs provenant des processus fils; par contre la compréhension de ce fichier est plus difficile.
L'écrivain inscrit une chaîne qui est lue par le lecteur, d'une manière asynchrone (sans aucun mécanisme de contrôle d'accès à la ressource).
L'écrivain écrit cycliquement, à la période T1. Une période se décompose en un temps d'écriture Te, et un temps Tb passé en boucle d'attente active (simulation d'une activité quelconque): T1=Te+Tb. On écrit successivement 2 chaînes complètement différentes (par exemple "+++++" et "-----").
Le lecteur lit cycliquement Buffer, à la période T2, qui se décompose en un temps de lecture Tr, et un temps Ts=0,5s où le processus est endormi (attente passive): T2=Tr+Ts. Le lecteur, connaissant à l'avance ce qu'il doit lire (soit "+++++", soit "-----"), vérifie l'intégrité de la donnée.
Réaliser le programme, en utilisant les appels
système (a.s.) ; voir interf.h
ou man :
Etudier le taux de conflit (Nombre d'erreurs en lecture / Nombre de lectures) en fonction du rapport Te/T1 (proportion de temps que l'écrivain passe en section critique). On peut mesurer les temps avec les a.s. start_chrono et read_chrono (interf.h).
Cette étude est à réaliser en dehors des séances de TP.
Exercice 2
Exclusion mutuelle par verrouillage de l'ordonnanceur (a.s. lock et unlock dans interf.h)
Un processus peut se réserver le processeur pour exécuter une section critique, soit en bloquant l'ordonnanceur, voire même en rendant le processeur ininterruptible.
- Quels sont les inconvénients de cette
méthode ?
- Vérifier ce fonctionnement sur le programme
précédent.
Remarque: En mode User sous Unix, il n'est pas possible de réaliser un verrouillage véritable de l'ordonnanceur. Les a.s. lock et unlock ne font qu'une simulation de ce mode de fonctionnement. C'est un code de politesse que tous les processus doivent employer pour accéder à la donnée partagée, en prenant soin de ne pas se réserver la donnée lorsque ce n'est pas absolument nécessaire.
Exercice 3
Construction des mécanismes d'endormissement-réveil de processus
Les mécanismes des exercices suivants s'appuient sur cet exercice.
Chaque processus Unix est identifié par son numéro: le pid (Process IDentifier), retourné par getpid() ou create_task (interf.h).
Une file d'attente de processus utilise un tableau de pid,
géré comme un buffer circulaire.
Ecrire dans un fichier séparé une structure file d'attente
fifo avec ses procédures
get_fifo et
put_fifo,
voire init_fifo,
est_vide, et
est_pleine.
La fifo étant accédée tant par le maitre que par
l'esclave, elle devra résider en mémoire partagée.
Un moyen simple est de mettre les déclarations et le code dans un fichier fifo.h, inclus au début du .c, mais une maniere plus propre est de faire de la compilation séparée (==> fifo.h fifo.c), ce qui implique de recopier et modifier compsep. Il est d'autre part impératif de tester séparément la gestion de file (==> creer un petit main de test).
Voir quelques conseils de réalisation.
Ecrire (dans un fichier séparé) une procédure stop(file d'attente): le processus appelant range son pid dans la file donnée en paramètre et s'endort (a.s. arret (interf.h) )
Un processus qui appelé la fonction arret() se trouve endormi et ne peut donc pas retourner de lui-même au programme appelant. Il pourra être réveillé grâce à un autre processus qui lui envoie un signal. Un signal (Unix) est une interruption émise par un processus vers un autre processus. Le processus émetteur utilise l'a.s. kill (unix) pour générer le signal. Dans ces TP, le signal SIGUSR1 servira à réveiller le processus destinataire (retour de l'a.s. arret).
La procédure marche(pid) est fournie pour cela, et elle prend en paramètre le pid du processus à réveiller.
Ecrire (dans le même fichier que stop) une fonction go(file d'attente) qui extrait le 1er numéro de processus de la fifo, réveille ce processus, et retourne son pid.
Écrire deux processus : l'un, appelé esclave, est cyclique. Dans un cycle de l'esclave, le processus s'endort d'abord par le stop. Au bout d'un certain temps, il est réveillé par le maitre, et alors affiche un message.
L'autre processus (le maitre) est aussi cyclique. Un cycle du maître est composé d'un temps d'endormissement (1 sec), puis le maître réveille l'esclave, et affiche le pid de ce dernier.
Créer plusieurs esclaves qui affichent des messages personnalisés et diminuer le temps d'attente du maître.
Exercice 4
Construction d'un mécanisme de sémaphore
Définir dans un fichier séparé une structure sémaphore sema (fifo de processus + compteur), puis dans un autre fichier, les procédures init_sema (initialisation de la structure), p() et v().
Application: On considère un sémaphore s, et deux processus : l'un, appelé esclave, appelle p(s), puis affiche le compteur du sémaphore, s'endort (1s) et enfin reboucle sur le p(s). L'autre processus (le maître), attend un caractère entré au clavier, réveille l'esclave par v(s), affiche son pid et boucle sur l'entrée clavier.
Etudier l'effet du nombre initial de jetons, et de la cadence d'entrée au clavier sur l'exécution du programme.
Exercice 5
Exclusion mutuelle par sémaphore
Reprendre le programme de l'exercice 2, en installant le mécanisme d'exclusion mutuelle par sémaphore, et vérifier que le taux d'erreur est nul
Exercice 6
Protocole Lecteurs-Ecrivains (priorité aux lecteurs)
Définir une structure Lecteurs-Ecrivains, et 5 procédures: init_LE (initialisation de la structure), p_L, v_L pour les lecteurs et p_E, v_E pour les écrivains.
Sur le modèle du programme de l'exercice 2, en installant le mécanisme Lecteurs-Ecrivains, vérifier que le taux d'erreur est nul.
Exercice 7
Protocole Producteurs-Consommateurs
Définir une structure Producteurs-Consommateurs (les messages seront des chaînes de caractères), et 3 procédures init_PC (initialisation de la structure), produire, consommer.
Application 7.1
Sur le modèle du programme de l'exercice 4, en installant le mécanisme Producteurs-Consommateurs à la place du sémaphore: Le maître produit des chaînes de caractères entrées au clavier, et l'esclave les consomme.
- Comparer la synchronisation obtenue avec celle de
l'exercice 4.
- Le maître peut-il écraser un message non lu par l'esclave?
- L'esclave peut-il lire plusieurs fois le même message,
ou une case vide?
Application 7.2
Construire un système montrant le dialogue entre 2 clients et un serveur:
- Le serveur gère une ressource (chaîne
de caractères)
- Les clients émettent des requêtes de lecture ou
écriture vers le serveur, et attendent des
accusés-réception de ce dernier, en lecture.
ATTENTION ! POUR TOUS LES EXERCICES :
Ne pas oublier d'initialiser l'interface par init_interf();