Cours n°6 :
Signaux

Réseau et Prog. Bas Niveau
Victor Poupet

Signaux

Interruptions logicielles envoyées à un processus pour indiquer un événement exceptionnel

Exemples :

Présentation

sys/signal.h
...
#define	SIGHUP	1
#define	SIGINT	2
#define	SIGQUIT	3
#define	SIGILL	4
#define	SIGTRAP	5
#define	SIGABRT	6
#define	SIGPOLL	7
#define	SIGIOT	SIGABRT
#define	SIGEMT	7
#define	SIGFPE	8
#define	SIGKILL	9
#define	SIGBUS	10
...
$ man signal

No    Name         Default Action
1     SIGHUP       terminate process
2     SIGINT       terminate process
3     SIGQUIT      create core image
4     SIGILL       create core image
5     SIGTRAP      create core image
6     SIGABRT      create core image
7     SIGEMT       create core image
8     SIGFPE       create core image
9     SIGKILL      terminate process
10    SIGBUS       create core image
11    SIGSEGV      create core image
12    SIGSYS       create core image
13    SIGPIPE      terminate process
14    SIGALRM      terminate process
15    SIGTERM      terminate process
16    SIGURG       discard signal
17    SIGSTOP      stop process

Nombre limité de signaux

Chaque signal a

Exemples

Quelques signaux courants :

Dans le terminal

La commande kill permet d'envoyer un signal à un processus

  • envoie le signal SIGTERM par défaut
  • signal donné par nom ou par numéro
  • prend le PID du processus cible

On peut aussi utiliser killall

Raccourcis clavier envoient un signal au processus au premier plan :

  • Ctrl-C envoie SIGTERM
  • Ctrl-Z envoie SIGTSTP
  • Ctrl-\ envoie SIGQUIT
$ kill 1234
[1]+  Terminated: 15          ./a.out

$ kill -SIGSEGV 1234
Segmentation fault: 11

$ kill -9 1234
[1]+  Killed: 9               ./a.out

Liste

La commande kill -l liste les signaux disponibles

$ kill -l
1) SIGHUP         2) SIGINT        3) SIGQUIT       4) SIGILL
5) SIGTRAP        6) SIGABRT       7) SIGEMT        8) SIGFPE
9) SIGKILL       10) SIGBUS       11) SIGSEGV      12) SIGSYS
13) SIGPIPE      14) SIGALRM      15) SIGTERM      16) SIGURG
17) SIGSTOP      18) SIGTSTP      19) SIGCONT      20) SIGCHLD
21) SIGTTIN      22) SIGTTOU      23) SIGIO        24) SIGXCPU
25) SIGXFSZ      26) SIGVTALRM    27) SIGPROF      28) SIGWINCH
29) SIGINFO      30) SIGUSR1      31) SIGUSR2

Gestion des signaux

pending and blocked signalspending and blocked signalspending and blocked signals

L'état des signaux pour un processus est représenté par 2 entiers

  • Signaux en attente
  • Signaux ignorés

Chaque bit représente un signal

Lorsque le processus est activé

  • les procédures correspondant aux signaux en attente non bloqués sont exécutées
  • les signaux traités sont retirés
  • les signaux bloqués sont conservés
  • le processus reprend son exécution

Handlers

struct sigaction {
  void (* sa_handler) (int);
  sigset_t sa_mask;
  int sa_flags;
}

int sigaction(
  int sig,
  struct sigaction *act,
  struct sigaction *old_act
);
void handle_sigint(int sig) {
  printf("Reçu signal %d\n", sig);
}

int main() {
  struct sigaction act;
  act.sa_handler = handle_sigint;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  sigaction(SIGINT, &act, NULL);
  while (1){}
}

$ ./a.out
^CReçu signal 2
^CReçu signal 2
void handler(int sig) {
  printf("Je suis occupé\n");
}

