Programmation Système
TP n° 3
(Signaux)




Notions abordées :


Exercice 1.

Expliquer le fonctionnement du programme suivant.
   
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
struct sigaction action;
void hand_sigusr1(int sig){
       printf("(de %d) Signal SIGUSR1 recu\n", getpid());
       exit(0);
}
int main() {
  pid_t pid;
  int i = 0;
  int status;
  action.sa_handler=hand_sigusr1;
  sigaction(SIGUSR1,&action,NULL);
  if((pid=fork())==0){
     printf("Je suis le fils de PID %d\n", getpid());
     /* processus fils bouclant */
     while(1) { i = 0; };
  }
  printf("Je suis le pere de PID %d\n", getpid());
  if(kill(pid,0)==-1){
     printf("(de %d) Fils %d inexistant\n", getpid(), pid);
  }
  else{
     printf("(de %d) Envoi du signal SIGUSR1 au processus %d\n", getpid (), pid);
     kill(pid,SIGUSR1);
  }
  pid=waitpid(pid,&status,0);
  printf("(de %d) Status du fils %d : %d\n",getpid(),pid,status);
}



Exercice 2.

(Dispositif de l'homme mort.) Ecrire un programme qui prend un entier n en argument sur la ligne de commande et compte à rebours à partir de n : le programme doit afficher les valeurs n, n-1, n-2, ... à raison d'une valeur toutes les secondes et il termine si le compteur atteint 0. Si l'utilisateur tape Ctrl+C (signal SIGINT) avant que le compteur arrive à 0, alors le compte rebours reprend à partir de n.

Exemple d'exécution :

$ ./compte-a-rebours 5
 5 4 3 2
  (l'utilisateur tape Ctrl+C)
 5 4 3 2 1 0
  Alerte : vous dormez !
$


Exercice 3.

(Jeu de dés.) Ecrire un programme qui boucle en attendant que l'utilisateur tape Ctrl+C. Lorsque l'utilisateur tape Ctrl+C, le programme choisit aléatoirement 2 valeurs entre 1 et 6 et les affiche. Le programme termine lorsque l'utilisateur tape 2 fois Ctrl+C en moins d'1 seconde : Utiliser la primitive alarm() pour déclencher le chronomètre.          
               
Exemple d'exécution :

$ ./jeu-de-des
 (appui sur Ctrl+C)
 2 3   
 (appui sur Ctrl+C)
 5 1
 appui sur Ctrl+C)
 6 6
 (appui 2 fois rapidement sur Ctrl+C)            
$



Exercice 4.

La suite de Syracuse est définie par :

                              u(n+1) = u(n)/2,          si u(n) est pair
                                              3u(n)+1,      sinon

Une conjecture est que quelque soit le terme initial u(0) (non nul) de la suite, celle-ci finit par valoir 1 (puis par boucler sur 4, 2, 1). Etant donnée une valeur initiale u(0) de la suite, on appelle temps de vol, le plus petit indice k pour lequel u(k) = 1 pour la première fois, et altitude maximale, la valeur maximale prise par la suite durant ce temps de vol. Nous utilisons un programme calculant la suite de Syracuse pour chaque valeur initiale comme exemple d’utilisation possible des signaux. Pour simplifier, les variables liées à la suite de Syracuse seront globales.
   
   1. Ecrire une fonction void syracuse(void) qui étant donnée une valeur initiale calcule les termes de la suite de Syracuse jusqu’à ce qu’elle vaille 1. Comme la valeur d'un terme de la suite peut être très grande, utiliser le type long long plutôt que le type int.

   2. Faire une boucle infinie pour i = 1, 2, 3, ... qui fixe u(0) = i et lance le calcul précédent. Le programme boucle indéfiniment sans rien afficher.

   3. Faire en sorte qu’à la réception du signal SIGTSTP (Ctrl+Z), le programme affiche la valeur initiale courante, l’indice du dernier terme calculé et la valeur prise par la suite, sans stopper le programme.

   4. Faire en sorte que Ctrl+C (SIGINT) termine le programme en affichant : la plus grande valeur initiale testée ; l’altitude maximale atteinte et la valeur initiale pour laquelle cette altitude maximale est atteinte ; le temps de vol maximal et la valeur initiale pour laquelle ce temps de vol a été atteint.

   5. Faire en sorte que Ctrl+C ne termine pas le programme mais que retaper Ctrl+C dans les deux secondes termine le programme (utiliser une alarme).


Exercice 5.

Ecrire un programme qui crée un tube et N processus fils où N est donné en argument sur la ligne de commande. Le père s'endort pendant 1 seconde dans une boucle infinie (en l'attente de l'arrivée d'un signal). Lorsqu'il reçoit le signal SIGUSR1, il écrit un nombre tiré au hasard dans le tube et renvoie le signal SIGUSR2 au fils qui a émis le signal SIGUSR1 (Pour savoir quel est le fils qui a émis le signal, utiliser le drapeau SA_SIGINFO de la primitive sigaction()). Chaque fils émet le signal SIGUSR1 au père toutes les 2 secondes et lorsqu'il reçoit le signal SIGUSR2, il lit le nombre dans le tube et exécute un calcul long sur ce nombre (on pourra simplement endormir le processus pendant 5 secondes).

Exercice 6.

Analysez le comportement du programme C suivant :


#include <stdio.h>
#include <signal.h>

sigset_t ens1, ens2, ens3;
int sig ;

main ()
{
  sigemptyset(&ens1);
  sigaddset(&ens1, SIGINT);
  sigaddset(&ens1, SIGUSR1);
  sigprocmask(SIG_SETMASK, &ens1, NULL);
  sleep(15);
  sigpending(&ens2);
  printf("Signaux pendants :");
  for (sig=1; sig<NSIG; sig++)
     if (sigismember(&ens2, sig))
  printf("%d ", sig);
  putchar('\n');
  sleep(15);
  sigemptyset(&ens1);
  printf("Deblocage des signaux \n");
  sigprocmask(SIG_SETMASK, &ens1, NULL);
  printf("Fin du processus \n");
}

Exercice 7.


Idem avec le programme C suivant :


#include <stdio.h>
#include <signal.h>

sigset_t ens;
struct sigaction action ;

void hand(int sig) {

  char c ; int i ;
  printf(“signal recu : %d\n”, sig);
  sigprocmask(SIG_BLOCK, NULL, &ens);
  printf(“Signaux bloques :”);
  for (i=1; i<NSIG; i++)
    if (sigismember(&ens, i))
  printf("%d ", i);
  putchar('\n');

  if (sig = = SIGINT)
    {
      action.sa.handler = SIG_DFL;
      sigaction(SIGINT, &action, NULL);
    }

  printf(« Sortie du handler \n ») ;
}

main()
{
  action.sa_handler = hand ;
  sigemptyset(&action.sa_mask) ;
  sigaction(SIGQUIT, &action, NULL);
  sigaddset(&action.sa_mask, SIGQUIT) ;
  sigaction(SIGINT, &action, NULL);

  while(1) sleep(1) ;
}