【问题标题】:waitpid() and fork() to limit number of child processeswaitpid() 和 fork() 限制子进程的数量
【发布时间】:2012-09-25 21:46:02
【问题描述】:

我的程序应该将子进程的数量限制为 3。

使用下面的代码,waitpid 会停止我的父进程,因此我无法在第一个子进程之后创建更多子进程。如果我不使用waitpid,那么我不知道子进程何时退出以减少活动进程的数量。

int numProcs = 0;
while(1==1) {
    /* 
     * inserts code that waits for incoming input 
     */
    numProcs++;
    pid = fork();
    if (pid == 0) {
        doStuff(); // may exit anytime, based on user input
    } else {
        if (numProcs > 3) {
            wait(&status);
            numProcs--;
        } else {
            waitpid(pid, &status, 0); // PROBLEM!
            numProcs--;
        }
    }
}

我整天都在寻找这个问题。有人可以帮忙吗?

【问题讨论】:

    标签: c++ c concurrency


    【解决方案1】:

    冒着明显的风险,您基本上只想删除 else 子句。您正在寻找的逻辑类似于:

    int max_active = 3; // or whatever
    int number_active = 0;
    bool done = false;
    
    for (; !done; ++number_active) {
      // wait for something to do;
      GetSomeWork();
      // wait for something to finish, if necessary.
      for (; number_active >= max_active; --number_active)
        wait(&status);
      pid = fork();
      if (pid < 0)
        ReportErrorAndDie();
      if (pid == 0)
        DoTheWorkAndExit();
    }
    

    这实际上让您可以在不重新启动的情况下更改 max_active 的值,这是围绕 wait() 调用的 for 循环的唯一理由。

    明显的抱怨是我的版本中的 number_active 实际上并没有告诉您有多少进程处于活动状态,这是真的。它告诉你有多少进程没有等待(),这意味着你可能会保留一些僵尸(但数量有限)。如果您始终以或接近最大任务数运行,这无关紧要,除非您的最大值很大,否则无论如何都没关系,因为唯一的设计要求是您使用的任务数不超过任务的最大数量,因此您只需要知道活动的数量不超过最大值。

    如果这真的让您感到困扰并且您想清理任务,您可以输入:

    for (; waitpid(-1, &status, WNOHANG) > 0; --number_active) {}
    

    在另一个 for 循环之前,它将在检查是否需要阻止之前收割僵尸。 (如果根本没有进程,我不记得 waitpid(-1, &status WNOHANG) 是否返回错误,但无论如何,如果出现错误,继续循环是没有意义的。)

    【讨论】:

    • 孩子终止时如何减少number_active?看这段代码,如果你fork()exit() 3次,number_active就变成了3,而且由于没有子进程可以终止,你就卡在wait(&amp;status)了?
    • 有一个子进程要收割,直到你收割它,即使它已经终止。这就是僵尸:一个已经终止但从未调用过 wait[pid] 的孩子。子进程描述符必须继续存在,直到父进程提取状态信息。僵尸没有到期日,所以无论你是在工作之前还是之后等待它们都无关紧要。
    • 这完全可行!非常感谢您的补充说明。我是一名网络开发人员,这些东西让我头晕目眩。你摇滚@rici!
    • 在任何重要的时间里,你不会有大量的僵尸,除非你有太多的孩子,以至于你最终换掉了父母。在任何情况下,父母都会像创建新孩子一样快地收获僵尸(通过调用等待);添加第二个循环不会明显加快速度。
    • @ChrisDodd,我完全同意,我在回答中说了类似的话。但是每次我使用这样的代码时,都会有人嘀咕一些关于僵尸的东西,所以我想我有点防御性了。我个人只在进程终止时使用 reap-loop,即使那样也只是出于可能错位的整洁感。
    【解决方案2】:

    您的代码有两个问题,但第二个问题被第一个掩盖了。

    您的直接问题是 waitpid(pid, &status, 0);将阻塞,直到具有指定 pid 的进程终止。您想将 WNOHANG 选项作为第三个参数添加到 waitpid() 调用中。这将确保调用不会阻塞。

    这将增加一个新问题:您必须自己检查是否有任何子进程已终止。你可以使用 WIFEXITED 宏来做到这一点:

    } else {
      waitpid (-1, &status, WNOHANG);
      if (WIFEXITED(status)) {
        numProcs--;
      }
    }
    

    第二个问题是您的原始代码只等待创建最新的 pid。您应该等待 -1,这是所有子进程。

    【讨论】:

    • 我刚试过这个。当孩子使用_exit(EXIT_SUCCESS) 正常退出时,numProcs 并没有减少。我认为问题是当我们调用waipid WNOHANG时,孩子并没有退出,它稍后在没有检查时退出。
    • 这不是问题。问题是检查 WIFEXITED 不是使用 waitpid NOHANG 的正确方法;你应该检查waitpid返回的pid_t。我的方法更好:)
    • @hellocrowley,这是正确的。只有在退出后调用 waitpid() 时才会收割僵尸。此外,如果有多个子进程退出,您只会获得其中一个进程,因此应该真正调用 waitpid,直到 WIFEXITED 返回 false。
    • @rici 检查返回值对您没有任何帮助,因为在进程终止之外的其他情况下,waitpid 会给出一个 pid。
    猜你喜欢
    • 2012-08-29
    • 2016-03-19
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多