int main() {
  struct sigaction act, old_act;
  act.sa_handler = handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  sigaction(SIGINT, &act, &old_act);
  printf("Je travaille\n");
  sleep(5);
  sigaction(SIGINT, &old_act, NULL);
  printf("J'ai fini de travailler\n");
  while(1){}
}void handler(int sig) {
  printf("Je suis occupé\n");
}

int main() {
  struct sigaction act;
  act.sa_handler = handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;

  sigaction(SIGINT, &act, NULL);
  printf("Je travaille\n");
  sleep(5);
  act.sa_handler = SIG_DFL;
  sigaction(SIGINT, &act, NULL);
  printf("J'ai fini de travailler\n");
  while(1){}
}

Je travaille
^CJe suis occupé
J'ai fini de travailler
^C

Pour redéfinir le comportement d'un signal

Signal

typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);

void handle_sigint(int sig) {
  printf("Reçu signal %d\n", sig);
}

int main() {
  signal(SIGINT, handle_sigint);
  while (1){}
}

$ ./a.out
^CReçu signal 2
^CReçu signal 2

On peut aussi changer le handler avec signal()

signal est plus simple mais moins complète que sigaction

Parfois déconseillée pour raisons de compatibilité

Signaux prioritaires

void handler(int sig) {
  printf("Reçu signal %d\n", sig);
}

int main() {
  struct sigaction act, old_act;
  act.sa_handler = handler;
  act.sa_mask = 0;
  act.sa_flags = 0;
  sigaction(SIGINT, &act, NULL);
  sigaction(SIGKILL, &act, NULL);
  sigaction(SIGTERM, &act, NULL);
  sigaction(SIGSTOP, &act, NULL);
  sigaction(SIGTSTP, &act, NULL);
  while(1){}
}

SIGKILL et SIGSTOP ne peuvent pas être ignorés ou redéfinis


$ ./a.out
Reçu signal 15             kill
^CReçu signal 2            Ctrl-C
^ZReçu signal 18           Ctrl-Z
                           kill -SIGSTOP
[1]+  Stopped     ./a.out
$ fg
./a.out
Killed: 9                  kill -SIGKILL

Envoi de signaux

pid_t pid = getpid();
kill(pid, SIGINT); // ou raise(SIGINT);
int main() {
  int stat;
  pid_t pid = fork();
  if (pid == 0) {
    while(1){}
  } else {
    kill(pid, SIGINT);
    wait(NULL);
  }
}
pid_t pid;
void handler1(int sig) {
  printf("ping\n"); kill(pid, SIGUSR1);
}
void handler2(int sig) {
  printf("pong\n"); exit(0);
}

int main() {
  struct sigaction act;
  act.sa_handler = handler1;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  sigaction(SIGUSR1, &act, NULL);
  if ((pid = fork()) == 0) {
    act.sa_handler = handler2;
    sigaction(SIGUSR1, &act, NULL);
    kill(getppid(), SIGUSR1);
    while(1){}
  }
  waitpid(pid, NULL, 0);
}

On peut envoyer des signaux avec

int kill(pid_t pid, int sig)

Le retour des zombies

static void handler(int sig) {
  wait(NULL);
}

int main() {
  signal(SIGCHLD, handler);
  int pid = fork();
  if (pid) {
    // père
    /* Tâche principale */
  } else {
    // fils
    /* Tâche secondaire */
  }
}static void handler(int sig) {
  wait(NULL);
}

int main() {
  struct sigaction act;
  act.sa_handler = handler;
  sigemptyset(&act.sa_mask);
  act.sa_flags = 0;
  sigaction(SIGCHLD, &act, NULL);
  int pid = fork();
  if (pid) {
    // père
    /* Tâche principale */
  } else {
    // fils
    /* Tâche secondaire */
  }
}int main() {
  signal(SIGCHLD, SIG_IGN);
  int pid = fork();
  if (pid) {
    // père
    /* Tâche principale */
  } else {
    // fils
    /* Tâche secondaire */
  }
}

Lorsqu'un fils termine, il envoie le signal SIGCHLD au parent