Chapitre 12. Trapper les signaux

Table des matières

1. Signaux
1.1. Introduction
1.2. Utilisation de signaux avec kill
2. Piéger les signaux
2.1. Généralité
2.2. Comment Bash interprète trap
2.3. Plus d'exemples
3. Résumé
4. Exercices

Résumé

Dans ce chapitre nous traiterons les sujets suivants:

  • Signaux disponibles

  • Intérêt des signaux

  • Emploi de l'instruction trap

  • Comment éviter que les usagers interrompent votre programme

1. Signaux

1.1. Introduction

1.1.1. Trouver la page man de signal

Votre système contient une page man qui liste tous les signaux disponibles, mais selon votre système, elle peut être affichée de diverses façons. Sur la plupart des Linux, ce sera avec man 7 signal. Dans le doute, localisez la page man exacte ainsi que la section avec une commande comme

man -k signal | grep list

ou

apropos signal | grep list

Les noms de signaux peuvent être trouvés avec kill -l.

1.1.2. Les signaux en direction de votre Shell Bash

En l'absence de toute trappe, un Shell Bash interactif ignore SIGTERM et SIGQUIT. SIGINT est récupéré et considéré, et si le contrôle de travaux est actif, SIGTTIN, SIGTTOU et SIGTSTP sont aussi ignorés. Les commandes qui sont lancées en tant que résultat de substitution de commandes ignorent aussi ces signaux quand le clavier les a générés.

SIGHUP par défaut fait quitter le Shell. Un Shell interactif enverra un SIGHUP à tous les travaux, en exécution ou pas; voir la documentation sur l'intégrée disown si vous souhaitez désactiver ce comportement pour un processus particulier. Utilisez l'option huponexit pour tuer tous les travaux à la réception du signal SIGHUP avec l'intégrée shopt.

1.1.3. Envoyer des signaux avec le Shell

Les signaux suivants peuvent être envoyés en utilisant le Shell Bash:

Tableau 12.1. Les signaux de contrôle dans Bash

Combinaison standard de touchessens
Ctrl+CLe signal d'interruption, envoie SIGINT à tous les travaux s'exécutant dans le Shell courant.
Ctrl+YLe caractère delayed suspend. Provoque la suspension d'un processus actif quand il tente de lire son entrée depuis le terminal. Le contrôle est rendu au Shell, l'utilisateur peut renvoyer en tâche de fond, réactiver ou tuer le processus. 'Delayed suspend' n'est pas une fonctionnalité connue de tous les systèmes.
Ctrl+ZLe signal suspend envoie SIGTSTP à un programme en train de s'exécuter, donc il s'arrête et redonne le contrôle au Shell.

Paramètres du terminal

Vérifiez les paramètres stty. La suspension et la reprise d'affichage sont généralement désactivées si vous utilisez des émulations « modernes » de terminaux. Le xterm standard reconnaît Ctrl+S et Ctrl+Q par défaut.

1.2. Utilisation de signaux avec kill

La plupart des Shell récents, Bash inclus, ont une fonction intégrée kill. Dans Bash, à la fois les noms de signaux et leur numéro sont acceptés en tant qu'option, et les arguments peuvent être des identifiants de travaux ou de processus. Un statut d'exécution peut être renvoyé avec l'option -l : zéro si au moins un signal a été envoyé correctement, différent de zéro si une erreur s'est produite.

Avec la commande kill de /usr/bin, votre système peut activer des options supplémentaires, telles que la capacité de tuer des processus provenant d'autres identifiants utilisateurs que le votre, et celle de spécifier les processus par leur nom, comme avec pgrep et pkill.

Les 2 commandes kill envoient le signal TERM si aucun n'est donné.

Voici une liste des principaux signaux:

Tableau 12.2. Signaux courants de kill

Nom du signalValeur du signalEffet
SIGHUP1Suspend
SIGINT2Interruption depuis le clavier
SIGKILL9signal kill
SIGTERM15signal d'arrêt d'exécution
SIGSTOP17,19,23Stop le processus

SIGKILL et SIGSTOP

SIGKILL et SIGSTOP ne peuvent pas être trappés, bloqués ou ignorés.

Pour tuer un processus ou une série de processus, il est de bon sens de commencer par essayer avec le signal le moins dangereux, SIGTERM. De cette façon, les programmes qui se soucient d'un arrêt correct ont une chance de suivre les procédures qui leur ont été demandé d'exécuter à la réception du signal SIGTERM, tel que purger et fermer les fichiers ouverts. Si vous envoyez un SIGKILL à un processus, vous retirez toute chance au processus d'effectuer un arrêt soigné, ce qui peut avoir des conséquences néfastes.

Mais si l'arrêt soigné ne fonctionne pas, le signal INT ou KILL peut être le seul moyen. Par exemple,quand un processus ne meurt pas avec Ctrl+C, c'est mieux d'utiliser kill -9 sur cet ID de processus:

maud: ~> ps -ef | grep stuck_process
maud    5607   2214  0 20:05 pts/5    00:00:02 stuck_process

