如何正确分叉并完成进程以避免 EAGAIN 错误

问题描述 投票:0回答:2

我需要分叉许多生命周期较短的进程。 (但我不想等到它完成。就让它运行吧。)。一段时间后,我在执行 fork 时收到错误(errno == 11)。看起来分叉的进程仍然存在(至少它们在 htop 中可见)。

例如,请考虑以下示例。我想分叉一个进程,然后不久后退出它。

#include <cstdio>
#include <cstdlib>
#include <unistd.h>
#include <errno.h>
#include <signal.h>

void RunProc(){   
    pid_t pid = fork();
    if(pid == 0)
    {
        puts("fork with success");
        //no needed to reproduce issue. Used in my code to run some commands.
        //execvp()             
        exit(0);
    }
    else if (pid == -1) {
        printf("Could not start process\n");
        printf("Errno: %d\n", errno);
    }
    else {
    }
}
 
int main()
{
    while(true){
        RunProc();
    }

    int res = 0;
    int i = 5;
    res = scanf("%d", &i);
    res = printf("End of program %d\n", i);
    return res;
}

一开始它运行正确。几分钟后我只收到错误:

无法启动进程 错误号:11

我无法再开始新的分叉了。 我在 Ubuntu 22.04 上运行它

c++ linux fork
2个回答
3
投票

您需要在父进程中调用

wait
函数之一,以便在子进程退出时获取子进程。

您可以使用例如

waitpid
使用
WNOHANG
标志来轮询子进程退出。或者使用
SIGCHLD
信号来了解子进程何时退出。

您的系统中可用的进程槽数量有限,如果您不获取子进程,它们将填满所有槽并导致您收到错误。

正如

fork
手册页所说的
EAGAIN
错误:

遇到系统对线程数施加的限制。


0
投票

当进程终止时,其PID和退出状态仍然作为进程表中的记录保留。这就是所谓的僵尸进程。您可以通过使用

wait()
函数系列之一读取其状态来删除它,如兄弟答案中所述。

如果您不需要关心子进程的状态,您可以允许

init
读取它们的状态并清理表。当父进程终止时,
init
(PID 为 1 的进程)默认成为其所有子进程的父进程。因此,您可以使用双叉使
init
成为进程的父进程:

void RunProc(void)
{
    pid_t pid = fork();

    if (pid == -1) {
        perror("fork()");
        return;
    }

    if (pid == 0)
    {
        // first level child
        pid_t pid = fork();

        if (pid == -1) {
            perror("fork()");
            _exit(1);
        }
        if (pid != 0) {
            // first level child just terminates
            _exit(0);
        }

        // second level child
        puts("fork with success");

        _exit(0);
    }

    // wait for first level child
    if (waitpid(pid, NULL, 0) == -1) {
        perror("wait()");
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.