Windows: Как Jupyter генерирует прерывание клавиатуры?
Я знаю, как прервать ядро (например, нажав I
дважды или прерывая ядро в веб-интерфейсе). Тем не менее, я построил C-расширение для Python (я использую Windows), которое обрабатывает события CTRL-C в моем коде C++ (игрушечный пример):
static int s_interrupted = 0;
BOOL WINAPI consoleHandler(DWORD fdwCtrlType) {
switch (fdwCtrlType)
{
// Handle the CTRL-C signal.
case CTRL_C_EVENT:
s_interrupted = 1;
return TRUE;
}
}
int main() {
s_interrupted = 0;
int output = 1;
if (!SetConsoleCtrlHandler(consoleHandler, TRUE)) {
std::cout<<"ERROR: Could not set control handler"<<std::endl;
} else {
std::cout<<"Control hanlder installed"<<std::endl;
}
int k = 10000;
while (int i < k) {
if (s_interrupted == 1) {
output = -1;
break;
}
output = i
i = i + 1;
}
return output;
}
Вывод моей основной программы меняется в зависимости от значения s_interrupted
, Другими словами, если я не нажму CTRL+C, программа завершит цикл while и вернет целое число. Если я нажму CTRL+C, цикл while будет прерван и вернет другое целое число. Я не ожидаю увидеть KeyboardInterrupt
в моем терминале Python.
Он прекрасно работает, когда я вызываю это расширение C в терминале. Однако, когда я делаю это в блокноте Jupyter, программа ведет себя так, как будто я никогда не прерывал ядро. Юпитер не посылает SIGINT
когда я прерываю ядро?
2 ответа
Вы не можете использовать consoleHandler()
здесь, потому что нет консоли. Ядро IPython является "безголовым" дочерним процессом, который выполняет код по запросу, направляемый внешним интерфейсом Jupyter.
Чтобы прервать работающее ядро IPython, интерфейс Jupyter использует SIGINT
сигнал. Это делается как на POSIX, так и на Windows; в Windows Jupyter использует дополнительную инфраструктуру, построенную вокруг CreateEventA
, SetEvent
а также WaitForMultipleObjects
добиться того же результата, что и POSIX os.killpg(PID, SIGINT)
вызов; отдельный поток опрашивает событие и запускает SIGINT
сигнал в основной теме.
Обратите внимание, что ядро IPython явно восстанавливает обработчик сигналов Python по умолчанию для каждого сообщения, которое оно обрабатывает. Увидеть ipykernel.kernelbase.Kernel
реализации для pre_
а также post_handler_hook
методы:
self.saved_sigint_handler = signal(SIGINT, default_int_handler)
а также
signal(SIGINT, self.saved_sigint_handler)
Эти две перехваты выполняются до и после каждого обработчика сообщений (то есть для каждого сообщения, отправляемого веб-интерфейсом процессу ядра, включая сообщения execute).
это signal.default_int_handler
что поднимает KeyboardInterrupt
в основном потоке Python. Если ваш код должен обнаруживать прерывания, он должен будет зарегистрировать свой собственный signal
обработчик каждый раз, когда IPython запускает ячейку.
Примечание: автономный интерактивный интерпретатор Python не использует SetConsoleCtrlHandler
также обнаруживать прерывания клавиатуры; единственное место, которое используется в исходном коде Python, находится в py
лаунчер, и только потом заглушить управляющие коды с помощью обработчика, который возвращает TRUE
всегда. Вместо этого Python полагается на Windows, отправляющую SIGINT
сигнал всем подключенным процессам консоли для активного окна консоли.
Ваш код не устанавливает обработчики сигналов и s_signal_handler
не используется Вам нужно позвонить signal
функция для регистрации вашего обратного вызова.
#include <atomic>
#include <signal.h>
::std::atomic<bool> s_interrupted{};
static void signal_handler(int signal)
{
s_interrupted = true;
}
int main()
{
::signal(SIGINT, &::signal_handler);