Les signaux constituent la forme la plus simple de communication entre processus. Un signal est la notification asynchrone d'un événement. Un signal est dit envoyé au processus quand l'événement associé au signal se produit.

Il y a plusieurs types de signaux, indiquant chacun une condition particulière. Les signaux reçus par un programme proviennent de plusieurs sources possibles:

Le but de ce TP est d'étudier les signaux et les processus (en langage C).

Exercice 1 - Masquer des signaux

  1. Écrire un processus qui ignore les cinq premiers signaux SIGINT et réagit au sixième : il intercepte les cinq premiers sans faire de traitement particulier puis sur le sixième, il rétablit le handler SIG_DFL.

  2. #include ‹stdio.h›
    #include ‹signal.h›
    
    int cpt = 0;
    
    void hand()
    {
      cpt++;
      printf("Appui sur Ctrl-C numero %d \n",cpt);
      if (cpt==5)
        {
          printf("Arret au prochain Ctrl-C\n");
          signal(SIGINT,SIG_DFL);
        }
      
    }
    
    int main()
    {
      signal(SIGINT,hand);
      while(1);
    }
    

    ou plus proprement avec sigaction :

    #include ‹stdio.h›
    #include ‹signal.h›
    
    struct sigaction action;
    // contient void      *sa_handler  - gestionnaire de signal
    //          sigset_t  sa_mask      - signaux à bloquer 
    //          int       sa_flags     - options
    
    int cpt = 0;
    
    void hand()
    {
      cpt++;
      printf("Appui sur Ctrl-C numero %d \n",cpt);
      if (cpt==5)
        {
          printf("Arret au prochain Ctrl-C\n");
          action.sa_handler=SIG_DFL;
          sigaction(SIGINT,&action,NULL);
        }
      
    }
    
    int main()
    {
      action.sa_handler=hand;
      sigaction(SIGINT,&action,NULL);
      while(1);
    }
    
  3. Écrire un programme C qui compte les signaux qu'il reçoit et affiche ces compteurs.

#include ‹stdio.h›
#include ‹signal.h›
#include ‹unistd.h›

int nsig[NSIG];

void hand(int s)
{  
  printf("Signal %d recu %d fois\n", s, ++nsig[s]);
}


int main(void)
{  
  int s;
  struct sigaction act;

  // contient void      *sa_handler  -> gestionnaire de signal
  //          sigset_t  sa_mask      -> signaux à bloquer 
  //          int       sa_flags     -> options

  act.sa_handler = hand;
  sigfillset(&act.sa_mask);
  act.sa_flags = 0;
  
  for (s = 1; s ‹ NSIG; s++)
    {  
      if (sigaction(s, &act, NULL) == -1)
	fprintf(stdout, "Capture du signal no %d\n impossible", s);
      nsig[s] = 0;
    }
  
  while (1);
}

top

