【问题标题】:How do I automatically destroy child processes in Windows?如何在 Windows 中自动销毁子进程?
【发布时间】:2010-09-08 08:49:29
【问题描述】:

在 C++ Windows 应用程序中,我启动了几个长时间运行的子进程(目前我使用 CreateProcess(...) 来执行此操作。

我希望子进程自动关闭如果我的主进程崩溃或关闭。

由于要求这需要在“父”崩溃时起作用,我相信这需要使用操作系统的某些 API/功能来完成。这样所有的“子”进程都被清理干净了。

我该怎么做?

【问题讨论】:

    标签: windows process


    【解决方案1】:

    您可能必须保留您启动的进程的列表,并在退出程序时将它们一一终止。我不确定在 C++ 中执行此操作的具体细节,但应该不难。困难的部分可能是确保在应用程序崩溃的情况下关闭子进程。 .Net 能够添加一个函数,当发生未处理的异常时调用该函数。我不确定 C++ 是否提供相同的功能。

    【讨论】:

    • >> 如果我的主进程崩溃了
    【解决方案2】:

    Windows API 支持称为“作业对象”的对象。以下代码将创建一个“作业”,该“作业”被配置为在主应用程序结束时(当其句柄被清理时)关闭所有进程。这段代码应该只运行一次。:

    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() 遇到访问被拒绝问题,请参阅 AssignProcessToJobObject always return "access denied" on Vista

    【讨论】:

    • 在评论中回答您的问题:是的,当您不再需要手柄时,您应该 CloseHandle。
    • 不,我不这么认为。 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE 导致作业在最后一个 job 句柄关闭时终止,进程句柄不应该有所作为。你试过了吗?
    • 它在Windows 2000 上不起作用:((但它应该在其他版本的windows 下)
    • 这里有一个理论上的竞争条件,如果新进程足够快地退出。要消除它,请使用 CREATE_SUSPENDED 标志并仅在将进程添加到作业后调用 ResumeThread。
    • 我在一个可执行文件中使用了上述的 JobObjects,它调用了 MsBuild 和其他东西。我可以随时杀死 exe,一切都会立即清理干净。极好的。 :)
    【解决方案3】:

    您可以保持一个单独的看门狗进程运行。它的唯一任务是观察当前进程空间以发现您描述的情况。它甚至可以在崩溃后重新启动原始应用程序,或者为用户提供不同的选项、收集调试信息等。只要尽量保持简单,这样你就不需要第二个看门狗来监视第一个。

    【讨论】:

      【解决方案4】:

      一个有点老套的解决方案是让父进程作为调试器附加到每个子进程(使用DebugActiveProcess)。当调试器终止时,它的所有被调试进程也会终止。

      更好的解决方案(假设您也编写了子进程)是让子进程监视父进程并在它消失时退出。

      【讨论】:

      • 您可以将 DEBUG_PROCESS 作为您的创建标志之一传递给 CreateProcess,而不必调用 DebugActiveProcess。这样代码更少。
      【解决方案5】:

      您可以将每个进程封装在一个 C++ 对象中,并将它们的列表保存在全局范围内。析构函数可以关闭每个进程。如果程序正常退出但它崩溃了,那一切正常。

      这是一个粗略的例子:

      class myprocess
      {
      public:
          myprocess(HANDLE hProcess)
              : _hProcess(hProcess)
          { }
      
          ~myprocess()
          {
              TerminateProcess(_hProcess, 0);
          }
      
      private:
          HANDLE _hProcess;
      };
      
      std::list<myprocess> allprocesses;
      

      然后每当你启动一个,调用 allprocessess.push_back(hProcess);

      【讨论】:

      • >> 如果我的主进程崩溃了
      【解决方案6】:

      Windows 作业对象听起来是个不错的起点。作业对象的名称必须是众所周知的,或者传递给子对象(或继承句柄)。当父母死亡时,孩子需要得到通知,无论是通过失败的 IPC“心跳”还是只是父母进程句柄上的 WFMO/WFSO。此时任何子进程都可以使用 TermianteJobObject 来破坏整个组。

      【讨论】:

      • 相反,子进程只能在父句柄上使用 WFMO/WFSO 并自行关闭。如果每个子进程都这样做,则不需要作业对象。
      【解决方案7】:

      您可以在创建进程之前将作业分配给父进程:

      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://docs.microsoft.com/en-us/windows/win32/procthread/process-creation-flags

      您可以使用 Sysinternals 的流程资源管理器来确保将所有流程分配给作业。像这样:

      【讨论】:

      • 请注意,此方法只能在Windows 8/10中使用,因为一个进程只能关联一个作业。作业不能嵌套。在 Windows 8 和 Windows Server 2012 中添加了嵌套作业的功能。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-10-09
      • 1970-01-01
      • 2020-05-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多