Fluctuation10111
Fluctuation10111

Reputation: 11

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

I wrote this simple program which endlessly asks the user for an input and then creates another process with fork() to execute "ls -la".

When the user input was 0, the process is created as a background process. If the user input was anything else, the parent process waits for the child process to terminate.

In each iteration, zombie processes are terminated. This seems to work just fine

The problem with this code is: When a background process is created, the parent process for any subsequent processes does not wait any more.

#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();
    }

}

I expect the output

E.G. I would expect following output after input 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

However i only get

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

Upvotes: 0

Views: 643

Answers (2)

John Bollinger
John Bollinger

Reputation: 180093

In the event that you run the child with the intention of waiting for it, you do not actually wait for that specific child, but rather for any child. If there is a previously launched and unwaited-for child then that can be collected instead.

You want something more like this:

        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");
            }
        }

Note also that WIFEXITED(status) tests whether the process being reported on terminated normally. That does not include those that are killed by signals, which are nevertheless just as dead. It also does not include those that have been stopped but not terminated -- those are not dead, but your program stops waiting for them anyway.

Upvotes: 1

MikeCAT
MikeCAT

Reputation: 75062

You are waiting for any child processes in this part:

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");
    }
}

Therefore, the "background process" may corrected here instead of the new process. The "background process" should have different pid from the new process, so the message "child dead..." may not be printed.

This can be verified by inserting a debug-printing statement like this:

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");
    }
}

To avoid this, you should specify the process to wait via the first argument of waitpid() like this:

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");
    }
}

Upvotes: 1

Related Questions