Exercice 2 - Signal SIGALARM

  1. Écrire un programme qui propose à l'utilisateur un calcul sur 3 entiers tirés pseudo-aléatoirement en contrôlant son temps de réponse :

    • si l'utilisateur ne répond pas au bout d'un temps t fixé, le programme se termine avec un message d'avertissement.

    • sinon le programme propose un autre calcul

    #include ‹stdio.h›
    #include ‹stdlib.h›
    #include ‹signal.h›
    #include ‹sys/times.h› 
    #include ‹unistd.h›
    
    
    struct sigaction action;
    struct tms buf;
    int timeout;
    
    void hand()
    {
      printf("\nTrop tard !\n");
      exit(1);
    }
    
    int main()
    {
      int resultat, a, b, c;
      char reponse;
      int fini=0;
    
      action.sa_handler=hand;
      action.sa_flags=0;
      sigaction(SIGALRM,&action,NULL);
      
      printf("\nChoisissez votre temps de reponse (en sec.) : ");
      scanf("%d", & timeout);
    
      while(fini==0)
        {
          times(&buf);
          srand(time());
    
          a = random() % 20;
          b = random() % 20;
          c = random() % 20;
    
          printf("Calculer %d x %d - %d   Reponse = ",a,b,c);
    
          alarm(timeout);
          scanf("%d",& resultat);
          alarm(0);
    
          if (resultat == (a*b-c))
    	printf("Le compte est bon\n");
          else
    	printf("La reponse est fausse\n");
          
          printf("Voulez-vous continuer ? (o/n) ");
          scanf("%s",&reponse);
          if ((reponse == 'n') || (reponse == 'N')) fini=1;
        }
    
      return(0);
    }
    
  2. Écrire un programme qui affiche l’heure chaque minute puis le message Dring à une heure donnée par l’utilisateur.

    #include ‹stdio.h›
    #include ‹stdlib.h›
    #include ‹signal.h›
    #include ‹time.h›
    #include ‹sys/types.h› 
    #include ‹unistd.h›
    
    int rev_heure,rev_minutes;
    
    void interruption(int signum)
    {
    
      time_t temps;
      struct tm * t;
    
      time(&temps);
      t=localtime(&temps);
    
      printf("\n%2dh%2d\n",t-›tm_hour,t-›tm_min);  
      if(t-›tm_hour == rev_heure  && t-›tm_min == rev_minutes)
        {
          printf("Dring!\n");
          exit(EXIT_SUCCESS);
        }
      alarm(60);
    }
    
    void usage(char *nomcommande)
    {
      printf("Usage : %s nnhmm\n"
    	 "(nn=heures, mm=minutes)\n",nomcommande);
      exit(EXIT_FAILURE);
    }
    
    int main(int argc, char *argv[])
    {
      
      struct sigaction action;
    
      if(argc != 2 || 
         sscanf(argv[1],"%2dh%2d",&rev_heure,&rev_minutes) != 2)
        usage(argv[0]);
      
      action.sa_handler=interruption;
      action.sa_flags=0;
      sigemptyset(&action.sa_mask);
      sigaction(SIGALRM,&action,NULL);
      
      alarm(60);
      for(;;) pause();
    
      return EXIT_SUCCESS;
    }
    
top

Exercice 3 - Mini-ordonnanceur

Écrire deux programmes qui bouclent et qui affichent les messages « ping » et « pong » respectivement lorsqu'ils recoivent un signal à définir. La synchronisation entre les processus correspondants aux deux programmes (pour afficher de manière alternative les messages « ping » et « pong ») est assurée par un troisième processus : l'ordonnanceur.

On lancera trois terminaux, puis on exécutera dans deux d'entre eux les programmes « ping » et « pong » et dans le troisième terminal l'ordonnanceur, avec comme argument les pid des deux processus maintenant en attente.

#include ‹stdio.h›
#include ‹signal.h›
#include ‹unistd.h›

void interrupt(int s)
{
  printf("Ping\n");
  signal(s,interrupt);
}

int main()
{
  // Pour des raisons pratiques...
  printf("Mon PID est : %d\n",getpid());

  signal(SIGUSR1,interrupt);

  
  // Boucle infinie :
  while(1) 
    {
      pause();
    }
  
  return 0;
}


#include ‹stdio.h›
#include ‹signal.h›
#include ‹stdlib.h›
#include ‹unistd.h›

// Numero de processus actuel, et pid correspondants :
int procactuel,pid1,pid2;

// Donne la main aux processsus l'un apres l'autre :
void swap(int s)
{
  if(procactuel==0)
    {
      printf("Main au processus 2\n");
      kill(pid2,SIGUSR1);
      procactuel=1;
    }
  else
    {
      printf("Main au processus 1\n");
      kill(pid1,SIGUSR1);
      procactuel=0;
    }
  
  // Reprise :
  alarm(1);
  signal(s,swap);
}


void detruit(int s)
{
  // L'ordonnanceur entraine dans sa chute les deux
  // processus fils :
  kill(pid1,SIGINT);
  kill(pid2,SIGINT);
  int mypid=getpid();
  alarm(0);
  kill(mypid,SIGKILL);
}


void usage(char *nomcommande)
{
  printf("Usage : %s ‹pid1› ‹pid2›\n",nomcommande);
  printf("Execute de maniere alternative ‹pid1› et ‹pid2›.\n");
  exit(EXIT_FAILURE);
}


