Windows下发送Ctrl+C信号优雅地中断进程
很多控制台应用程序,在执行的过程中,都可以通过按下ctrl+c组合键来结束进程。你可能会想,结束进程的方法有很多呀,比如很常用的 TerminateProcess()函数。但是,这些方法太过暴力,可能会导致不希望的结果。比如正在使用ffmpeg录音,而ffmpeg先把录制到 的内容存在高速缓存中(内存),每当塞满1M就写入磁盘。如果突然暴力终止进程,那么可能导致在内存中还没来得及写入磁盘的音频数据丢失。这个肯定不是我 们希望的事情。
其实,ffmpeg是可以优雅地结束的,就是用户按下ctrl+c键,此时ffmpeg会停止录制,并且把剩余的音频数据写入磁盘,然后退出。这个 机制就是Linux下很常用很著名的信号(signal)机制。在Linux下,可以通过signal()函数注册一个信号处理器,然后可以通过 raise()产生一个针对本进程的信号,或者通过kill()函数对别的进程发送信号。
我曾经一直以为信号机制是Linux特有的。后来在Windows上用了ffmpeg,发现依旧可以使用ctrl+c,于是就引起了我的好奇——难道Windows上也有信号机制?
百度之,在万能的“栈爆网(stackoverflow)”还真找到了类似的提问,发现一个叫做 GenerateConsoleCtrlEvent()的函数。然后顺藤摸瓜,并且附加上自己的试验,还真的成功了。 GenerateConsoleCtrlEvent()这个函数必须有其他三个函数配置使用:
+++code
AttachConsole(processId); //附加到目标进程的console
SetConsoleCtrlHandler(NULL,TRUE); //添加自己的ctrl+c处理,防止自己被终止
GenerateConsoleCtrlEvent(CTRL_C_EVENT,0); //发送ctrl+c(注意:这是向所有共享该console的进程发送)
FreeConsole(); //脱离目标console
---code
上具体的程序,可以看得更加清楚些。
在 R:\新建一个son.c,贴入如下代码:
+++code
#include <stdio.h>
#include <signal.h>
#include <windows.h>

void on_sigint(int p_sig)
{
    printf("son receives ctrl+cn");
}

int main()
{
    signal(SIGINT,on_sigint);
    Sleep(5000);
    return 0;
}
---code
编译链接成 R:\son.exe。
在 R:\新建一个father.c,贴入如下代码:
+++code
#include <stdio.h>
#include <windows.h>

void send_sigint(DWORD p_pid)
{
    AttachConsole(p_pid);
    SetConsoleCtrlHandler(NULL,TRUE);
    GenerateConsoleCtrlEvent(CTRL_C_EVENT,0);
    FreeConsole();
}

int main()
{
    STARTUPINFO t_si;
    memset(&t_si,0,sizeof(STARTUPINFO));
    t_si.cb=sizeof(STARTUPINFO);
    t_si.dwFlags=STARTF_USESHOWWINDOW;
    t_si.wShowWindow=SW_SHOW;
    PROCESS_INFORMATION t_pi;
    CreateProcess(NULL,"R:\son.exe",NULL,NULL,FALSE,0,NULL,NULL,&t_si,&t_pi);
    printf("create son,pid:%dn",t_pi.dwProcessId);
    Sleep(1000);
    printf("father sends ctrl+cn");
    send_sigint(t_pi.dwProcessId);
    return 0;
}
---code
编译链接成 R:\father.exe。
运行R:\father.exe,结果截图如下:
1.jpg
这个说明,father.exe成功地向son.exe发送了SIGINT信号(也就是CTRL+C信号)。如果son.exe需要在收到CTRL+C后处理一些事情后再退出,可以将
+++code
void on_sigint(int p_sig)
{
    printf("son receives ctrl+cn");
}
---code
改为
+++code
void on_sigint(int p_sig)
{
    //do something
    exit(0);
}
---code