Странное поведение Perl SIGPIPE («IGNORE» работает, саб не работает) - PullRequest
0 голосов
/ 23 сентября 2018

Как отмечено в комментариях к этим вопросам:

perl -e '$SIG{PIPE} = "IGNORE"; sleep(1); print "foo\n";' | echo -n

выдает сообщение об ошибке Unable to flush stdout: Broken pipe.

Но странно

perl -e '$SIG{PIPE} = sub { print STDERR "XXX\n"; }; sleep(1); print "foo\n";' | echo -n

не выдает никакого сообщения.

Почему вторая команда не выдает сообщение?Как перехватить это событие SIGPIPE?

Что мне действительно нужно (для эксперимента, чтобы ответить на этот вопрос ), чтобы перехватить SIGPIPE и, когда это произойдет, вывести что-то в файл журнала.

1 Ответ

0 голосов
/ 23 сентября 2018

При записи в канал, как в вашем примере, STDOUT буферизуется по умолчанию.Это означает, что print "foo\n" в вашем коде не сразу записывает в STDOUT, а записывает в буфер, и буфер будет очищаться (записываться в STDOUT) только тогда, когда он полон или является частью очистки программы.Использование strace для просмотра того, что на самом деле делает ваша программа, показывает, что она сначала восстанавливает обработчики сигналов как часть очистки программы, а затем записывает в STDOUT, что приводит к SIGPIPE, который больше не обрабатывается вашим сабвуфером и, таким образом, приводит к уничтожениюпрограммы.

Если вы убедитесь, что вывод выполняется немедленно, а не при очистке программы, ваш обработчик сигнала будет успешно запущен.Это можно сделать, отключив буферизацию STDOUT с помощью $|=1:

$ perl -e \
   '$|=1; $SIG{PIPE} = sub { print STDERR "XXX\n"; }; sleep(1); print "foo\n";' \
   | echo -n

output: XXX

Другими словами: если ваш обработчик сигнала работает, он работает отлично, и это был только артефакт вашего конкретного использования, который он не вызвал.Почему он не сбрасывает обработчик сигнала с игнорирования (SIG_IGN) на значение по умолчанию (SIG_DFL) при выходе и, таким образом, показывает ту же проблему с IGNORE, которого я не знаю.

...