maud: ~> kill -9 5607

maud: ~> ps -ef | grep stuck_process
maud    5614    2214 0 20:15 pts/5    00:00:00 grep stuck_process
[1]+ Killed		stuck_process

Quand un processus démarre plusieurs instances, killall peut être plus facile. Elle prend la même option que la commande kill, mais s'applique à toutes les instances d'un processus donné. Tester cette commande avant de l'employer dans un environnement de production, parce qu'elle pourrait ne pas fonctionner comme attendu sur certains UNIX commerciaux.

2. Piéger les signaux

2.1. Généralité

Il peut y avoir des situations ou vous ne souhaitez pas que les usagers de vos scripts quitte abruptement par une séquence de touches du clavier, par exemple parce que une entrée est en attente ou une purge est à faire. L'instruction trap trappe ces séquences et peut être programmée pour exécuter une liste de commandes à la récupération de ces signaux.

La syntaxe de l'instruction trap est directe:

trap [COMMANDS] [SIGNALS]

Ceci indique à la commande trap de récupérer les SIGNAUX listés, qui peuvent être des noms de signaux avec ou sans le préfixe SIG, ou des numéros de signaux. Si un signal est 0 ou EXIT, les COMMANDES sont exécutées quand le Shell se finit. Si l'un des signaux est DEBUG, la liste des COMMANDES est exécutée après chaque commande simple. Un signal peut être aussi spécifié pour ERR; dans ce cas COMMANDES sont exécutées chaque fois qu'une commande simple s'achève avec un statut différent de zéro. Notez que ces commandes ne seront pas exécutées quand le statut d'exécution différent de zéro vient d'une instruction if, ou d'une boucle while ou until. Aucune ne sera exécutée si un AND (&&) ou un OR (||) logique donne un statut d'exécution différent de zéro, ou quand le code retour d'une commande est inversé par l'opérateur !.

Le statut renvoyé par la commande trap elle-même est zéro à moins qu'un signal invalide ait été spécifié. La commande trap admet des options qui sont documentées dans les pages info de Bash.

Voici un exemple très simple, récupérant Ctrl+C frappé par l'usager, ce qui déclenche l'affichage d'un message. Quand vous essayez de tuer ce programme sans spécifier le signal KILL, rien ne se produit:

#!/bin/bash
# traptest.sh

trap "echo Booh!" SIGINT SIGTERM
echo "pid is $$"

while :			# Ceci est équivalent à  "while true".
do
        sleep 60	# Ce script ne fait pas vraiment quelque chose.
done

2.2. Comment Bash interprète trap

Quand Bash reçoit un signal pour lequel un piège a été défini durant l'exécution d'une commande, le piège ne sera mis en oeuvre que une fois que la commande aura terminé. Quand Bash attend après une commande asynchrone via l'intégrée wait, la réception d'un signal pour lequel un piège a été défini fera que l'intégrée wait redonnera la main immédiatement avec un statut d'exécution supérieur à 128, immédiatement après quoi le piège est exécuté.

2.3. Plus d'exemples

2.3.1. Détecter quand une variable est utilisée

Quand vous débuggez de longs scripts, vous pouvez vouloir donner à une variable l'attribut trace et trapper les messages DEBUG pour cette variable. Normalement vous déclarez juste une variable avec une affectation du genre VARIABLE=valeur. En remplaçant la déclaration de la variable avec les lignes suivantes vous pouvez obtenir des informations intéressantes sur ce que fait votre scripts:

declare -t VARIABLE=valeur

trap "echo VARIABLE est utilisée ici." DEBUG

# suite du script

2.3.2. Purger les déchets de traitements avant de quitter

La commande whatis repose sur une base de données qui est régulièrement reconstruite avec le script makewhatis.cron lancé par cron:

#!/bin/bash

LOCKFILE=/var/lock/makewhatis.lock

# Le makewhatis précédent devrait s'être exécuté avec succès:

[ -f $LOCKFILE ] && exit 0

# Avant de quitter, éliminer les fichiers verrous.

trap "{ rm -f $LOCKFILE ; exit 255; }" EXIT

touch $LOCKFILE
makewhatis -u -w
exit 0

3. Résumé

Des signaux peuvent être envoyés à votre programme avec la commande kill ou des raccourcis clavier. Ces signaux peuvent être récupérés, sur quoi une action peut être effectuée, avec l'instruction trap.

Certains programmes ignorent les signaux. Le seul signal qu'aucun programme ne peut ignorer est KILL.

4. Exercices

Quelques exemples:

  1. Ecrire un script qui écrit une image de démarrage système sur une disquette avec l'outil dd. Si l'utilisateur essaye d'interrompre avec Ctrl+C, afficher un message disant que cette action rendra la disquette inutilisable

  2. Ecrire un script qui automatise l'installation d'un paquetage tiers de votre choix. Le paquetage doit être téléchargé par INTERNET. Il doit être décompressé, désarchivé et compilé si ces actions sont appropriées. Seule l'opération d'installation du paquetage ne devrait pas être interruptible.