int main(int argc, char * argv[])
{
  if (argc!=3)
    usage(argv[0]);
  
  // Recuperation des parametres :
  pid1=atoi(argv[1]);
  pid2=atoi(argv[2]);
  
  // Verification des deux pid :
  if(pid1#139;=0 || pid2#139;=0)
    usage(argv[0]);
  
  // Declaration des fonctions de traitement d'interruption :
  signal(SIGALRM,swap);
  signal(SIGINT,detruit);
  
  // Initialisation :
  printf("Pressez CTRL-C pour quitter !\n");
  printf("Mini-ordonnanceur pret.\n");
  procactuel=0;
  kill(pid1,SIGUSR1);
  alarm(1);
  
  // Boucle infinie :
  while(1) {pause();}
  return 0;
}

top

Exercice 4 - Sac à dos

Faire un programme C qui fabrique une instance aléatoire du problème du "sac à dos" modulo 232 et qui cherche à la résoudre par recherche exhaustive. Le programme engrendre donc dans un premier temps 33 nombres s, p1, ..., p32 de 32 bits de manière pseudo-aléatoire.
Une solution du problème est un sous-ensemble I de {1,...,32} tel que la somme modulo 232 des pi pour i dans I est égale à s.

Une telle recherche est assez longue. On cherche à pouvoir la lancer en arrière plan, tout en pouvant continuer à dialoguer avec le processus. On cherche essentiellement à lui transmettre plusieurs messages différents:

  1. afficher ou la recherche en est (message STATUS),
  2. abandonner la recherche et recommencer avec une autre instance (message RESTART),
  3. abandonner définitivement (message STOP),
  4. demander l'affichage des sous-ensembles I qui seraient des solutions modulo 2n (message DISP n).

Définir un protocole de signaux pour coder ces messages, et faire une application Perl qui envoie ce type de message à un processus donné.


La correction est disponible ici.

top

Pour mémoire, voici un extrait du fichier signal.h :

#define SIGHUP 1 /* hangup */
#define SIGINT 2 /* interrupt (rubout) */
#define SIGQUIT 3 /* quit (ASCII FS) */
#define SIGILL 4 /* illegal instruction (not reset when caught)*/
#define SIGTRAP 5 /* trace trap (not reset when caught) */
#define SIGIOT 6 /* IOT instruction */
#define SIGABRT 6 /*used by abort, replace SIGIOT in the future */
#define SIGEMT 7 /* EMT instruction */
#define SIGFPE 8 /* floating point exception */
#define SIGKILL 9 /* kill (cannot be caught or ignored) */
#define SIGBUS 10 /* bus error */
#define SIGSEGV 11 /* segmentation violation */
#define SIGSYS 12 /* bad argument to system call */
#define SIGPIPE 13 /* write on a pipe with no one to read it */
#define SIGALRM 14 /* alarm clock */
#define SIGTERM 15 /* software termination signal from kill */
#define SIGUSR1 16 /* user defined signal 1 */
#define SIGUSR2 17 /* user defined signal 2 */
#define SIGCLD 18 /* child status change */
#define SIGCHLD 18 /* child status change alias (POSIX) */
#define SIGPWR 19 /* power-fail restart */
#define SIGWINCH 20 /* window size change */
#define SIGURG 21 /* urgent socket condition */
#define SIGPOLL 22 /* pollable event occured */
#define SIGIO SIGPOLL /* socket I/O possible (SIGPOLL alias) */
#define SIGSTOP 23 /* stop (cannot be caught or ignored) */
#define SIGTSTP 24 /* user stop requested from tty */
#define SIGCONT 25 /* stopped process has been continued */
#define SIGTTIN 26 /* background tty read attempted */
#define SIGTTOU 27 /* background tty write attempted */
#define SIGVTALRM 28/* virtual timer expired */
#define SIGPROF 29 /* profiling timer expired */
#define SIGXCPU 30 /* exceeded cpu limit */
#define SIGXFSZ 31 /* exceeded file size limit */
#define SIGWAITING 32 /* process's lwps are blocked */
#define SIGLWP 33 /* special signal used by thread library */
#define SIGFREEZE 34/* special signal used by CPR */
#define SIGTHAW 35 /* special signal used by CPR */
#define SIGCANCEL 36/*thread cancel signal used by libthread */
#define _SIGRTMIN 37/*first (highest-priority) realtime signal*/
#define _SIGRTMAX 44/* last (lowest-priority) realtime signal */