为什么我的 waitpid() 在创建后台进程后不等待 child

Why does my waitpid() not wait for child after making a background process

我写了这个简单的程序,它无休止地要求用户输入,然后用 fork() 创建另一个进程来执行“ls -la”。

当用户输入为 0 时,该进程被创建为后台进程。如果用户输入的是其他内容,parent 进程将等待 child 进程终止。

在每次迭代中,僵尸进程都会被终止。这似乎工作得很好

这段代码的问题是:创建后台进程时,任何后续进程的parent进程不再等待。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <errno.h>


void delete_zombies(void)
{
    pid_t kidpid;
    int status;

    while ((kidpid = waitpid(-1, &status, WNOHANG)) > 0)
    {
        printf("Child %ld terminated\n", kidpid);
    }
}

int main() {

    int runBGflag =0;

    while(1)
    {
        scanf("%d",&runBGflag); //get user input
        if (runBGflag==0) printf("making a child process and wait for it to finish\n");
        else printf("making a background process\n");
        pid_t pid;   //fork
        pid = fork();




        if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
        {
            int status;
            if (waitpid(-1, &status, 0) == pid && WIFEXITED(status))
            {
                printf("child dead. parent continues\n");
            }
        }

        if (pid == 0)  //Child process. Executes commands and prints error if something unexpected happened
        {
            execlp ("ls", "-la", NULL);
            printf ("exec: %s\n", strerror(errno));
            exit(1);
        }

        delete_zombies();
    }

}

我期待输出

我希望在输入 010

后得到以下输出
0
making a child process and wait for it to finish
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp
child dead. parent continues
1
making a background process
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp
0
making a child process and wait for it to finish
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp
child dead. parent continues

然而我只得到

0
making a child process and wait for it to finish
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp
child dead. parent continues
1
making a background process
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp
0
making a child process and wait for it to finish
CMakeCache.txt  cmake_install.cmake  sigchild      Testing
CMakeFiles  Makefile         sigchild.cbp

您正在等待这部分的任何个子进程:

if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
    int status;
    if (waitpid(-1, &status, 0) == pid && WIFEXITED(status))
    {
        printf("child dead. parent continues\n");
    }
}

因此,此处可能更正的是“后台进程”,而不是新进程。 “后台进程”应该与新进程具有不同的 pid,因此可能不会打印消息“child dead...”。

这可以通过插入这样的调试打印语句来验证:

if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
    int status;
    pid_t ret = waitpid(-1, &status, 0);
    printf("debug: expected %d, actual %d\n", (int)pid, (int)ret);
    if (ret == pid && WIFEXITED(status))
    {
        printf("child dead. parent continues\n");
    }
}

为避免这种情况,您应该通过 waitpid() 的第一个参数指定要等待的进程,如下所示:

if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
{
    int status;
    if (waitpid(pid, &status, 0) == pid && WIFEXITED(status))
    {
        printf("child dead. parent continues\n");
    }
}

如果您 运行 child 打算等待它,您实际上并没有等待那个特定的 child,而是 任何child。如果有之前发布的和unwaited-for child那么可以代为收集。

你想要更像这样的东西:

        if (pid > 0 && runBGflag==0) //Parent process. Waits for child termination and prints exit status
        {
            int status;
            if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) // CHANGED
            {
                printf("child dead. parent continues\n");
            }
        }

另请注意,WIFEXITED(status) 测试报告的进程是否正常 终止。这不包括那些被信号杀死的,但它们仍然是死的。它也不包括那些已经停止但没有终止的——那些没有死,但你的程序无论如何都会停止等待它们。