Обработка сигналов консоли Windows для подпроцесса c ++ - PullRequest
0 голосов
/ 25 января 2019

Предположим, у нас есть небольшая программа, написанная на C ++, которая выглядит следующим образом.
Эта программа сама намеренно НЕ выполняет обработку сигналов с помощью вызова WinAPI SetConsoleCtrlHandler - это важная часть вопроса.

#include <stdio.h>
#include <stdlib.h>

int main() {
  while(true) {
    int status = system("EXTERNAL COMMAND");
    printf("RESULT STATUS = %d\n", status);
  }
}

Когда на терминале была нажата комбинация клавиш Ctrl+C, вышеуказанная программа имеет совершенно другое поведение, в зависимости от того, какой "EXTERNAL COMMAND" был вызван

1) Если внешняя команда pause, программа будет стоять в бесконечном цикле, вызывая pause команду шаг за шагом, и будет печатать "RESULT STATUS = 0" many times, в то время как не завершена принудительно из-за уничтожения процесса.
2) Если внешняя команда в choice, программа будет завершена сразу послеCtrl+C давление.Он ничего не печатает и не возвращает после вызова system.
3) Если внешняя команда set /P VAR=, программа имеет много интересного поведения.При нажатии Ctrl+C программа печатает «RESULT STATUS = 1» и продолжает работать до тех пор, пока не будет выполнен первый асинхронный вызов.

Первый и второй случай можно объяснить следующим образом.Окна терминала - это прокс между пользовательским вводом и целевой программой, поэтому, когда пользователь нажимает Ctrl+C, окно терминала выполняет сам диспетчерский сигнал для целевого процесса.
Таким образом, некоторые подпроцессы могут вручную обрабатывать обработчик терминала через hConsole = GetStdHandle(STD_OUTPUT_HANDLE) и выполнять собственную обработку сигналов.Другой подпроцесс не делает этого, поэтому сигнал передается в родительский процесс и завершает его.

Но третий случай вызывает большой вопрос.Если дочерний процесс перехватывает SIGINT, почему родительский процесс завершает работу после первого асинхронного вызова.Если нет, то почему он не завершается немедленно и почему и как он печатает `" RESULT STATUS = 1 "и продолжает работать.

Спасибо

1 Ответ

0 голосов
/ 26 января 2019

В Windows нет сигналов Unix, по крайней мере, от ядра. Тем не менее, Windows и Windows API основаны на языке программирования C, который, будучи разработанным в тандеме с Unix, требует шесть сигналов . Среда выполнения C в Windows эмулирует SIGABRT и SIGTERM внутри процесса (например, для использования с C raise). Для SIGSEGV, SIGILL и SIGFPE используется обработчик исключений ОС. В консольном приложении стандартные SIGINT и нестандартные SIGBREAK связаны с обработчиком управления консоли среды выполнения C, который обычно является первым обработчиком, зарегистрированным через SetConsoleCtrlHandler. CTRL_C_EVENT сопоставлен с обработчиком сигнала SIGINT, а все остальные (CTRL_BREAK_EVENT, CTRL_CLOSE_EVENT) сопоставлены с обработчиком SIGBREAK.

События управления консолью отправляются хостом консоли (conhost.exe), который реализует это, заставляя сервер сеанса (csrss.exe) создать поток в клиентском процессе. Этот поток начинается с недокументированной функции CtrlRoutine в kernelbase.dll, которая обходит зарегистрированные обработчики элементов управления, пока один из них не обработает событие, возвращая true. Если ни один из них не обрабатывает событие, обработчик по умолчанию вызывает ExitProcess(STATUS_CONTROL_C_EXIT). Обратите внимание, что SetConsoleCtrlHandler(NULL, TRUE) устанавливает флаг, который заставляет CtrlRoutine игнорировать CTRL_C_EVENT, и этот флаг наследуется дочерними процессами и включается по умолчанию, когда процесс создается с флагом CREATE_NEW_PROCESS_GROUP. Кроме того, для CTRL_CLOSE_EVENT сервер сеансов дает каждому процессу 5 секунд для обработки события и выхода самостоятельно, в противном случае он принудительно завершает процесс.

Чтобы понять, что происходит с внутренней командой CMD PAUSE, см. SetConsoleMode и, в частности, ENABLE_PROCESSED_INPUT. PAUSE вызывает C _getch, который временно устанавливает консольный режим ввода на 0. При отключенном режиме обработанного ввода Ctrl + C просто читается как "\ x03" вместо генерации CTRL_C_EVENT.

...