如何创建一个CreateProcess使得当父进程被杀死时子进程也随之终止?

21

有没有一种方法可以调用CreateProcess,使得杀死父进程会自动杀死子进程?

也许可以使用创建进程标志

编辑
解决方案是创建作业对象,将父进程和子进程都放入作业对象中。当父进程被杀死时,子进程也会被杀死。我从这里获得了代码: 在父进程被杀死时杀死子进程 请注意@wilx的评论中继承句柄的问题。

6个回答

13

Neil 在 这里 推荐使用作业(Job)的方式。通过在作业(Job)对象上使用 SetInformationJobObject() 方法,将 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 设置为 true,即可使子进程随父进程关闭而结束。当父进程退出或结束时,作业对象句柄会关闭。要实现该功能,必须确保子进程未继承作业对象句柄。如果您还想跟踪孙子进程,则需要创建挂起的子进程、将其添加到作业对象中,然后再让它们运行。


幸运的是,我正在使用的库创建子进程时不会继承作业句柄,所以这很好。 - Suraj

6

最好的方法是将这两个进程放在同一个任务中,这样杀死任务也会杀死这两个进程。


@Neil - 在它们启动之后,我可以把它们放在同一个工作中吗? - Suraj
@Jerry/Neil - 我尝试过了。我确认我得到了一个作业句柄,也确保将进程分配给作业成功了,但是杀死一个进程并不能杀死另一个进程。也许是我做错了什么。 - Suraj
@SFun28:在这种情况下,杀死父进程并不会自动杀死子进程。父进程将必须在退出时调用TerminateJobObject来杀死作业对象中的其他进程。 - Jerry Coffin
@Jerry:如果父进程被杀死(根据问题),它就无法调用任何东西。然而,操作系统或驱动程序在父进程死亡时可以运行代码。 - Ben Voigt
@Ben:是的,这就是为什么我建议使用调试而不是这种方法。如果父进程正常退出,它可以终止作业对象(从而终止子进程)。如果您确实只需要在父进程正常退出时杀死子进程,那么这种方法将起作用。您还可以运行第二个子进程,等待父进程的进程ID,并在收到父进程的进程ID信号时调用TerminateJobObject。但我认为调试器方法更加简洁。 - Jerry Coffin
显示剩余3条评论

4
您需要子进程被“终止”,还是只需要检测父进程的退出以便可以干净地终止?父进程可以创建一个可继承的句柄,然后子进程可以将其与自己的对象一起传递给WaitForMultipleObjects(Ex)
如果子进程没有专门为此编写,您可以将其stdin连接到管道中,而管道的另一端由父进程持有。如果父进程死亡,则管道会自动关闭。
这与Unix行为非常相似,在其中子进程在其父进程死亡时不会被杀死,它通常会响应SIGHUP(但它可以处理该信号并实现任何行为)。在Linux上,孤儿进程的父PID会更改为1(init)。

@Ben - 很好的问题!我需要杀死子进程。我的意思是,我不拥有子进程,因此无法以任何方式定制子进程。 - Suraj
好主意,不过可能有点超出我的技能水平。最终我会通过.NET PInvoke来完成所有的Win32 API操作。 - Suraj
@SFun28:.NET 使得重定向标准输入变得非常简单,只需使用 System.Diagnostics.Process 类,并在调用 Process.Start 时使用 “RedirectStandardInput” 选项 - Ben Voigt
@Ben - 我想这样做,但我没有启动该进程=)我可以通过pid获取Process对象,但我的理解是我将无法访问标准输入(我知道对于标准输出/错误,我不能)。 - Suraj
@SFun28:如果你的程序没有启动子进程,我不确定你所说的“父进程”和“子进程”是什么意思。既然你在询问CreateProcess,我相信你正在启动子进程。 - Ben Voigt
@Ben - 抱歉...就称其为进程1和进程2吧。实际上,进程1确实会创建进程2,但这是在第三方库中完成的,而不是直接进行,因此我无法访问进程对象。 - Suraj

2

一种不同的方法

并非在所有情况下都有用,但我有一个特定的场景,其中一个应用程序完全控制了一个子进程。为了进行通信和API拦截,需要进行DLL注入,以便在实际子进程中运行DLL并向父进程报告。

注意:现在,这并不是为了创意奖而做的。背景是:一个需要被包装的应用程序,但那是一个遗留应用程序,既不能修改,也不能以令人满意的方式重写。我们需要保持这个系统运行,同时仍然拦截它的输出。

此外,子进程编写得很差,会立即再次启动自己,然后终止,因此父进程实际上不是我们的主要应用程序。

如果您控制子进程并且已经注入了DLL,则可以通过监视父进程来扩展此DLL,以检查其是否正在运行。如果没有运行,则使用ExitProcess

如果您不需要在子进程中使用DLL,则其他解决方案很可能更好。


您可以通过例如注册表键传递parentProcessID,该键已由您的应用程序使用。或者,如果这不会破坏子进程,则可以传递命令行。

这必须在自己的线程中运行。我的DLL有两个线程:这里的代码和控制和通信代码。

DLL

bool IsProcessRunning(HANDLE hProcess)
{
    DWORD exitCode;
    GetExitCodeProcess(hProcess, &exitCode);
    return exitCode == STILL_ACTIVE;
}

bool WINAPI DllMain(HINSTANCE hInstDll, DWORD fdwReason, LPVOID lpvReserved)
{
    if (fdwReason == DLL_PROCESS_ATTACH)
    {
        int parentProcessID = [...]
        
        HANDLE parentProcessHandle = OpenProcess(PROCESS_ALL_ACCESS, TRUE, parentProcessID);
        while (IsProcessRunning(parentHandle)) Sleep(100);
        ExitProcess(0);
    }

    return true;
}

1

我想 DEBUG_PROCESS 或者 DEBUG_ONLY_THIS_PROCESS 可以作为一种几乎意外的副作用来实现这个功能。不过,Windows 并不像类 Unix 系统那样按照树形结构创建进程。


我尝试了这个方法,它似乎暂停了子进程。以前我在同一个控制台窗口中看到了子进程的标准输出,现在什么也没有显示。也许我需要结合一些其他的标志吗? - Suraj
@SFun28:这可能不是另一个标志的问题,而是在父进程中放置一个小循环,调用WaitForDebugEventContinueDebugEvent,以便子进程可以运行直到您决定终止它。 - Jerry Coffin
这变得有些混乱了...似乎从.net调用调试API的最佳方法是使用这个:http://blogs.msdn.com/b/jmstall/archive/2006/11/22/mdbg-sample-2-1.aspx - Suraj
@SFun28:是的,从.NET来看,这可能会很麻烦。 - Jerry Coffin
感谢您的建议。最终我放弃了这种方法,转而采用了 Job 方法,现在已经运行良好。感谢您的帮助。 - Suraj

0
不清楚Windows,但这在Linux上可以工作: prctl(PR_SET_PDEATHSIG,SIGHUP);

请注意:这应该在子进程中调用。 - domen

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接