Странная проблема с ловушкой и SIGINT

/etc/init.d/iptables stop отключит iptables
chkconfig iptables off

5
09.04.2017, 00:14
3 ответа

Странная проблема с ловушкой и SIGINT

Спасибо всем за ответы и за время, потраченное на изучение проблема.

Пожалуйста, позвольте мне резюмировать и интегрировать (с извинениями за то, что может быть очевидным в следующем):

1) Я забыл добавить в свой вопрос, я также пробовал SIGQUIT, и он вел себя как SIGINT;

2 ) Исходя из этого, я уже подозревал, что проблема была связана с расположением по умолчанию интерактивного bash для этих двух сигналов;

3) Их действие по умолчанию не происходит при взаимодействии с bash, потому что нет смысла выходить или прерывать что-либо, когда единственное, что у вас есть, это подсказка. Если вы хотите покинуть оболочку, просто введите exit;

4) Я не вижу, чтобы SIGQUIT и SIGINT играли особую роль в управлении заданиями (в отличие от SIGTSTP, SIGTTOU, SIGTTIN);

5) Нет. Для меня имеет смысл, что расположение по умолчанию интерактивного bash для этих двух сигналов должно быть унаследовано фоновой (неинтерактивной) оболочкой (та, которая выполняет scriptb.sh в нашем случае);

6) Фактически, просто поскольку группа процессов переднего плана не наследует (от оболочки, которая ее запустила) диспозиции для SIGQUIT и SIGINT, IMHO должно иметь смысл, чтобы то же самое произошло с группами фоновых процессов.

7) Более того, какой бы ни была унаследованная предрасположенность, ловушка должна ее изменить.

8) В целом я склонен согласиться с тригоном и думать, что то, что мы здесь видим, является ошибкой.

0
27.01.2020, 20:33

С помощью трассировки:

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 выглядит очень сложным, и есть еще обработка сигналов в других местах...

3
27.01.2020, 20:33

Если я переключаюсь с SIGINT на любой другой сигнал (я пробовал SIGTERM, SIGUSR1) ловушка печатает "Ouch", как и ожидалось.

По-видимому, вы не пробовали SIGQUIT; Вы, вероятно, обнаружите, что он ведет себя так же, как SIGINT.

Проблема заключается в контроле заданий.

В первые дни Unix, всякий раз, когда оболочка помещает процесс или конвейер в фон, он настроил эти процессы на игнорирование SIGINT и SIGQUIT, таким образом, они не будут прекращены, если пользователь впоследствии набрано Ctrl+C (прерывание) или Ctrl+ (выход) для задачи переднего плана.  Когда появился контроль за заданиями, он принес с собой группы процессов, и так теперь все, что нужно сделать оболочке помещает фоновое задание в новую группу процессов; до тех пор, пока это не является текущей группой терминальных процессов, Процессы не будут видеть сигналы, поступающие с клавиатуры (Ctrl+C, Ctrl+ и Ctrl+Z (SIGTSTP)).  Оболочка может оставлять фоновые процессы с расположением сигнала по умолчанию.  На самом деле, это, вероятно, должно быть, чтобы процессы были убиваются. по Ctrl+C, когда они вынесены на передний план.

Но неинтерактивные оболочки не используют управление заданиями.  Имеет смысл, что неинтерактивная оболочка будет отступать назад к старому поведению игнорирования SIGINT и SIGQUIT для фоновых процессов, по исторической причине — чтобы разрешить выполнение фоновых процессов, даже если им посылаются сигналы клавиатурного типа.  А сценарии оболочки выполняются в неинтерактивных оболочках.

И, если вы посмотрите на последний абзац под командой trap в bash(1) вы увидите

Сигналы, игнорируемые при входе в оболочку, не могут быть захвачены или сброшены.

Итак, если вы запустите ./scriptb.sh & из командной строки оболочки interactive, его сигнальные диспозиции остаются в покое (даже если это отодвинуто на задний план), и команда trap работает должным образом.  Но, если вы запустите ./scripta.sh&, или без него), он запускает сценарий в неинтерактивной оболочке.  И когда эта неинтерактивная оболочка запускается ./scriptb.sh &, он настраивает процесс scriptb на игнорирование прерывания и завершение работы.  И поэтому команда trap в scriptb.sh молча терпит неудачу.

11
27.01.2020, 20:33

Теги

Похожие вопросы