ASR31-valarche-2021/ASR31-5-Signaux.md

152 lines
5.2 KiB
Markdown
Raw Permalink 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.

## Cours 4 : les signaux
Les interruptions logicielles ou signaux sont utilisées par le système dexploitation pour aviser les processus utilisateurs de l' par exemple) ou l'emploi dune adresse non valide, sont converties en signaux qui sont envoyés au processus fautif. Ce mécanisme permet à un processus de réagir à cet événement sans être obligé den tester en permanence larrivée.
Les processus peuvent indiquer au système ce qui doit se passer à la réception dun signal. On peut ainsi ignorer le signal, ou bien le prendre en compte, ou encore laisser le système dexploitation appliquer le com- portement par défaut, qui en général consiste à tuer le processus. Certains signaux ne peuvent être ni ignorés, ni capturés. Si un processus choisit de prendre en compte les signaux quil reçoit, il doit alors spécifier la procé- dure de gestion de signal. Quand un signal arrive, la procédure associée est exécutée. A la fin de lexécution de la procédure, le processus sexécute à partir de linstruction qui suit celle durant laquelle linterruption a eu lieu.
Ainsi, un processus peut :
Ignorer le signal.
Appeler une routine de traitement fournie par le noyau. Cette procédure provoque normalement la mort du processus. Dans certains cas, elle provoque la création dun fichier core, qui contient le contexte du processus avant de recevoir le signal. Ces fichiers peuvent être examinés à laide dun débogueur.
Appeler une procédure spécifique créée par le programmeur. Tous les signaux ne permettent pas ce type daction.
### Envoie d'un signal
```c
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signal);
```
Renvoie $-1$ en cas d'erreur, $0$ sinon. Pour ```pid``` on peut avoir :
- si ```pid```$> 0$, le processus ```pid```
- si ```pid```$= 0$, le groupe de l'émetteur
- si ```pid```$= -1$, tous les processus (seul ```root``` peut faire ça)
- si ```pid```$< -1$, au groupe ```gid``` = valeur absolue (```pid```)
#### Exemple
```c
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sighandler (int signum);
int main(int argc, char *argv[]) {
char buffer[256];
if (signal(SIGTERM, &sighandler) == SIG_ERR) {
printf("Ne peut pas manipuler le signal\n");
exit(1);
}
while (1) {
fgets(buffer, sizeof(buffer), stdin);
printf("Input : %s", buffer);
}
return EXIT_SUCCESS;
}
void sighandler (int signum) {
printf("Masquage du signal SIGTERM\n");
}
```
Avec pour la fonction ```signal```:
```c
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
```
or in the equivalent but easier to read typedef'd version:
```c
typedef void (*sig_t) (int);
sig_t signal(int sig, sig_t func);
```
qui permet de modifier le comportement du processus à la réception d'un signal (sauf ```SIGKILL``` et ```SIGSTOP```).
**Remarque** par défaut la commande ```kill``` du shell envoie le signal 15 (```SIGTERM```) au processus en paramètre.
### SIGUSR1 et SIGUSR2
```c
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define FOREVER for(;;)
static void action (int sig);
int main(int argc, char *argv[]) {
int i, pid, etat;
sig_t s1, s2;
s1 = signal(SIGUSR1, action);
s2 = signal(SIGUSR2, action);
if (( s1 == SIG_ERR) || (s2 == SIG_ERR)) {
perror("Erreur attachement signal");
exit(1);
}
if ((pid = fork()) == 0) {
pause();
kill(getppid(), SIGUSR1);
sleep(10);
}
else {
sleep(2);
kill(pid, SIGUSR2);
pause();
printf("Parent : Demande terminaison du fils\n");
fflush(stdout);
kill(pid, SIGTERM);
wait(&etat);
printf("Parent : fils terminé\n");
fflush(stdout);
}
return EXIT_SUCCESS;
}
static void action(int sig) {
switch (sig) {
case SIGUSR1 :
printf("Parent : signal SIGUSR1 recu\n");
fflush(stdout);
break;
case SIGUSR2 :
printf("Fils : signal SIGUSR2 recu\n");
fflush(stdout);
break;
default :
break;
}
}
```
On peut utiliser les fonctions ```pause``` et ```sleep``` pour *faire attendre les processus* (attente d'un signal de façon non active). Ces fonctions sont interrompus à la réception d'un signal.
#### Exercices
1- Ecrire un programme qui cré un fils. Celui-ci modifie son comportement sur ```SIGQUIT``` et ```SIGINT``` en les ignorants (on affiche qu'on ne les traite pas). Le fils s'endort au moins 3 fois (pour traiter le réveil des 3 signaux ```SIGQUIT```, ```SIGINT``` et ```SIGKILL```).
2- Ecrire un programme qui crée un processus qui se met à travailler :
- si pendant le travail il reçoit un message ```SIGUSR2``` alors il se met à sauvegarder ces données (dans un fichier) puis il signale à son père qu'il a terminé. Son père le tue définitivement.
- si il a le temps de finir son travail, alors il signale à son père qu'il a terminé. Son père lui demande d'engager une sauvegarde des données (dans un fichier). Puis le tue une fois la sauvegarde terminée.
3- Etude de cas : est ce que le handler de signaux d'un processus est hérité par son fils?