ESIEE I3 IN301/IN3ST01 

Systèmes d'exploitation - UNIX

TD-Machine : Communication et synchronisation entre processus et entre threads

Objectifs

  1. Comprendre et mettre en oeuvre un mécanisme de tubes
  2. Mettre en oeuvre un premier programme multithreadé
  3. Comprendre et mettre en oeuvre des solutions de synchronisation entre threads
  4. Comprendre et mettre en oeuvre une communication inter-processus par signal
  5. Continuer la programmation de bibliothèques utiles pour le projet IN3(ST)01

Remarques préliminaires

Consigne générale: on écrira un makefile permettant de compiler chacun des programmes demandés. A la fin du TD, le fait d'exécuter la commande 'make' devra recompiler chacun des exercices de ce TD.

I - Communications inter-processus

Dans le projet IN3(ST)01, vous allez considérer plusieurs processus qui correspondent entre autres à des serveurs. Le comportement simplifié d'un serveur consiste à répéter indéfiniment trois étapes :

  1. Lire un message
  2. Analyser son contenu
  3. Transmettre le message

Dans cet exercice vous allez simuler l'échange de messages entre deux serveurs A et B. Voici une brève description du fonctionnement de ces deux serveurs. Le serveur A lit des messages depuis une source (étape 1), analyse leur contenu (étape 2), et en fonction de cette analyse choisit ou non de les transmettre au serveur B via un "canal de transmission" (étape 3). Symétriquement, le serveur B écoute les messages envoyés par le serveur A sur le "canal de transmission" (étape 1), analyse leur contenu (étape 2), et en fonction de cette analyse choisit ou non de les transmettre à une certaine destination (étape 3).

A - Dessinez sur papier le schéma qui correspond à la description donnée ci-dessus. Pour cela, vous pouvez utiliser le code graphique donné ci-dessous et supposer que la source et la destination sont des fichiers texte.

graphique

L'objectif des deux questions suivantes est de mettre en oeuvre ce que vous avez dessiné sur papier.

B - Bibliothèque de lecture/écriture de fichier

1) Pour commencer, vous ajouterez à la bibliothèque de l'exercice III ("Lecture écriture de fichier et héritage des processus") du premier TD une fonction permettant d'écrire une chaîne de caractère dans un fichier.

2) Pour tester votre nouvelle fonction ,vous modifiez, le programme de la question III-B-1 du TD1 en remplaçant l'affichage via la bibliothèque d'affichage par une écriture dans un fichier texte que vous appelez CanalDeTransmission.txt.

3) Que se passe-t-il si au lieu d'écrire dans le descripteur de fichier correspondant à CanalDeTransmission.txt vous passez la valeur 1 comme descripteur de fichier à votre fonction d'écriture ? Comment modifier votre programme pour qu'au lieu de lire dans un fichier, il lise la ligne qui est tapé au clavier, c'est à dire sur l'entrée standard ? Effectuez les modifications nécessaires pour cela.

C - Vous allez maintenant implémenter le programme qui correspond au schéma que vous avez dessiné.

1) Quelles méthodes voyez vous pour implémenter le "Canal de transmission" entre les serveurs A et B ? Dans quel cas utilisez vous une méthode plutôt qu'une autre ?

2) Écrivez un programme qui se dédouble et pour lequel le père exécute le code correspondant au serveur A et le fils celui du serveur B. Laquelle des méthodes proposées à la question I-B-1 est la plus adaptée ? Pour simplifier, les étapes de traitement (numérotées 2) sont supposées vides et les serveurs choisissent toujours de transmettre les messages. Le serveur A effectue la lecture de tous ses messages à partir du fichier texte Source.txt. Le serveur B écrit tous ses messages dans un fichier texte que vous nommez Destination.txt.

II - Threads, données partagées et synchronisation de threads

Il est souvent intéressant de diviser un serveur en threads : un (ou plusieurs) thread(s) pour lire dans une (ou plusieurs) source(s) et un (ou plusieurs threads) chargé(s) de transmettre des données. Dans ce contexte les threads chargés de la lecture et ceux chargés de l'écriture doivent partager une zone de la mémoire. On a donc le schéma simplifié suivant :

threads

L'objectif de cet exercice est d'implanter le schéma ci-dessus. L'étape 1 du thread 1 et l'étape 2 du thread 2 seront réalisées avec votre bibliothèque de lecture et écriture de fichiers. La source sera le fichier texte donné dans l'exercice précédent, la destination sera un fichier texte Destination.txt,

A - Implantez le schéma ci-dessus. La mémoire partagée sera un pointeur vers une chaîne de caractères déclaré comme variable globale par : 'char *c;' .

B - Quel est le problème de la solution A ? Proposez (sans l'implémenter) un schéma de solution au problème de A.

Remarque importante : Pour compiler un programme qui se sert des threads, il faut utiliser la commande 'gcc -lpthread'.

III - Processus et signaux

L'objectif de cet exercice est d'implémenter un programme qui simule l'ouverture d'un coffre fort protégé par le code secret "1234". Au lancement du programme, l'utilisateur est invité à taper au clavier un code secret. Si l'utilisateur met plus de 10 secondes à entrer un code alors le programme affiche "Trop tard : coffre-fort fermé". S'il tape un mauvais code alors le programme affiche "Code faux : coffre-fort fermé". Sinon, le programme affiche "Bravo : coffre-fort ouvert".

Pour réaliser cette simulation, vous utiliserez deux processus. Le premier est chargé de lire et vérifier le code tapé sur l'entrée standard. Le second comptera 10 secondes et signalera au premier processus qu'il est trop tard. Pour compter 10 secondes, vous pouvez utiliser la fonction sleep.

IV - Threads concurrents (questions de cours)

On a 3 threads P1, P2, et P3 concurrents. Le code indépendant de chaque thread Pi (i=1,2,3) est le suivant :


 P(int i) // thread Pi avec i=1,2,3
 {
   while(TRUE)
     printf("je suis le thread %d\n", i);
 }

 je suis le thread 1
 je suis le thread 2
 je suis le thread 3
 je suis le thread 1
 je suis le thread 2
 je suis le thread 3
 ...
Synchroniser les 3 threads à l'aide des sémaphores binaires.