子流程c ++的Windows控制台信号处理

时间:2019-01-25 09:49:30

标签: c++ windows cmd signals windows-console

假设我们有一个用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 :(得分:1)

Windows中没有Unix信号,至少不是来自内核的信号。也就是说,Windows和Windows API基本上基于C编程语言,而C语言是与Unix协同开发的,确实需要six signals。 Windows中的C运行时在进程内模拟SIGABRTSIGTERM(例如,与C raise一起使用)。对于SIGSEGVSIGILLSIGFPE,它使用OS异常处理程序。在控制台应用程序中,标准SIGINT和非标准SIGBREAK与C运行时的控制台控制处理程序关联,通常是通过SetConsoleCtrlHandler注册的第一个处理程序。 CTRL_C_EVENT映射到SIGINT信号处理程序,所有其他(CTRL_BREAK_EVENTCTRL_CLOSE_EVENT)映射到SIGBREAK处理程序。

控制台控制事件由控制台主机(conhost.exe)发送,控制台主机通过使会话服务器(csrss.exe)在客户端进程中创建线程来实现此目的。该线程始于kernelbase.dll中未公开的CtrlRoutine函数,该函数遍历已注册的控制处理程序,直到其中一个通过返回true来处理事件。如果它们都不处理事件,则默认处理程序调用{​​{1}}。请注意,ExitProcess(STATUS_CONTROL_C_EXIT)设置了一个标志,使SetConsoleCtrlHandler(NULL, TRUE)忽略CtrlRoutine,并且该标志由子进程继承,并且在创建带有标志CTRL_C_EVENT的进程时默认启用。此外,对于CREATE_NEW_PROCESS_GROUP,会话服务器会给每个进程5秒的时间来处理事件并自行退出,否则它将强制终止该进程。

要了解CMD内部的CTRL_CLOSE_EVENT命令正在发生什么,请参阅SetConsoleMode,尤其是PAUSEENABLE_PROCESSED_INPUT调用C PAUSE,这会暂时将控制台输入模式设置为0。在禁用输入处理模式的情况下,Ctrl + C只是读为“ \ x03”,而不是生成{{1} }。