--- 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 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 #include #include #include 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 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 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