Сначала я был смущен ответом Джанчета, пока не обнаружил, что цель siglongjmp
состоит в том, чтобы разблокировать принятый сигнал в маске сигнала, прежде чем совершать прыжок. Сигнал блокируется при вводе обработчика сигнала, так что обработчик не прерывает себя. Мы не хотим оставлять сигнал заблокированным, когда мы возобновляем нормальное выполнение, поэтому мы используем siglongjmp
вместо longjmp
. AIUI, это просто сокращение, мы могли бы также позвонить sigprocmask
, а затем longjmp
, что, кажется, то, что glibc делает в siglongjmp
.
Я думал, что это может быть опасно делать прыжок, потому что readline()
звонки malloc
и free
. Если сигнал принят, в то время как некоторая неактивная функция асинхронного сигнала, такая как malloc
или free
, модифицирует глобальное состояние, может произойти некоторое повреждение, если мы должны были выскочить из обработчика сигнала. Но Readline устанавливает собственные обработчики сигналов, которые осторожно относятся к этому. Они просто устанавливают флаг и выходят; когда библиотека Readline снова получает контроль (обычно после прерывания вызова read()), он вызывает RL_CHECK_SIGNALS()
, который затем пересылает любой ожидающий сигнал в клиентское приложение с использованием kill()
. Таким образом, можно безопасно использовать siglongjmp()
для выхода из обработчика сигнала для сигнала, который прервал вызов readline()
- сигнал гарантированно не был получен во время небезопасной функции асинхронного сигнала.
На самом деле, это не совсем верно, потому что есть несколько вызовов malloc()
и free()
в пределах rl_set_prompt()
, которые readline()
вызовов непосредственно перед rl_set_signals()
. Интересно, должен ли этот порядок вызова быть изменен. В любом случае вероятность состояния гонки очень тонкая.
Я посмотрел исходный код Bash и, похоже, выпрыгнул из своего обработчика SIGINT.
Другим интерфейсом Readline, который вы можете использовать, является интерфейс обратного вызова. Это используется приложениями, такими как Python или R, которые необходимо прослушивать сразу в нескольких дескрипторах файлов, например, чтобы определить, изменяется ли окно графика при активном интерфейсе командной строки. Они сделают это в цикле select()
.
Вот сообщение от Chet Рэми, который дает некоторые идеи о том, что нужно сделать, чтобы получить Баш поведение как при получении SIGINT в интерфейсе обратного вызова:
https://lists.gnu.org/archive/html/bug-readline/2016-04/msg00071.html
выводятся сообщения говорит о том, что вы делаете что-то вроде это:
rl_free_line_state();
rl_cleanup_after_signal();
RL_UNSETSTATE(RL_STATE_ISEARCH|RL_STATE_NSEARCH|RL_STATE_VIMOTION|RL_STATE_NUMERICARG|RL_STATE_MULTIKEY);
rl_line_buffer[rl_point = rl_end = rl_mark = 0] = 0;
printf("\n");
Когда SIGINT получен, вы можете установить флаг, а затем проверить флаг в вашем select()
цикле - так как select()
вызов будет получить прерван по сигналу с errno==EINTR
. Если вы обнаружите, что флаг установлен, выполните вышеуказанный код.
Мое мнение таково, что Readline должен запускать что-то вроде вышеуказанного фрагмента в своем собственном коде обработки SIGINT. В настоящее время он более или менее выполняет только первые две строки, поэтому такие вещи, как макросы инкрементного поиска и клавиатуры, отменены на^C, но строка не очищается.
Другой плакат сказал «Звоните rl_clear_signals()», который все еще меня смущает. Я не пробовал, но я не вижу, как это будет выполнено, учитывая, что (1) обработчики сигналов Readline передают вам сигнал в любом случае, и (2) readline()
устанавливает обработчики сигналов при вводе (и очищает их при выходе), поэтому они обычно не будут активны вне кода Readline.
Вы не можете безопасно вызывать 'printf' из обработчика сигналов. – pat