Когда обрабатывается сигнал и почему некоторая информация останавливается? - PullRequest
0 голосов
/ 19 ноября 2018
  1. Откройте терминал с именем «termA» и запустите созданный файл callback.sh с /bin/bash callback.sh.

    cat  callback.sh
    #!/bin/bash
    myCallback() {
        echo "callback function called at $(date)"
    }
    trap myCallback SIGUSR1
    sleep 20 
    
  2. Откройте новый терминал с именем «termB» и запустите:

    pkill -USR1  -f  callback.sh
    

После 20 секунд втерма;он никогда не показывается в термине A мгновенно:

callback function called at Mon Nov 19 08:21:52 HKT 2018

В заключении подтверждается, что когда Bash выполняет внешнюю команду на переднем плане, он не обрабатывает сигналы, полученные до тех пор, пока процесс на переднем плане не завершится (см. trap-сигнал).

Сделайте небольшое изменение в callback.sh:

cat  callback.sh
#!/bin/bash
myCallback() {
    echo "callback function called at $(date)"
}
trap myCallback SIGUSR1
while true; do
    read -p  "please input something for foo: " foo
done

Добавьте бесконечный цикл while и удалите sleep 20.

  1. Откройте терминал с именем «termA» и запустите созданный файл callback.sh с помощью /bin/bash callback.sh;в первый раз информация всплывает мгновенно.

    please input something for foo: 
    
  2. Откройте новый терминал с именем "termB" и выполните pkill -USR1 -f callback.sh;в первый раз информация всплывает мгновенно в termA.

    callback function called at Mon Nov 19 09:07:14 HKT 2018
    

Проблема 1: callback.sh содержит бесконечный цикл while.Как это объясняет следующее?

он не обрабатывает сигналы, полученные до тех пор, пока процесс переднего плана не завершится

В этом случае процесс переднего плана никогда не завершится.

Продолжайте работать в termB, запускайте pkill -USR1 -f callback.sh во второй раз.

callback function called at Mon Nov 19 09:07:14 HKT 2018

Приведенная выше информация мгновенно всплывает в termA снова.

Проблема 2: Нет please input something for foo:, отображаемое в termA, перейти к termB, запустить pkill -USR1 -f callback.sh в третий раз, следующая информация снова появится в termA.

callback function called at Mon Nov 19 09:07:24 HKT 2018

Все еще нет please input something for foo: в терминах A.
Почему информация please input something for foo: зависает?

Ответы [ 3 ]

0 голосов
/ 21 ноября 2018

Перед объяснением вашей проблемы, немного контекста о том, как работает команда read. Он считывает во входных данных от stdin до EOF. Можно с уверенностью сказать, что вызов команды read неблокируемый, когда дело доходит до чтения из файлов на диске. Но когда stdin подключен к терминалу, команда будет блокироваться, пока пользователь не наберет что-нибудь.

Как работают обработчики сигналов?

Простое объяснение того, как работает обработка сигналов. Смотрите приведенный ниже фрагмент в C, который действует только на SIGINT (он же CTRL+C)

enter image description here

#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

0 голосов
/ 22 ноября 2018

Вывод подтвердил, что когда bash выполняет внешнюю команду на переднем плане, она не обрабатывает сигналы, полученные до тех пор, пока процесс на переднем плане не завершится.

bash обрабатывает сигналы на уровне C / уровень реализации, но не будет запускать обработчики, установленные с trap, до тех пор, пока процесс переднего плана не будет завершен. Это требуется по стандарту :

When a signal for which a trap has been set is received while the shell is waiting for the completion of a utility executing a foreground command, the trap associated with that signal shall not be executed until after the foreground command has completed.

Обратите внимание, что стандарт не делает различий между встроенными и внешними командами; в случае «утилиты», подобной read, которая не выполняется в отдельном процессе, неясно, происходит ли сигнал в его «контексте» или в контексте основной оболочки, и установлен ли обработчик с trap должен выполняться до или после возврата read.

выпуск 1: callback.sh содержит бесконечный цикл while, как объяснить, что он не обрабатывает сигналы, полученные до тех пор, пока не завершится процесс переднего плана. В этом случае процесс переднего плана никогда не завершится.

Это неправильно. bash не выполняет цикл while в отдельном процессе. Поскольку нет приоритетного процесса, ожидающего bash, он может немедленно запустить любой обработчик, установленный с trap. Если бы read была внешней командой, то, а не while, был бы процесс переднего плана.

выпуск2: нет please input something for foo: shown в термине A;

перейти к termB, запустить pkill -USR1 -f callback.sh в третий раз.

Следующая информация снова отображается в термине A: callback function called at Mon Nov 19 09:07:24 HKT 2018

Все еще нет please input something for foo:, показанное в termA. Почему информация please input something for foo: зависает?

Он не замерзает. Просто нажмите Введите , и он появится снова.

Это просто, что

a) bash перезапустит встроенную read 1059 * вместо при прерывании сигнала - он не вернется и не пройдет через цикл while

b) в этом случае он не будет отображать приглашение, установленное с помощью -p.

Обратите внимание, что bash даже не отобразит приглашение снова в случае обработки SIGINT с клавиатуры, даже если оно отбрасывает строку, прочитанную так далеко от пользователя:

$ cat goo
trap 'echo INT' INT
echo $$
read -p 'enter something: ' var
echo "you entered '$var'"

$ bash goo
24035
enter something: foo<Ctrl-C>^CINT
<Ctrl-C>^CINT
<Ctrl-C>^CINT
bar<Enter>
you entered 'bar'

Будет напечатано you entered 'foobar', если сигнал был отправлен из другого окна с помощью kill -INT <pid> вместо Ctrl-C с терминала.

Весь материал в этой последней части (как прерывается read и т. Д.) Очень специфичен bash. В других оболочках, таких как ksh или dash и даже в bash при работе в режиме POSIX (bash --posix), любой обработанный сигнал фактически прервет встроенную функцию read. В приведенном выше примере оболочка напечатает you entered '' и выйдет после первого ^ C, и если read вызывается из цикла, цикл будет перезапущен.

0 голосов
/ 21 ноября 2018

termA не замерзает. Это просто отображение обратного вызова в поле ввода. Просто нажмите клавишу Enter, чтобы продолжить ввод.

...