С помощью трассировки:
strace -o aaa ./scripta
мы можем наблюдать, что по умолчанию
read(255, "#!/bin/bash\n# this is scripta.sh"..., 153) = 153
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
lseek(255, -108, SEEK_CUR) = 45
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f1ee9b7ca10) = 2483
так scripta
заблокировала INT
и CHLD
сигналы; scriptb
наследует эти настройки через fork
(здесь называется clone
). А что делает scriptb
? Если мы запустим его из scripta
через:
strace -o bbb ./scriptb &
И затем поищем вещи, связанные с сигналами, мы найдем:
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_IGN, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fb1b2e75250}, {SIG_DFL, [], SA_RESTORER, 0x7fb1b2e75250}, 8) = 0
Что указывает на то, что ничего не блокируется, а затем, что сигналы INT
сначала обрабатываются по умолчанию, а затем игнорируются. scriptb
, запущенный непосредственно из оболочки под strace
, напротив, показывает:
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT], [], 8) = 0
rt_sigaction(SIGINT, {0x45fbf0, [], SA_RESTORER, 0x7f2c3f6d4250}, {SIG_DFL, [], SA_RESTORER, 0x7f2c3f6d4250}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [INT CHLD], [], 8) = 0
rt_sigprocmask(SIG_BLOCK, ~[RTMIN RT_1], [INT CHLD], 8) = 0
...
Или никогда не игнорируется, прежде чем перейти к повторяющейся обработке вызова sleep. Хорошо. Ух. Давайте поместим между scripta
и scriptb
шим, который сбрасывает SIGINT
в состояние по умолчанию...
#include <getopt.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "h?")) != -1) {
switch (ch) {
case 'h':
case '?':
default:
abort();
}
}
argc -= optind;
argv += optind;
if (argc < 1) abort();
signal(SIGINT, SIG_DFL);
execvp(*argv, argv);
abort();
return 1;
}
Используется следующим образом:
$ make defaultsig
cc defaultsig.c -o defaultsig
$ grep defaultsig scripta.sh
./defaultsig ./scriptb.sh &
$ ./scripta.sh
12510 started
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
scriptb.sh 12510
scriptb.sh 12510
scriptb.sh 12510
scripta.sh 12509
Ouch
...
Ага, теперь работает хорошо. Однако, я не знаю, почему bash
ведет себя таким образом, может, написать им об ошибке? Код в sig.c
выглядит очень сложным, и есть еще обработка сигналов в других местах...