c++ - 子进程 C++ 的 Windows 控制台信号处理

标签 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 "多次,但未通过进程终止强制终止。
2) 如果在选择中有外部命令,程序将在Ctrl+C按下后立即终止。它不会打印任何内容,也不会从 system 调用返回。
3) 如果外部命令是set/P VAR=,程序会有很多有趣的行为。当按下 Ctrl+C 时,程序打印 `"RESULT STATUS = 1"并继续工作直到执行第一次异步调用。

第一种和第二种情况可以用以下方式解释。终端窗口位于用户输入和目标程序之间,因此当用户按下 Ctrl+C 时,终端窗口会自行向目标进程发送信号。
因此,一些子进程可以通过 hConsole = GetStdHandle(STD_OUTPUT_HANDLE) 手动获取终端处理程序并执行自己的信号处理。另一个子进程不这样做,所以信号传递给父进程并终止它。

但是第三种情况引起了很大的问题。如果子进程拦截 SIGINT,为什么父进程在第一次异步调用后执行终止。如果不是,为什么它不立即终止,为什么以及如何打印 `"RESULT STATUS = 1"并继续工作。

谢谢

最佳答案

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

控制台控制事件由控制台主机 (conhost.exe) 发送,它通过让 session 服务器 (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 , session 服务器给每个进程 5 秒的时间来处理事件并自行退出,否则它会强制终止进程。

了解 CMD 的内部发生了什么 PAUSE命令,参见 SetConsoleMode ,特别是 ENABLE_PROCESSED_INPUT . PAUSE调用 C _getch ,它暂时将控制台输入模式设置为 0。在禁用处理输入模式的情况下,Ctrl+C 被简单地读作“\x03”,而不是生成 CTRL_C_EVENT。 .

关于c++ - 子进程 C++ 的 Windows 控制台信号处理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54362699/

相关文章:

c++ - 如何从 C++ 使用 Windows API 获取应用程序的 CPU 使用率或磁盘使用率?

javascript - 在 Node.js 命令上,为什么我收到错误消息 "windows cannot find $. Make sure you' 已经正确输入了名称...”

java - 为什么 java 参数在 Windows 和 Linux 中的行为不同?

c# - 如何在 dll 中创建可以通过运行它的应用程序更改的条件

c++ - 发送 MAPI 消息时遇到问题

c++ - 使用纯虚拟成员地址的虚拟调用。合法吗?

c++ - 如果它被声明为 double ,如何在数字中添加逗号?

c# - 获取 Windows 版本

c++ - 如何使用 win32 api 将我的应用程序窗口放置在 Windows 桌面窗口层次结构中的特定位置?

iis - 远程重置 IIS