Search code examples
clinuxforkwaitpidos.execl

waitpid with execl used in child returns -1 with ECHILD?


When do I need to use waitpid if I am using execl in a child process which may take time to finish?

When I use waitpid in the parent, it tells me that the child is running as the return value from waitpid is 0. However, if I call waitpid after some time in another function, it returns -1 with errno set to ECHILD. When should I use waitpid if I am not sure whether the child has completed or not?

//pid_t Checksum_pid = fork();
Checksum_pid = fork();

if (Checksum_pid == 0)
{
    execl(path, name, argument as needed, ,NULL);
    exit(EXIT_SUCCESS);     
}
else if (Checksum_pid > 0)
{ 
    pid_t returnValue = waitpid(Checksum_pid, &childStatus, WNOHANG);

    if ( returnValue > 0)
    {
        if (WIFEXITED(childStatus))
        {
            printf("Exit Code: _ WEXITSTATUS(childStatus)") ;               
        }
    }
    else if ( returnValue == 0)
    {
        //Send positive response with routine status running (0x03)
        printf("Child process still running") ; 
    }
    else
    {
        if ( errno == ECHILD )
        {
             printf(" Error ECHILD!!") ;
        } 
        else if ( errno == EINTR )
        {
            // other real errors handled here.
            printf(" Error EINTR!!") ;
        }
        else
        {
            printf("Error EINVAL!!") ;
        }       
    }
}
else
{   
    /* Fork failed. */
    printf("Fork Failed") ;
}

Solution

  • If you wait with WNOHANG, then you won't get any useful status unless your child has already died — and it may not even have got going yet (it could still be waiting for the executable to be loaded from disk when the parent executes the waitpid()).

    If you want to know that the child has finished, don't use WNOHANG — use 0 for the third argument unless you want to know about untraced or continued processes. This will wait until the process identified by Checksum_pid exits, or the system loses track of it by some mysterious means (which I've never seen happen).


    This code produces Exit Code: 0 as the output:

    #include <errno.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/wait.h>
    #include <unistd.h>
    
    int main(void)
    {
        pid_t Checksum_pid = fork();
    
        if (Checksum_pid < 0)
            printf("Fork Failed\n");
        else if (Checksum_pid == 0)
        {
            execl("/bin/sleep", "/bin/sleep", "2", NULL);
            exit(EXIT_FAILURE);
        }
        else
        {
            int childStatus;
            pid_t returnValue = waitpid(Checksum_pid, &childStatus, 0);
    
            if (returnValue > 0)
            {
                if (WIFEXITED(childStatus))
                    printf("Exit Code: %d\n", WEXITSTATUS(childStatus));
                else
                    printf("Exit Status: 0x%.4X\n", childStatus);
            }
            else if (returnValue == 0)
                printf("Child process still running\n");
            else
            {
                if (errno == ECHILD)
                    printf(" Error ECHILD!!\n");
                else if (errno == EINTR)
                    printf(" Error EINTR!!\n");
                else
                    printf("Error EINVAL!!\n");
            }
        }
    
        return 0;
    }
    

    It is very similar to your code; I've merely moved the check for fork() failing to the top. I'd still prefer to get rid of the 'bushy trees' of if statements, but it isn't critical. What happens when you run this code? What do you have to change to get the ECHILD error (can you change it so that you get the ECHILD error)?

    When you've managed to get code based off this to reproduce the problem, we can work out why you get that behaviour.

    Tested on Mac OS X 10.9.2 Mavericks with GCC 4.8.2, and also Ubuntu 13.10 with GCC 4.8.1 (I needed to add -D_XOPEN_SOURCE=700 to get it to compile with my stringent compilation flags; Mac OS X managed without that), but I don't expect to get different results elsewhere.