« Programmation C/Gestion des signaux » : différence entre les versions

Contenu supprimé Contenu ajouté
Aucun résumé des modifications
m Formatage, ajout de code
Ligne 1 :
{{ébauche}}
<noinclude>{{Programmation C}}</noinclude>
Les signaux permettent une communication, assez sommaire, entre le système et un processus, ou entre différents processus. Cette communication est sommaire, car un ''signal'' ne porte qu'une seule information: son numéro, de type <ttcode>int</ttcode>. Un processus peut aussi s'envoyer un signal à lui-même.
 
Ces signaux sont envoyés de manière ''asynchrone'': lorsqu'un processus reçoit un signal, son exécution est interrompue, et une fonction spécifique, dite ''gestionnaire de signal'', est appelée, avec en paramètre le numéro du signal reçu, pour traiter l'événement. Lorsque cette fonction se termine, le processus reprend là où il s'était arrêté.
Ligne 7 :
C'est à chaque processus de déterminer ce qu'il fait quand il reçoit un signal de numéro donné, en définissant un gestionnaire de signal pour tous les signaux qu'il le souhaite. Vous l'aurez remarqué, dans tous les exemples de ce livre rencontrés jusqu'à présent, nous ne nous sommes pas occupés de savoir quels signaux nous pourrions recevoir, et comment il fallait les traiter. En l'absence de précisions dans nos programmes, l'implémentation fournira en effet un gestionnaire de signal par défaut, qui le plus souvent se contentera d'arrêter le programme.
 
Les fonctions suivantes, fournies par l'en-tête <ttcode><signal.h></ttcode>, sont utilisées dans la gestion des signaux :
* <ttcode>signal()</ttcode> pour définir un gestionnaire de signal;
* et <ttcode>raise()</ttcode> pour envoyer un signal au processus courant.
 
Le C définit ces deux fonctions, et pas plus, alors que les signaux peuvent faire beaucoup plus... En particulier, aucune fonction pour envoyer un signal à un autre processus n'est définie. Cela est dû au fait que, sur certains systèmes, la notion de processus n'existe pas, et une seule tâche s'exécute. Dans de telles situations, il serait aberrant de demander à un compilateur C pour un tel système de fournir des moyens de communications inter-processus ! Par contre, les communications inter-processus étant chose courante dans de nombreux autres systèmes, des extensions fournissent des moyens de le faire (voir par exemple la fonction <ttcode>kill()</ttcode> définie dans POSIX, cf. le chapitre [[Programmation POSIX:Signaux|Gestion des signaux]] du livre [[Programmation POSIX]]).
 
== Définir un gestionnaire de signal ==
Ligne 21 :
</source>
 
permet de définir un gestionnaire de signal. La signature de cette fonction étant un peu complexe, on peut la simplifier en utilisant un <ttcode>typedef</ttcode>:
 
<source lang="c">
Ligne 31 :
'''Le comportement de signal() varie selon les versions d’Unix, et a aussi varié au cours du temps dans les différentes versions de Linux. Évitez de l’utiliser : utilisez plutôt sigaction(2).'''
 
Ainsi, on voit mieux comment fonctionne cette fonction (notez que le type <ttcode>t_handler</ttcode> n'est pas défini par la norme, et n'est ici que pour clarifier la syntaxe). Elle prend deux paramètres:
* le numéro du type de signal à traiter;
* et un pointeur vers la fonction de gestion du signal.
 
Le premier paramètre est le numéro du signal. La norme définit un faible nombre de signaux, par des macros dans l'en-tête <ttcode><signal.h></ttcode>, par exemple <ttcode>SIGSEGV</ttcode> qui indique un accès illégal à la mémoire (''SEGmentation Violation''), et laisse à l'implémentation la liberté de définir d'autres types de signaux.
 
Pour le deuxième paramètre, on peut donner trois valeurs possibles:
*<ttcode>SIG_IGN</ttcode> (macro définie dans <ttcode><signal.h></ttcode>): tout signal ayant le numéro <ttcode>sig</ttcode> sera ignoré (i.e. rien ne se passera, le programme continue de s'exécuter);
*<ttcode>SIG_DFL</ttcode> (id.): le gestionnaire de signal par défaut sera mis en place pour ce type de signal.
*un pointeur vers une fonction de type <ttcode>t_handler</ttcode>: cette fonction sera appelée lorsqu'un signal de type <ttcode>sig</ttcode> sera reçu par le processus.
 
Cette fonction renvoie la valeur du dernier gestionnaire de signal pour le numéro donné (qui peut être <ttcode>SIG_IGN</ttcode> ou <ttcode>SIG_DFL</ttcode>).
 
En cas d'erreur, la fonction renvoie <ttcode>SIG_ERR</ttcode> et place la variable <ttcode>errno</ttcode> à une valeur significative.
 
== Les gestionnaires de signaux ==
Ligne 54 :
</source>
 
Elles prennent un argument de type <ttcode>int</ttcode>, qui est le numéro du signal, et ne renvoient rien. En effet, ces fonctions étant appelées de manière asynchrone, leur éventuelle valeur de retour ne serait récupérée, et encore utilisée, par personne...
 
Comme on utilise <ttcode>signal</ttcode> pour associer à chaque numéro de signal une fonction qui va gérer les signaux de ce numéro, on pourrait penser qu'il n'est pas nécessaire de donner ce numéro en paramètre à la fonction. Cependant cela s'avère pratique, car on peut ainsi définir une seule fonction qui peut gérer plusieurs types de signaux, et dont le comportement variera en fonction du signal à traiter.
 
Voici un exemple simple de définition et de mise en place d'un gestionnaire de signaux, pour le type de signal <ttcode>SIG_FPE</ttcode>, qui concerne des exceptions lors des calculs en virgule flottante (''Floating Point Exception''):
 
'''Le comportement de signal() varie selon les versions d’Unix, et a aussi varié au cours du temps dans les différentes versions de Linux. Évitez de l’utiliser : utilisez plutôt sigaction(2).'''