Search code examples
cforkchild-process

Why does a process create a zombie if execv fails, but not if execv is successful and terminates?


So I am confused by the behavior of my C program. I am using the construct,

int pid = fork();
if (pid == 0) {
    if(file_upload_script_path) {
        rc = execv(file_upload_script_path, args);
        if(rc == -1) {   
            printf("Error has occured when starting file_upload.exp!\n");
            exit(0);
        }
    } else {   
        printf("Error with memory allocation!\n");
    }
}
else {
    printf("pid=%d\n", pid);
}

To fork the process and run a script for doing file upload. The script will by itself terminate safely, either by finishing the upload or failing.

Now, there was a problem with the script path, causing execv to fail. Here I noted the child process will terminate successfully if execv finishes, but in case it fails (r==-1) and I exit the process, it will become a zombie. Anyone knows why this happens?

Note here, I know why the child-process becomes a zombie. What I am confused about is why the process not becomes a zombie if execv works.

EDIT:

I got a question about errno and the cause of the error. The cause of the error is known. There were a problem with the build process, so the path of the script were another than expected.

However, this may happen again and I want to make sure my program does not start spawning zombies when it does. The behavoir where zombies are created in some situations and not others are very confusing.

BR Patrik


Solution

  • If you don't want to create zombies, your program has to reap zombie processes no matter if they call execv or not call it or no matter if the execv call succeeds. To reap zombie processes "automagically" handle SIGCHLD signal:

    void handle_sigchld(int sig) {
        int saved_errno = errno;
        while (waitpid((pid_t)(-1), 0, WNOHANG) > 0) {}
        errno = saved_errno;
    }
    
    int main() {
        signal(SIGCHLD, handle_sigchld);
        // rest of your program....
    }
    

    Inspired (no... ripped off) from: this link.

    Or maybe you want only to reap only this specified child, because later you want to call fork() and handle childs return value. Then pass the returned pid from fork() in your parent to the signal handler and wait on this pid in sigchld if needed (with some checking, ex. if the pid already finished then ignore future SIGCHLD etc...).