1. Combats de fork et de threads
30. Combat à coup de Signaux

Vous créerez dans votre répertoire personnel, s'il n'est pas déjà là, le sous-répertoire "public_html" dans lequel vous placerez toutes vos réponses, qui y seront automatiquement ramassées. Seuls les fichiers demandés le seront, aussi il est impératif de bien contrôler leur création: nom exact (sans confondre minuscules et majuscules, tiret et souligné etc, ni placer des espaces autour du nom) et droit d'accès 640 pour un fichier non exécutable et droit d'accès 750 pour ses répertoires parents ou un fichier exécutable. Dans le cas où une réponse nécessite les fonctions écrites dans une réponse précédente, celles-ci ne doivent pas être recopiées, le recours au mécanisme d'inclusion des langages de programmation étant plus fiable et plus lisible.

Nous allons construire une application où deux processus s’envoient respectivement des signaux avec kill pour simuler un combat. Le principe est que chaque combattant (processus) alterne entre une phase de défense où il se protège (en masquant les signaux), et une phase d’attaque où il envoie un signal à l’adversaire (attaque) mais devient en contrepartie vulnérable aux signaux pendant un moment.

On va modéliser également des points de vie, c’est-à-dire que le fait de recevoir un signal quand on est vulnérable (dans sa phase d’attaque) va décrémenter un compteur (initialement trois vies). Quand le compteur atteint 0, le processus meurt et rend la valeur 1. Quand l’attaquant détecte la mort de son adversaire, c’est-à-dire que son pid n’existe plus (ce qui cause une exception sur kill), il meurt également et rend la valeur 0.

Le temps passé en phase de défense (signal masqué) ou d’attaque (signal reçu) est aléatoire et compris entre 0,3 et 1 seconde.

L’énoncé demande d’écrire deux fonctions relativement indépendantes, on peut écrire la deuxième sans avoir réussi à écrire la première. Une troisième question consiste en des interrogations sur les possibilités d’évolution du jeu.


Warning: fsockopen(): unable to connect to ssl://www-master.ufr-info-p6.jussieu.fr:8083 (Connection refused) in /dsk/www-master/html/2017/ecrire/inc/distant.php on line 699

Warning: fsockopen(): unable to connect to ssl://www-master.ufr-info-p6.jussieu.fr:8083 (Connection refused) in /dsk/www-master/html/2017/ecrire/inc/distant.php on line 699

Warning: fsockopen(): unable to connect to ssl://www-master.ufr-info-p6.jussieu.fr:8083 (Connection refused) in /dsk/www-master/html/2017/ecrire/inc/distant.php on line 699

1 Création des processus (10  point(s) Coeff 5)

Écrire un programme P dont le main crée deux processus fils F1 et F2 à l’aide de la fonction C fork.

À ce stade on demande que F1 se termine avec la valeur de sortie 0 ; et que F2 se termine avec la valeur de sortie 1 (donc F1 gagne tout le temps le combat).

P doit attendre la fin de ses deux fils, et afficher leur valeur de sortie. Attention, on ne doit pas présumer de l’ordre dans lequel les fils meurent, il faut donc utiliser waitpid à bon escient pour récupérer les valeurs de sortie des fils dans n’importe quel ordre.

P connaît naturellement les PID de ses deux fils, et F2 peut hériter de son père la connaissance du PID de son grand frère F1. On demande d’utiliser un pipe pour véhiculer l’information restante, c’est-à-dire pour communiquer le PID de F2 (second fils) au processus F1 (grand frère).

Un processus (soit P soit F2) doit écrire dans le pipe le PID de F2, et F1 doit commencer par lire dans le pipe ce PID. On prendra soin de fermer les descripteurs du pipe aux endroits appropriés.

Exemple d’exécution :

Processus 27963, P=27963, F1=27964, F2=27965
Processus 27965, P=27963, F1=27964, F2=27965
Processus 27964, P=27963, F1=27964, F2=27965
Processus fils 27965 est mort avec valeur 1
Processus fils 27964 est mort avec valeur 0
Barème (Coeff 5)
  • compilation sans warning grave (implicit function declaration, pointer from integer, conversion to non scalar....) Exécution sans faute grave (SIGSEGV, mort immédiate...)
  • P crée deux fils avec fork
  • F1 meurt 0, F2 meurt 1
  • P wait ses fils
  • P wait ses fils dans le désordre
  • P affiche la valeur de sortie des fils
  • P et F2 affichent les pid de tout le monde
  • Création du pipe et écriture dans le pipe
  • Lecture dans le pipe + affichage de F1
  • close des pipe

2 Combat (9  point(s) Coeff 5)

Définir les deux fonctions suivantes :

Définir une fonction « void combat(pid_t adversaire) » qui boucle indéfiniment sur une défense suivie d’une attaque et invoquez-la dans le corps des deux fils.

Pour endormir un processus pour une durée aléatoire, utiliser la fonction :

#include <time.h>

void randsleep() {
 int r = rand();
 double ratio = (double)r / (double) RAND_MAX;
 struct timespec tosleep;
 tosleep.tv_sec =0;
 // 300 millions de ns = 0.3 secondes
 tosleep.tv_nsec = 300000000 + ratio*700000000;
 struct timespec remain;
 while ( nanosleep(&tosleep, &remain) != 0) {
   tosleep = remain;
 }
}

Exemple d’exécution (on s’attend à voir des résultats différents à chaque exécution) :

Processus 31604, P=31604, F1=31605, F2=31606
Processus 31605, P=31604, F1=31605, F2=31606
Processus 31606, P=31604, F1=31605, F2=31606
Attaque reçue par 31605; PV restants 2
Attaque reçue par 31605; PV restants 1
Attaque reçue par 31606; PV restants 2
Attaque reçue par 31606; PV restants 1
Attaque reçue par 31606; PV restants 0
Plus de vie pour 31606; mort du processus.
Processus fils 31606 est mort avec valeur 1
Detection de Mort de l'adversaire de pid=31606
Processus fils 31605 est mort avec valeur 0
Barème (Coeff 5)
  • compilation sans warning grave (implicit function declaration, pointer from integer, conversion to non scalar....) Exécution sans faute grave (SIGSEGV, mort immédiate...)
  • On a une variable globale PV, initialisée à 3
  • attaque positionne un handler
  • le handler décrémente les PV
  • le handler fait mourir le processus avec 1 si 0 PV
  • attaque envoie un signal
  • attaque dort au bon moment (signaux actifs)
  • defense positionne sig ignore
  • défense dort au bon moment (signaux désarmés)

3 Parer les coups (7  point(s) Coeff 4)

On propose de modifier la défense de F1 pour qu’il affiche les coups parés. On propose de procéder de la manière suivante :

Barème (Coeff 4)
  • Q1 : permet si l’on est interrompu de tout de même finir de dormir pour la bonne durée (i.e. on explique les variables tosleep, remain)
  • Q1 : on précise que l’interruption en question peut être due à la réception d’un signal
  • Q2 : uniquement en attaque en vrai ; en défense le signal sera ignoré donc ne sortira pas du sleep
  • Q3 : il faut utiliser srand
  • Q3 : on peut lui passer l’heure (time), le pid...
  • Q4 : non il n’est plus équitable
  • Q4 : car F1 attends un coup pour se réveiller avec l’approche proposée (il va forcément parer).