如果我的主进程崩溃或关闭,我希望子进程能够自动关闭。
由于这需要在“父”进程崩溃时工作,我相信这需要使用操作系统的某些API/功能来完成,以便清理所有“子”进程。
我该怎么做?
Windows API支持名为“Job Objects”的对象。以下代码将创建一个“作业”,配置为在主应用程序结束时(当其句柄被清理时)关闭所有进程。此代码仅应运行一次。
HANDLE ghJob = CreateJobObject( NULL, NULL); // GLOBAL
if( ghJob == NULL)
{
::MessageBox( 0, "Could not create job object", "TEST", MB_OK);
}
else
{
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli = { 0 };
// Configure all child processes associated with the job to terminate when the
jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
if( 0 == SetInformationJobObject( ghJob, JobObjectExtendedLimitInformation, &jeli, sizeof(jeli)))
{
::MessageBox( 0, "Could not SetInformationJobObject", "TEST", MB_OK);
}
}
当每个子进程被创建时,执行以下代码来启动每个子进程并将其添加到作业对象中:
STARTUPINFO info={sizeof(info)};
PROCESS_INFORMATION processInfo;
// Launch child process - example is notepad.exe
if (::CreateProcess( NULL, "notepad.exe", NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo))
{
::MessageBox( 0, "CreateProcess succeeded.", "TEST", MB_OK);
if(ghJob)
{
if(0 == AssignProcessToJobObject( ghJob, processInfo.hProcess))
{
::MessageBox( 0, "Could not AssignProcessToObject", "TEST", MB_OK);
}
}
// Can we free handles now? Not sure about this.
//CloseHandle(processInfo.hProcess);
CloseHandle(processInfo.hThread);
}
VISTA注意事项:如果在VISTA上使用AssignProcessToObject()时遇到访问被拒绝的问题,请参见AssignProcessToObject在VISTA上始终返回“访问被拒绝”。
一种有点粗糙的解决方案是,父进程作为调试器附加到每个子进程上(使用DebugActiveProcess)。当调试器终止时,所有调试目标进程也会终止。
更好的解决方案(假设您也编写了子进程)是让子进程监视父进程,如果父进程消失,则退出。
Windows Job Objects听起来是一个不错的开始。Job Object的名称必须是众所周知的,或者传递给子进程(或继承句柄)。子进程需要注意到父进程死亡,可以通过失败的IPC“心跳”或仅仅是在父进程句柄上等待(WFMO/WFSO)来实现。此时,任何子进程都可以使用TerminateJobObject来关闭整个组。
在创建进程之前,您可以将作业分配给父进程:
static HANDLE hjob_kill_on_job_close=INVALID_HANDLE_VALUE;
void init(){
hjob_kill_on_job_close = CreateJobObject(NULL, NULL);
if (hjob_kill_on_job_close){
JOBOBJECT_EXTENDED_LIMIT_INFORMATION jobli = { 0 };
jobli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(hjob_kill_on_job_close,
JobObjectExtendedLimitInformation,
&jobli, sizeof(jobli));
AssignProcessToJobObject(hjob_kill_on_job_close, GetCurrentProcess());
}
}
void deinit(){
if (hjob_kill_on_job_close) {
CloseHandle(hjob_kill_on_job_close);
}
}
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
会在最后一个与作业关联的句柄关闭时终止与该作业相关的所有进程。默认情况下,除非在调用 CreateProcess
时传递了 CREATE_BREAKAWAY_FROM_JOB
,否则所有子进程都将自动分配给该作业。有关 CREATE_BREAKAWAY_FROM_JOB
的更多信息,请参见 https://learn.microsoft.com/en-us/windows/win32/procthread/process-creation-flags。
你可以将每个进程封装在一个C++对象中,并在全局范围内保留它们的列表。析构函数可以关闭每个进程。如果程序正常退出,那么这将很好地工作,但如果程序崩溃,则一切都无法预料。
以下是一个简单的示例:
class myprocess
{
public:
myprocess(HANDLE hProcess)
: _hProcess(hProcess)
{ }
~myprocess()
{
TerminateProcess(_hProcess, 0);
}
private:
HANDLE _hProcess;
};
std::list<myprocess> allprocesses;
然后每当你启动一个进程时,调用allprocessess.push_back(hProcess);