(Вдохновленный ответом Жиля)
При установленном ISIG
флаге единственный способ Child
получить сценарий SIGINT
без получения родительского элемента SIGINT
- это войти в свою собственную группу процессов. Это может быть достигнуто с set -m
опцией.
Если вы включите -m
опцию в Child
сценарии оболочки, он будет выполнять управление заданиями, не будучи интерактивным. Это заставит его запускать вещи в отдельной группе процессов, предотвращая получение родительского элемента, SIGINT
когда INTR
читается символ.
Вот описание -m
опции POSIX :
-m
Эта опция должна поддерживаться, если реализация поддерживает опцию User Portability Utilities. Все задания должны выполняться в своих собственных группах процессов. Непосредственно перед тем, как оболочка выдает подсказку после завершения фонового задания, сообщение о состоянии завершения фонового задания должно быть записано со стандартной ошибкой. Если приоритетное задание останавливается, оболочка должна записать сообщение со стандартной ошибкой на этот счет в формате, описанном утилитой заданий. Кроме того, если задание изменяет состояние, отличное от выхода (например, если оно останавливается для ввода или вывода или останавливается сигналом SIGSTOP), оболочка должна написать аналогичное сообщение непосредственно перед написанием следующего приглашения. Эта опция включена по умолчанию для интерактивных оболочек.
-m
Вариант похож -i
, но это не меняет поведение оболочечных почти столько же , сколько -i
делает.
Пример:
Parent
скрипт:
#!/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"
Child
скрипт:
#!/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"
Вот что происходит, когда вы нажимаете Control+, Cпока Child
ожидает ввода:
$ ./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
Обратите внимание, как SIGINT
обработчик родителя никогда не выполняется.
Кроме того, если вы хотите изменить Parent
вместо Child
, вы можете сделать это:
Parent
скрипт:
#!/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"
Child
скрипт (нормальный, нет необходимости -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"
Альтернативные идеи
- Измените другие процессы в группе процессов переднего плана, чтобы игнорировать их
SIGINT
в течение Child
. Это не отвечает на ваш вопрос, но может дать вам то, что вы хотите.
- Изменить
Child
на:
- Используйте
stty -g
для резервного копирования текущих настроек терминала.
- Бегите ,
stty -isig
чтобы не генерировать сигналы с INTR
, QUIT
и SUSP
персонажами.
- В фоновом режиме прочитайте входной сигнал терминала и отправьте сигналы самостоятельно (например, запустите,
kill -QUIT 0
когда Control+ \читается, kill -INT $$
когда Control+ Cчитается). Это не тривиально, и может быть невозможно заставить это работать гладко, если Child
сценарий или все, что он выполняет, должно быть интерактивным.
- Восстановите настройки терминала перед выходом (в идеале из ловушки
EXIT
).
- То же, что №2, но вместо того, чтобы работать
stty -isig
, подождите, пока пользователь нажмет Enterили другой не специальный ключ, прежде чем убивать Child
.
Напишите свою собственную setpgid
утилиту на C, Python, Perl и т. Д., Которую вы можете использовать для вызова setpgid()
. Вот грубая реализация C:
#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;
}
Пример использования от 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
). Примеры:${ENV}
источник получен, оболочка не выйдет сразу, когда обнаружит ошибку,SIGQUIT
иSIGTERM
игнорируется.Что ж, из вопроса о переполнении стека, на которое вы ссылались, ясно говорится, что родительский элемент должен быть настроен для обработки сигнала.
Во-вторых, ссылка на POSIX четко гласит, что «Если установлен ISIG, символ INTR должен быть отброшен при обработке».
Так что это два варианта. Третий - запустить ребенка в своей собственной группе процессов.
источник