I am writing a monitoring program in C, which performs the fork() and exec() cycle. However I need to check whether the child process has terminated or not without blocking the main process, i.e. the monitoring program. Something like this:
Main process
pid_t child_pid = fork();
if (child_pid == 0)
exec(bar);
while (1) {
if (the child process has finished)
foo();
else
bar();
}
Considering the fact I have the child pid I have tried the following:
Sending a kill call with signal 0 and examining errno:
if (kill(child_pid, 0) == -1 || errno == ESRCH), which I think is not a good way to track the status of the child process, given that it is not safe from race conditions. Moreover it did not work or at least it seemed so.
Inspecting with stat(2) whether proc/child_pid exists. All of the above negative arguments are true in this case as well plus that this method is slower.
waitpid(2). Unfortunately it blocks the main process.
Is there any other way to obtain this kind of information? Or perhaps I am missing something from the solutions I have already tried?
If you pass WNOHANG to waitpid it should not block.
if(waitpid(child_pid, &status, WNOHANG) != 0) {
// child process has finished
foo();
} else {
// child process still running
bar();
}
When a process is terminating, you can set up the parent process to get and handle a SIGCHLD signal, see signal(7); the signal handler can only call async-signal-safe functions, or set a volatile sigatomic_t flag tested outside of the handler (e.g. in your main event loop around poll(2)...), or write(2) to some file descriptor (e.g. a pipe or some eventfd(2)). As Bruce Ediger answered you can also use the Linux-specific signalfd.
Then you can use some waiting function, e.g. waitpid(2) (perhaps with WNOHANG if you don't want to block) or wait4(2) to wait the process and get its status, etc.
Read also Advanced Linux Programming for more. It has several chapters on these questions.
If you want your process to not block, or maybe you've got other file descriptors to check up on, you should consider the signalfd() system call if you're writing for Linux.
This system call gives back a special file descriptor that you can use in select(), poll() and epoll() system calls. Set up correctly, a child process exiting causes the kernel to make the special file descriptor readabl. Reading from the special file descriptor gives you a filled-in struct with info about the child process' exit status.