---
title: "Les signaux"
categories: ["Programmation système", "Cours"]
tags: ["C", "programmation", "signaux"]
date: 2018-09-25
---

En programmation système, un signal est une notification à un processus qu'un
évènement a eu lieu. Également appelés **Interruptions Logicielles**, les
signaux son asynchrone est il est impossible de prédire à quel moment il
arriveront.

Les signaux couvrent plusieurs types d'évènements : 

 * **Les actions utilisateurs** `CTRL + C` pour *SIGINT* ou `CRTL + Z` pour
     *SIGSTOP*
 * **Les fautes matérielles** comme la division par zéro *SIGFPE*, la référence
     mémoire invalide *SIGSEGV*, écriture mémoire erronée *SIGSEGV* etc.
 * **les fautes logicielles** comme l'erreur d'écriture dans un tube *SIGPIPE*,
     la notification urgente de données disponibles *SIGURG*, l'alarme *SIGALRM*

Ils représentent une forme basique de communication inter-processus ( *IPC* )
mais permettent aussi à l'utilisateur d'intervenir dans le déroulement d'un
processus. (voir `man 1 kill` et `man 2 kill`).

C'est au processus de se déclarer à l'écoute d'un type d'évènement en
initialisant un gestionnaire appelé lorsque l'évènement visé aura lieu. Lorsque
ce dernier a lieu, le gestionnaire est appelé avec toutes les informations
nécessaires. A la fin de l'exécution du gestionnaire, l'exécution du processus
reprend normalement à l'endroit ou elle s'est arrêtée.

## Comportement associés aux signaux.

