cours/content/progsys/4_les-signaux/index.md

242 lines
7.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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 darrê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 darrê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