简体   繁体   中英

C signal parent process from child

I'm trying to solve a problem I've got where a child process runs execvp() and needs to let the parent know if it returns. So, after the execvp() returns (because there's been an error), how can I tell the parent that this particular event has happened so it can handle it.

There's one method of writing a string of text through the pipe I'm using and then reading that from the parent.. but it seems a bit sloppy. Is there a better way?

Thanks!

Edit: Here is some code I'm trying where I can't seem to get the read to return.

int errorPipe[2];
signal( SIGPIPE, SIG_IGN );

int oldflags = fcntl (errorPipe[0], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[0], F_SETFD, oldflags);
oldflags = fcntl (errorPipe[1], F_GETFD, 0);
oldflags |= FD_CLOEXEC;
fcntl (errorPipe[1], F_SETFD, oldflags);

pipe( errorPipe );

// in the child..
char *error_message = "exec failed";
write( errorPipe[1], error_message, strlen(error_message)+1 );
exit(-1);

// in the parent
printf("read gives: %d\n", read(errorPipe[0], error_read, MAX_LINE_LENGTH) );

The easiest way is a pipe with the FD_CLOEXEC flag set, as then you can detect a successful exec as easily as a failure. In the event of a failure, I'd write whole the error message back to the parent over the pipe, but you could just write the status code or anything else that is meaningful. (Definitely write something though; nothing written has got to be a sign of a successful start of the other executable.)

[EDIT]: How to make use of this:

If the parent needs to wait until it knows whether the child successfully ran execve() (the unlying syscall) then it should do a blocking read() on the pipe. A zero result from that indicates success. (Make sure you've got SIGPIPE ignored.)

If the parent has some kind of event handling framework based on non-blocking IO and select() (or poll() or kqueue() or …) then wait for the pipe to become readable before trying to read the message (which will be zero-length if the child did the execve() correctly).

execvp () never returns, except when it fails to even start the executable at all. If it can start the executable, it will not return, no matter what the executable does (ie regardless to whether the executable succeeds at its task or not).

Your parent process will receive a SIGCHLD signal, for which you can install a signal handler.

Or you can wait (2) for the child process.

int child_pid = fork();
if (child_pid == 0) {
    execvp("/path/to/executable", ...);
    exit(123); /* this happens only if execvp() fails to invoke executable */
}

/* ... */

int status = 0;
int exit_pid = waitpid(-1, &status, WNOHANG);
if (exit_pid == child_pid && WIFEXITED(status)) {
    if (WEXITSTATUS(status) == 0) {
        /* child process exited fine */
    } else if (WEXITSTATUS(status) == 123)
        /* execvp() itself failed */
    } else {
        /* executed child process failed */
    }
}

Cache the pid for the (child) process for which you want to examine the status in the parent. Add a handler for SIGCHLD in the parent. In the child call exit with some status value of your choosing to denote that execvp failed. On receiving the signal in the parent you now have 2 options
a) Call waitpid with a pid of -1 (ie wait for any child), examine the return value, if that matches your cached pid, examine the status using macros like WEXITSTATUS.
b) Call waitpid with your cached pid , then on return examine the exit status.

To make this robust you should call WIFEXITED(status) before examining the exit status via WEXITSTATUS. WIFEXITED returns true if the child terminated normally ie by calling exit or _exit and not as a result of seg fault, un handled signal etc. Also see man wait(2).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM