TP noté I53 2012

par Joseph Razik, le 2012-12-12

Exercice 1: Détection d'erreurs

On veut simuler très simplement un programme qui détecte les erreurs de transmission (au niveau de la couche liaison) en utilisant, comme code détecteur, un bit de parité paire ajouté à chaque paquet transmis. La taille des paquets transmis sera de \(N=8\) bits. Le bit de parité calculé sera égal à 0 si le nombre de 1 dans le paquet transmis est pair, et sera égal à 1 dans le cas contraire.

Ce programme fera intervenir un serveur (le processus père) et des “clients” (les processus fils). Chaque processus fils enverra au serveur, via un tube anonyme, un (seul) paquet de \(N\) bits suivi du bit de parité qu’il aura calculé. Le serveur, à la réception du paquet, va recalculer le bit de parité sur le paquet reçu, et va le comparer au bit de parité reçu du client. Si les bits de parité sont différents, le serveur affichera le message “ERREUR DE TRANSMISSION”.

Nous nous proposons de résoudre le problème de manière progressive.

Question 1

Écrire le programme exam1v1.c qui va travailler, dans un premier temps, avec 1 seul client (fils).

Le processus client envoie au serveur, grâce à un tube, un seul paquet de \(N=8\) bits (la valeur des bits est générée aléatoirement) suivi du bit de parité que le client calcule lui-même. Le client enverra les bits un à un en tant qu’entiers indépendants, puis le bit de parité. Le client affichera la suite des bits qu’il a transmise, ainsi que le bit de parité. Puis le processus client se termine.

De son côté, le processus serveur s’endort d’abord 5 secondes, puis il lit le paquet reçu du client, ainsi que le bit de parité reçu. Le serveur recalcule le bit de parité sur le paquet reçu. Le serveur affiche le paquet reçu, ainsi que la valeur des 2 bits de parité (celui reçu et celui recalculé). Ensuite il compare les 2 bits de parité. S’ils sont différents il affiche un message d’erreur de transmission.

Exemple d’exécution :

>$ ./exam1v1
Processus client de pid 760 envoie au serveur la suite : 1 1 1 0 1 0 0 0 bit parite : 0
SERVEUR : processus no 1 de pid 760 :
1 1 1 0 1 0 0 0 => parite recue du client=0   parite calculee par serveur=0

Question 2

Recopiez votre programme précédent exam1v1.c dans un autre fichier de nom exam1v2.c

Modifier ce programme exam1v2.c pour gérer maintenant nbProc clients (qui sera un paramètre de votre programme) mais en gérant séquentiellement ces nbProc clients (le serveur crée un processus client puis traite son paquet reçu avant de créer le processus client suivant).

Vérifier que votre programme fonctionne correctement.

>$ ./exam1v2 3           // ici 3 correspond au nombre de clients
= Processus client no 1 de pid 835 envoie au serveur la suite : 1 1 0 0 1 0 1 1 bit parite : 1
*SERVEUR : processus no 1 de pid 835 : 1 1 0 0 1 0 1 1 => parite recue du client=1 parite
calculee par serveur=1
= Processus client no 2 de pid 836 envoie au serveur la suite : 0 0 0 1 0 1 0 1 bit parite : 1
*SERVEUR : processus no 2 de pid 836 : 0 0 0 1 0 1 0 1 => parite recue du client=1 parite
calculee par serveur=1
= Processus client no 3 de pid 837 envoie au serveur la suite : 0 1 0 0 1 0 1 1 bit parite : 0
*SERVEUR : processus no 3 de pid 837 : 0 1 0 0 1 0 1 1 => parite recue du client=0 parite
calculee par serveur=0

Question 3

Recopiez votre programme précédent exam1v2.c dans un autre fichier de nom exam1v3.c.

Modifier ce programme exam1v3.c pour gérer maintenant ces nbProc clients de manière parallèle (le serveur doit créer tous les processus clients, puis ensuite il traitera tous les paquets reçus).

Vous pouvez constater qu’il y a maintenant des problèmes de transmission des paquets (testez votre programme avec au moins 5 clients).

Expliquez en commentaires dans votre programme pourquoi il y a ces problèmes.

Puis, corriger cela dans votre programme en ajoutant un sémaphore (en utilisant les fonctions IPC).




Exercice 2: Au feu les pompiers

Dans cet exercice nous allons utiliser une zone de mémoire partagée pour échanger des informations entre le processus père et les processus fils. Le processus père jouera le rôle de “pompier” et les processus fils “prendront feu” de manière aléatoire. L’intensité des feux des processus fils sera indiquée dans la zone de mémoire partagée.

La zone de mémoire partagée sera utilisée de la façon suivante: Elle contiendra un tableau d’entiers qui représentera pour chaque processus fils *i* l’intensité du feu dans le processus *i*. Au départ, chaque valeur est nulle. Au moment où un feu commence, le processus met la valeur à 1, quand il augmente d’intensité il incrémente la valeur courante de sa case. Si la valeur redevient nulle ou négative, le feu sera considéré comme stoppé.

Le père et les fils auront le comportement suivant: Le processus père va:

  • Creer les processus fils et stocke leur pid dans un tableau
  • Se mettre en attente de la fin de ses fils puis se terminer lui-même
  • Quand un feu se déclare (réception de ``SIGUSR1``), le processus père parcourt la zone de mémoire pour trouver le processus en feu puis, tant qu’il est en feu, il envoie un signal SIGUSR2 au processus fils pour l’éteindre (avec un attente aléatoire entre deux envois de signaux).

Les processus fils vont:

  • “Prendre feu” au bout d’un temps aléatoire:
    • Passer à 1 la valeur lui correspondant dans la zone de mémoire.
    • Envoyer le signal SIGUSR1 au processus père.
  • Au bout d’un temps aléatoire, si le feu est toujours actif, le processus incrémente la valeur de la zone mémoire pour signifie qu’il augmente d’intensité
  • Si le feu est éteint, le processus se termine
  • A la réception d’un signal SIGUSR2, le processus fils décrémente la valeur de la zone pour signifier que le feu diminue d’intensité.

Question 1

Ecrivez le programme exam2v1.c permettant de réaliser ces consignes. Dans un premier temps ne testez qu’avec la génération d’un seul fils. Attention, la valeur de l’intensité d’un feu pourra être négative. Avec un seul fils, vous pouvez essayer les paramètres d’attente suivants: Démarrage fils: attente de 0 à 2 secondes, attente après envoie du signal de démarrage: 0 à 2, attente pour augmenter l’intensité du feu: de 0 à 4. Père: attente entre deux envois de signaux: de 0 à 1. Dans le doute, ne mettre aucune attente pour le père surtout dans le cas de plusieurs processus fils.

Exemple d’exécution :

>$ ./exam2v1 1
shmid : 1998868
Fils 0:attente 2 secondes
Fils 0 de pid 9032 : demarrage de feu => Envoie SIGUSR1
Fils 0: attente 2 secondes
Pere: Reception SIGUSR1
Pere: feu du fils 0 de pid 9032, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Pere: feu du fils 0 de pid 9032, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Fils 0 de pid 9032 : reception SIGUSR2, activite feu = 0
Fils 0 de pid 9032 : reception SIGUSR2, activite feu = -1
Fils 0: fin
Pere: tous les fils termines => fin
>$ ./exam2v1 2
shmid : 2097174
Fils 0:attente 1 secondes
Fils 1:attente 2 secondes
Fils 0 de pid 9396 : demarrage de feu => Envoie SIGUSR1
Fils 0: attente 2 secondes
Pere: Reception SIGUSR1
Pere: feu du fils 0 de pid 9396, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Pere: feu du fils 0 de pid 9396, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Fils 0 de pid 9396 : reception SIGUSR2, activite feu = 0
Fils 0 de pid 9396 : reception SIGUSR2, activite feu = -1
Fils 0: fin
Fils 1 de pid 9397 : demarrage de feu => Envoie SIGUSR1
Fils 1: attente 1 secondes
Pere: Reception SIGUSR1
Pere: feu du fils 1 de pid 9397, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Pere: feu du fils 1 de pid 9397, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Pere: feu du fils 1 de pid 9397, intensite du feu = 1 => envoie de SIGUSR2
Pere: attente 0 secondes
Fils 1 de pid 9397 : reception SIGUSR2, activite feu = 0
Fils 1 de pid 9397 : reception SIGUSR2, activite feu = -1
Fils 1: fin
Pere: tous les fils termines => fin

Question 2

Expliquez pourquoi la valeur de l’intensité d’un feu peut être négative.

Question 3

Nous allons changer un peu le comportement du programme. Recopiez votre programme précédent exam2v1.c dans un autre fichier de nom exam2v2.c. Modifiez ce fichier exam2v2.c afin de tenir compte des modifications suivantes: En plus des processus fils qui peuvent prendre feu, le processus père va également créer d’autres processus fils qui auront eux le rôle de processus pompier. Le nombre de processus pompiers sera donné en second argument à votre programme. La communication entre le processus père et les processus pompiers se fera à l’aide d’un tube, cette communication sera le pid du fils en feu.

Les processus fils pompiers auront le comportement suivant:

  • Attendent dans un tube qu’un numéro de processus arrive (pas le pid mais le numéro de processus: 0, 1 ..)
  • Tant que le feu de ce processus est actif, il envoie des signaux SIGUSR2 à ce processus avec une attente aléatoire entre chaque envoie (entre 0 et 1 ou 2 secondes).
  • Quand le tube est cassé, le processus se termine

Par l’introduction de ces processus pompiers, le comportement du processus père va être modifié ainsi:

  • A la réception d’un signal SIGUSR1 de la part d’un fils, il va chercher un fils ayant un feu actif et va alors envoyer dans un tube le numéro de ce fils (pas son pid, son numéro), puis continuer ses occupations.
  • Pour bien gérer la fin de ses fils feux et pompiers, à la réception d’un signal SIGUSR2 par le père, celui-ci va décrémenter le nombre de processus feux existant
  • Lorsqu’il n’y a plus de processus feu, le processus père va effectuer une action afin de prévenir ses fils pompiers qu’ils doivent se terminer (il ne les termine pas directement)

Le comportement des processus feu n’est que légèrement modifié afin qu’avant de se terminer, ils envoient au processus père un signal SIGUSR2 pour indiquer qu’ils vont se terminer.

Voici un exemple d’exécution d’un tel programme:

$ ./exam2v2 2 2
Fils 0:attente 0 secondes
Fils 0 de pid 4137 : demarrage de feu => Envoie SIGUSR1
Fils 0: attente 2 secondes
Pere: Reception SIGUSR1
Pere: feu du fils 0 de pid 4137, intensite du feu = 1 => envoie de son numero dans le tube
Fils 1:attente 2 secondes
Pompier 0: feu du fils 0 de pid 4137, intensite du feu = 1 => envoie de SIGUSR2
Fils 0 de pid 4137 : reception SIGUSR2, activite feu = 0
Fils 0: fin
Pere: reception SIGUSR2
Pompier 0: attente 1 secondes
Fils 1 de pid 4138 : demarrage de feu => Envoie SIGUSR1
Fils 1: attente 1 secondes
Pere: Reception SIGUSR1
Pere: feu du fils 1 de pid 4138, intensite du feu = 1 => envoie de son numero dans le tube
Pompier 0: feu du fils 1 de pid 4138, intensite du feu = 1 => envoie de SIGUSR2
Pompier 0: attente 1 secondes
Fils 1 de pid 4138 : reception SIGUSR2, activite feu = 0
Fils 1: fin
Pere: reception SIGUSR2
Pompier 1: tube casse => terminaison
Pompier 0: tube casse => terminaison
Pere: tous les fils termines => fin