(Inspiré par la réponse de Gilles)
Avec l' ISIG
indicateur défini, le seul moyen pour le Child
script d'obtenir SIGINT
sans obtenir son parent SIGINT
est qu'il se trouve dans son propre groupe de processus. Cela peut être accompli avec l' set -m
option.
Si vous activez l' -m
option dans le Child
script shell, il effectuera le contrôle des travaux sans être interactif. Cela entraînera son exécution dans un groupe de processus distinct, empêchant le parent de recevoir le SIGINT
lorsque le INTR
caractère est lu.
Voici la description POSIX de l' -m
option :
-m
Cette option doit être prise en charge si la mise en œuvre prend en charge l'option User Portability Utilities. Tous les travaux doivent être exécutés dans leurs propres groupes de processus. Immédiatement avant que le shell émette une invite après la fin de la tâche en arrière-plan, un message signalant l'état de sortie de la tâche en arrière-plan doit être écrit sur l'erreur standard. Si un travail de premier plan s'arrête, le shell doit écrire un message d'erreur standard à cet effet, formaté comme décrit par l'utilitaire de travaux. En outre, si un travail change d'état autre que la sortie (par exemple, s'il s'arrête pour une entrée ou une sortie ou est arrêté par un signal SIGSTOP), le shell doit écrire un message similaire immédiatement avant d'écrire l'invite suivante. Cette option est activée par défaut pour les shells interactifs.
L' -m
option est similaire à -i
, mais elle ne modifie pas autant le comportement du shell -i
.
Exemple:
le Parent
script:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
./Child
echo "PARENT: child returned"
echo "PARENT: exiting normally"
le Child
script:
#!/bin/sh -m
# ^^
# notice the -m option above!
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Voici ce qui se passe lorsque vous appuyez sur Control+ Cen Child
attendant l'entrée:
$ ./Parent
PARENT: pid=12233
PARENT: Spawning child...
CHILD: pid=12234
CHILD: hit enter to exit
^CCHILD: caught SIGINT; exiting
PARENT: child returned
PARENT: exiting normally
Remarquez comment le SIGINT
gestionnaire du parent n'est jamais exécuté.
Alternativement, si vous préférez modifier Parent
au lieu de Child
, vous pouvez le faire:
le Parent
script:
#!/bin/sh
trap 'echo "PARENT: caught SIGINT; exiting"; exit 1' INT
echo "PARENT: pid=$$"
echo "PARENT: Spawning child..."
sh -m ./Child # or 'sh -m -c ./Child' if Child isn't a shell script
echo "PARENT: child returned"
echo "PARENT: exiting normally"
le Child
script (normal; pas besoin de -m
):
#!/bin/sh
trap 'echo "CHILD: caught SIGINT; exiting"; exit 1' INT
echo "CHILD: pid=$$"
echo "CHILD: hit enter to exit"
read foo
echo "CHILD: exiting normally"
Idées alternatives
- Modifiez les autres processus du groupe de processus de premier plan à ignorer
SIGINT
pendant la durée de Child
. Cela ne répond pas à votre question, mais cela peut vous donner ce que vous voulez.
- Modifiez
Child
en:
- Utilisez
stty -g
pour sauvegarder les paramètres actuels du terminal.
- Exécuter
stty -isig
pour ne pas générer des signaux avec les INTR
, QUIT
et les SUSP
personnages.
- En arrière-plan, lisez l'entrée du terminal et envoyez les signaux vous-même selon le cas (par exemple, exécutez
kill -QUIT 0
lorsque Control+ \est lu, kill -INT $$
lorsque Control+ Cest lu). Ce n'est pas anodin et il peut ne pas être possible de le faire fonctionner correctement si le Child
script ou tout ce qu'il exécute est censé être interactif.
- Restaurez les paramètres du terminal avant de quitter (idéalement à partir d'un piège
EXIT
).
- Identique à # 2, sauf qu'au lieu de courir
stty -isig
, attendez que l'utilisateur frappe Enterou une autre clé non spéciale avant de tuer Child
.
Écrivez votre propre setpgid
utilitaire en C, Python, Perl, etc. que vous pouvez utiliser pour appeler setpgid()
. Voici une implémentation C brute:
#define _XOPEN_SOURCE 700
#include <unistd.h>
#include <signal.h>
int
main(int argc, char *argv[])
{
// todo: add error checking
void (*backup)(int);
setpgid(0, 0);
backup = signal(SIGTTOU, SIG_IGN);
tcsetpgrp(0, getpid());
signal(SIGTTOU, backup);
execvp(argv[1], argv + 1);
return 1;
}
Exemple d'utilisation de Child
:
#!/bin/sh
[ "${DID_SETPGID}" = true ] || {
# restart self after calling setpgid(0, 0)
exec env DID_SETPGID=true setpgid "$0" "$@"
# exec failed if control reached this point
exit 1
}
unset DID_SETPGID
# do stuff here
ksh
). Exemples:${ENV}
provient, le shell ne se fermera pas immédiatement lorsqu'il rencontre une erreur,SIGQUIT
etSIGTERM
est ignoré.