Par défaut, des comportements sont associés aux signaux :

 * **stop** : arrête le processus
 * **cont** : continuer le processus s'il est arrêté.
 * **term** : terminer le processus
 * **ign** : ignorer le processus
 * **core** : créer un fichier *core* (contexte d'exécution du processus) puis
     *sigterm*

## Liste des signaux

### Norme POSIX 1-1999

Signal  | Valeur | Action | Commentaire
--------|--------|--------|----------------------------------------------------
SIGHUP  | 1      | Term   | Déconnexion du terminal ou fin du processus de contrôle
SIGINT	| 2	     | Term	  | Interruption depuis le clavier `CTRL + C`
SIGQUIT	| 3      | Core   | Demande ”Quitter” depuis le clavier `CTRL + \\`
SIGILL	| 4      | Core   | Instruction illégale
SIGABRT | 6      | Core   | Signal d’arrêt depuis abort(3)
SIGFPE  | 8      | Core   | Erreur mathématique virgule flottante
SIGKILL | 9      | Term   | Signal ”KILL”
SIGSEGV | 11     | Core   | Référence mémoire invalide
SIGPIPE | 13     | Term   | Écriture dans un tube sans lecteur
SIGALRM | 14     | Term   | Temporisation alarm(2) écoulée
SIGTERM | 15     | Term   | Signal de fin
SIGUSR1 | 10     | Term   | Signal utilisateur 1
SIGUSR2 | 12     | Term   | Signal utilisateur 2
SIGCHLD	| 17     | Ign	  | Fils arrêté ou terminé
SIGCONT | 18     | Cont   | Continuer si arrêté
SIGSTOP | 19     | Stop   | Arrêt du processus
SIGTSTP | 20     | Stop   | Stop invoqué depuis le terminal `CTRL + Z`
SIGTTIN | 21     | Stop   | Lecture sur le terminal en arrière-plan
SIGTTOU | 22     | Stop   | Écriture dans le terminal en arrière-plan

Les signaux **SIGKILL** et **SIGSTOP** ne peuvent ni être capturés ou ignorés.


### Normes SUSv2 et POSIX 1-2001

Signal    | Valeur | Action | Commentaire
----------|--------|--------|--------------------------------------------------
SIGBUS    | 7      | Core   | Erreur de bus (mauvais accès mémoire)
SIGPOLL   |        | Term   | Événement ”pollable”, synonyme de SIGIO
SIGPROF   | 27     | Term   | Expiration de la temporisation pour le suivi
SIGSYS    | 31     | Core   | Mauvais argument de fonction
SIGTRAP   | 5      | Core   | Point d’arrêt rencontré
SIGURG    | 23     | Ign    | Condition urgente sur socket
SIGVTALRM |	26     | Term	| Alarme virtuelle
SIGXCPU   | 24     | Core   | Limite de temps CPU dépassée
SIGXFSZ   | 25     | Core   | Taille de fichier excessive
SIGWINCH  | 28     | Ign    | Fenêtre redimensionnée

## Le core dump

Un **core dump** est une image mémoire d'un processus prise au moment d'un
plantage. Elle contient des information utiles pour l'analyse du crash :

 * copie de la mémoire au moment du plantage
 * statut de terminaison du processus
 * copie des registres *CPU*

Prenons l'exemple de code suivant : 

```C
int main ()
{
    int a = 10;
    int b = 0;
    a = a / b
}
```

Compilons le et exécutons le : 

```shell
$ gcc -g -Wall sigfpe.c -o sigfpe
$ ./sigfpe
[2] 8112 floating point exception (core dumped) ./sigfpe
```

Analysons maintenant le *core dump* avec `gdb`

```shell
$ gdb sigfpe core
GNU gdb (Debian 7.11.1-2) 7.11.1
Core was generated by `./sigfpe'.
Program terminated with signal SIGFPE, Arithmetic exception.
#0 0x00000000004004ec in main () at sigfpe.c:4
4       a = a / b;
(gdb) backtrace full
#0 0x00000000004004ec in main () at sigfpe.c:4
    a = 10
    b = 0
(gdb)
```

## Gestion des signaux.

en programmation système, la gestion des signaux revient à changer le
comportement par défaut par un souhaité lors de la réception d'un signal donné 
par un processus.

```C
#include <signal.h>

typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
```

 * `signum` est le numéro de signal à modifier
 * `handler` vaut soit *SIG_IGN* pour ignorer le signal, soit *SIG_DFL* pour le
     réinitialiser à sa valeur par défaut ou est un pointeur vers une fonction à
     appeler.

L'appel à `signal()` renvoie la valeur du gestionnaire de signal précédent en
cas de réussite, sinon *SIG_ERR*.

### exemple de code 

```C
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>

void sigusr1_trigger() {
    write(1, "SIGUSR1 received\n", 17);
}

int main () {
    if (signal(SIGUSR1, sigusr1_trigger) == SIG_ERR) {
        perror("Unable to catch SIGUSR1\n");
    }
    else {
        printf("SIGUSR1 is catched on process with PID=%d\n", getpid());
    }
    for(;;) {
        sleep(10);
    }
}
```

#### Exécution  

##### 1 sur le premier terminal

```shell
$ ./sigusr1
SIGUSR1 is catched on process with PID=10333
```

##### 2 Sur le second terminal, envoi du signal : 

```shell
$ kill -USR1 10333`
```

##### 3 Retour sur le premier terminal :

```shell
$ ./sigusr1
SIGUSR1 is catched on process with PID=10333
SIGUSR1 received
```

## Envoyer des signaux

Des signaux peuvent être envoyés à d'autres processus avec `kill()` ou au
processus en cours avec `raise()`.

```C
include <signal.h>

int kill(pid_t pid, int sig);
int raise(int sig);
```

Ces deux fonctions renvoient 0 en cas de succès, sinon -1.

Il est aussi possible de bloquer le processus courant et d'attendre un signal
avec la fonction `pause()`

```C
int pause(void);
```

En cas d'erreur, retourne -1 avec errno positionné à *EINTR*

## Poser une alarme

Un processus peut définir une alarme qui aboutira à l'envoi d'un signal
*SIGALRM* au bout d'un temps défini. Il n'est cependant possible de ne définir
qu'une seule alarme par processus.

```C
#include <unistd.h>

unsigned int alarm(unsigned int seconds);
```

Il est aussi possible d'annuler une alarme précédemment définir avec
`alarm(0)`.

### Bibliographie

[Présentation][f_pres] support de cours

[f_pres]:files/presentation.pdf
