Перед объяснением вашей проблемы, немного контекста о том, как работает команда read
. Он считывает во входных данных от stdin
до EOF
. Можно с уверенностью сказать, что вызов команды read
неблокируемый, когда дело доходит до чтения из файлов на диске. Но когда stdin
подключен к терминалу, команда будет блокироваться, пока пользователь не наберет что-нибудь.
Как работают обработчики сигналов?
Простое объяснение того, как работает обработка сигналов. Смотрите приведенный ниже фрагмент в C
, который действует только на SIGINT
(он же CTRL+C
)
#include <stdio.h>
#include <signal.h>
/* signal handler definition */
void signal_handler(int signum){
printf("Hello World!\n");
}
int main(){
//Handle SIGINT with a signal handler
signal(SIGINT, signal_handler);
//loop forever!
while(1);
}
Он зарегистрирует обработчик сигнала и затем войдет в бесконечный цикл. Когда мы нажимаем Ctrl-C
, мы все можем согласиться с тем, что обработчик сигнала signal_handler()
должен выполняться и "Hello World!"
печатать на экране, но программа была в бесконечном цикле. Чтобы напечатать "Hello World!"
, это должно было быть так, что он разорвал цикл для выполнения обработчика сигнала, верно? Поэтому он должен выйти из цикла, а также из программы. Посмотрим:
gcc -Wall -o sighdl.o signal.c
./sighdl.o
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
^CHello World!
Как показывают выходные данные, каждый раз, когда мы печатаем Ctrl-C
, "Hello World!"
печатает, но программа возвращается в бесконечный цикл. Только после выдачи сигнала SIGQUIT
с Ctrl-\
программа фактически завершила работу. В зависимости от ваших ulimit
настроек, он будет выгружать ядро или распечатывать полученный номер сигнала.
Хотя интерпретация выхода из цикла является разумной, она не учитывает основную причину обработки сигналов, то есть асинхронную обработку событий. Это означает, что обработчик сигнала действует вне стандартного потока управления программой; фактически вся программа сохраняется в контексте, и создается новый контекст только для того, чтобы обработчик сигнала мог выполнить его. Как только обработчик сигнала завершит свои действия, контекст переключается обратно и начинается нормальный поток выполнения (т.е. while(1)
).
Чтобы ответить на ваши вопросы,
Вывод подтвердил, что когда bash выполняет внешнюю команду на переднем плане, он не обрабатывает никакие сигналы, полученные до тех пор, пока процесс на переднем плане не завершится
Ключевым моментом, на который следует обратить внимание, является командная часть external . В первом случае sleep
является внешним процессом, а во втором случае read
является встроенным в самой оболочке. Таким образом, распространение сигнала на эти два отличается в обоих этих случаях
type read
read is a shell builtin
type sleep
sleep is /usr/bin/sleep
1.Откройте терминал с именем termA и запустите созданный файл callback.sh с / bin / bash callback.sh в первый раз, информация мгновенно появится.
Да, такое поведение ожидается. Потому что в настоящее время определена только функция, и обработчик прерываний зарегистрирован в функции myCallback
, а сигнал еще не получен в сценарии. По мере выполнения последовательности в первый раз выдается сообщение из приглашения read
.
2.Откройте новый терминал с именем termB и запустите pkill -USR1 -f callback.sh
в первый раз, информация мгновенно появится в termA
Да, пока команда read
ожидает строку, за которой следует нажатие клавиши Enter , которая сигнализирует EOF
, она получает сигнал SIGUSR1
от другого терминала, текущий контекст выполнения сохраняется и управление переключается обработчиком сигнала, который печатает строку с текущей датой.
Как только обработчик заканчивает выполнение, контекст возобновляется в цикле while
, в котором команда read
все еще ожидает входную строку. До тех пор, пока команда read
не будет успешной, все последующие прерывания сигнала будут просто печатать строку внутри обработчика сигнала.
Продолжить в termB, запустить pkill -USR1 -f callback.sh
во второй раз.
Как и объяснено ранее, команда read
не завершается ни разу в цикле while, только если она успешно читает строку, начнется следующая итерация цикла и будет выдано новое приглашение.
Источник изображения: Интерфейс программирования Linux от Michael KerrisK