Сначала меня смутил ответ jancheta, пока я не обнаружил, что цель 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 Ramey, которое дает некоторые идеи о том, что нужно сделать, чтобы получить поведение, подобное Bash, при получении сигнала 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.
person
Metamorphic
schedule
30.04.2016