Is waitpid() an atomic operation in Linux? - c

For example, in the parent process, I forked a child process and wait on the child process:
int main() {
setSignal(SIGCHLD, sigchld_handler)
while(1) {
// fork some child processes
myForkFunction()
waitpid(-1, &status, 0)
}
}
Moreover, I have a SIGCHLD signal handler:
void
sigchld_handler(int sig) {
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
// Reap zombie processes
}
}
As can be seen, waitpid() appears both in the main() function and in the sigchld_handler() function. I was wondering whether waitpid can be interrupted by SIGCHLD. If it can be interrupted by SIGCHLD, what will happen then?
Does anyone have any ideas about this?

The POSIX specification for waitpid() says in part:
If _POSIX_REALTIME_SIGNALS is defined, and the implementation queues the SIGCHLD signal, then if wait() or waitpid() returns because the status of a child process is available, any pending SIGCHLD signal associated with the process ID of the child process shall be discarded. Any other pending SIGCHLD signals shall remain pending.
Otherwise, if SIGCHLD is blocked, if wait() or waitpid() return because the status of a child process is available, any pending SIGCHLD signal shall be cleared unless the status of another child process is available.
For all other conditions, it is unspecified whether child status will be available when a SIGCHLD signal is delivered.
The third of the quoted paragraphs seems to imply that you're treading on thin ice. It doesn't mention 'implementation defined' or similar — unspecified means that the standard says nothing about what shall happen and you may or may not get any information from the implementation-specific documentation.
There is a lot of (very densely worded) information in the POSIX specification. There are also some examples, and a rationale — which mentions sigwait() and sigwaitinfo(). It is worth reading the whole of the waipid() page. You should probably also read about Signal concepts too — more dense reading. (One of these days, I'll do it, too — when I need to know about bits of signals that I haven't covered before.)
Why are you using WUNTRACED instead of 0 or WNOHANG? WUNTRACED is a very specialized condition — POSIX says:
WUNTRACED
The status of any child processes specified by pid that are stopped, and whose status has not yet been reported since they stopped, shall also be reported to the requesting process.
Similar comments apply to WCONTINUED. Those two flags are useful when you need them, but you very seldom need them.
I suggest you should normally use either 0 or WNOHANG in the third argument to waitpid().

Yes, in the sense that only one of them can succeed for a given child process; if the signal handler interrupts the one in main, then after the signal handler returns, the child will already have been reaped and the call in main will fail.
With that said, however, it's bad practice to write code like this. There should be a single place you handle reaping of a given child process, and usually a signal handler is a very bad choice because it's global and it would have to be aware of all possible child processes your program might have finishing, and have a way to communicate those results to the proper parts of your program.
Instead, it's generally better to monitor the termination of child processes via poll on a pipe to/from the child process, and only waitpid after you know it's terminated, or to perform blocking waitpid from a thread whose only job is to wait for the child.

Related

How is wait implemented in C?

When I create two child processes, I can't use SIGCHLD to tell whether both child processes have terminated since once a signal is delivered, future signals of the same type are discarded. When I receive a SIGCHLD signal and handle that signal, I cannot be sure whether that means both child processes have terminated and sent the SIGCHLD signal or just one of them has terminated. In other words, signals are not queued. However, with the function wait(), if I have two child processes, then I can call wait() twice to reap both child processes, and I wonder how it is implemented under the hood. It seems it is not using SIGCHLD as signals are not queued. So how is it able to handle terminated child processes one by one?
In cases like this, you'd instead call waitpid() with the WNOHANG flag in a loop until it returns 0 to indicate none of the specified processes have changed state (or -1 to indicate an error).
int status;
pid_t p;
while ((p = waitpid(-1, &status, WNOHANG)) > 0) {
// Inspect status and do whatever
}
As for how they work under the hood? The kernel keeps exited but not yet waited for processes in its list of processes (so called zombie processes). wait() just returns the status of one of the caller's exited children, or blocks until there is one (or an error), at which point the calling process is scheduled to run again.

Why to use non-blocking waitpid instead of blocking wait?

I am reading https://www.amazon.com/Unix-Network-Programming-Sockets-Networking/dp/0131411551, and there the author handle the sigchld in handler that calls waitpid rather then wait.
In Figure 5.7, we cannot call wait in a loop, because there is no way to prevent wait from blocking if there are
running children that have not yet terminated.
The handler is as follows:
void sig_chld(int signo)
{
pid_t pid;
int stat;
// while ((pid = wait(&stat)) > 0)
while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)
{
printf("child %d terminated\n", pid);
}
}
The question is, even if I use the blocking version of wait (as is commented out), the child are terminated anyway (which is what I want in order to not have zombies), so why to even bother whether it is in blocking way or non-blocking?
I assume when it is non-blocking way (i.e. with waitpid), then I can call the handler multiple times? (when some childs are terminated, and other are still running). But still I can just block and wait in that handler for all child to terminate. So no difference between calling the handler multiple times or just once. Or is there any other reason for non-blocking and calling the handler multiple times?
The while loop condition will run one more time than there are zombie child processes that need to be waited for. So if you use wait() instead of waitpid() with the WNOHANG flag, it'll potentially block forever if you have another still running child - as wait() only returns early with an ECHLD error if there are no child processes at all. A robust generic handler will use waitpid() to avoid that.
Picture a case where the parent process starts multiple children to do various things, and periodically sends them instructions about what to do. When the first one exits, using wait() in a loop in the SIGCHLD handler will cause it to block forever while the other child processes are hanging around waiting for more instructions that they'll never receive.
Or, say, an inetd server that listens for network connections and forks off a new process to handle each one. Some services finish quickly, some can run for hours or days. If it uses a signal handler to catch exiting children, it won't be able to do anything else until the long-lived one exits if you use wait() in a loop once that handler is triggered by a short-lived service process exiting.
It took me a second to understand what the problem was, so let me spell it out. As everyone else has pointed out, wait(2) may block. waitpid(2) will not block if the WNOHANG option is specified.
You are correct to say that the first call to wait(2) should not block since the the signal handler will only be called if a child exited. However, when the signal handler is called there may be several children that may have exited. Signals are delivered asynchronously, and can be consolidated. So, if two children exit at close to the same time, it's possible that the operating system only sends one signal to the parent process. Thus, a loop must be used to iteratively check if more than one child has exited.
Now that we've established that the loop is necessary for checking if multiple children have exited, it is clear that we can't use wait(2) since it will block on the second iteration if there is a child that has not exited.
TL;DR The loop is necessary, and hence using waitpid(2) is necessary.

Get signal when tid status change

There is a way to look when pid/tid status change with waitpid but this is blocking function.
I want to monitor all threads in specific pid and get signal when one of them change and print the tid.
For now I open threads as count of threads in that process and each 1 make waitpid on 1 tid and after that blocking function finish I print that tid that changed.
How can I get a signal that tid change so I can monitor all tid's in 1 thread.
I didn't want to monitor all pid in system only specific pid/tid.
Those tids/pids are not children of my process.
You can call
int status;
pid_t waitpid(-1, &status, 0);
to wait for any child process change.
So you do not have to specify in advance, which pid to monitor, and can react on any status change. This way you do not need to start one thread for each pid.
As to the signal part of your question: A SIGCHLD is sent to your process when a child process exits. This signal is ignored by default, but you can install a custom signal handler for it, of course.
If you only want to reap specific pids, linux provides the option WNOWAIT, which only reports the state, but does not really reap the child process. Now you can check, if the pid is one of those you want to monitor, and if so, call waitpid() again without the option.
If the processes are not children, waitpid() cannot be used in general. One option is, to attach with ptrace() to these 40 processes to get signalled, if one of these processes exit. This might have unwanted side-effects, however.
If you're using POSIX threads, then you could use pthread_cleanup_push and pthread_cleanup_pop to call a "cleanup" function when your thread is exiting.
This "cleanup" function could then send one of the user signals (SIGUSR1 or SIGUSR2) to the process which then catches it and treats it as a signal about thread termination.
If you use sigqueue you can add the thread-id for the signal handler so it knows which thread just exited.
You can use pthread_sigmask to block the user signal in all threads, to make sure it's only delivered to the main process thread (or use pthread_sigqueue to send to the main process thread specifically).

Which child process send SIGCHLD

I'm trying to understand the signal handling and process. I have a parent process that created several child processes. Now in the parent process I have a list of all child processes. when a child is terminated I want to delete it from the list. I know that when a child is terminated he's sending SIGCHLD to the parent. OK, now it's the tricky part, how can I find out if that child terminated or just suspended or something else?
As you said
when a child is terminated he's sending SIGCHLD to the parent
Make the parent call wait().
Either
by a blocking call to wait() or
on a continous base or
by seting up a signal handler serving SIGCHLD which in turn calls wait().
If having called waitpid() with a pid of -1 using the option WUNTRACED and then applying the macro WIFSTOPPED to the value returend by waitpid() it tells you whether the process was stopped or ended.
For Linux since kernel version 2.6.10 the same as for WIFSTOPPED applies to WCONTINUED.
There is a system call in signal.h - sigaction() , similar to siganl( ) but more useful.Visit http://man7.org/linux/man-pages/man2/sigaction.2.html
The signal handler function prototype for sigaction() looks like this:
void sa_handler(int signo, siginfo_t *si, void *ucontext);
It has an argument of type siginfo_t which contains all the information about the signal including the pid of the process which sent it.
Though using conventional signal handling mechanism, it can be done using waitid() as mentioned in previous answer(s) but waitid() needs pid as one of its argument.
Using the options argument of the wait() family of functions (waitpid(),waitid()).
http://linux.die.net/man/2/waitid

Using waitpid or sigaction?

I have understood that:
1) waitpid is used to wait for a child's death and then collect the SIGCHLD and the exit status of the child etc.
2) When we have a signal handler for SIGCHLD, we do some more things related to cleanup of child or other stuff (upto the programmer) and then do a waitpid so that the child will not go zombie and then return.
Now, do we need to have both 1 and 2 in our programs when we do a fork/exec and the child returns ?
If we have both, the SIGCHLD is obtained first, so the signal handler is called first and thus its waitpid is called successfully and not the waitpid in the parent process code as follows:
my_signal_handler_for_sigchld
{
do something
tmp = waitpid(-1,NULL,0);
print tmp (which is the correct value of the child pid)
}
int main ()
{
sigaction(SIGCHLD, my_signal_handler_for_sigchld)
fork()
if (child) //do something, return
if parent // waitpid(child_pid, NULL,0); print value returned from this waitpid - it is -1
}
Appreciate if someone helps me understand this.
You really don't need to handle SIGCHLD if your intent is to run a child process, do some stuff, then wait for it to finish. In that case, you just call waitpid when you're ready to synchronize. The only thing SIGCHLD is useful for is asynchronous notification of child termination, for example if you've got an interactive (or long-running daemon) application that's spawning various children and needs to know when they finish. However, SIGCHLD is really bad/ugly for this purpose too, since if you're using library code that creates child processes, you might catch the events for the library's children terminating and interfere with its handling of them. Signal handlers are inherently process-global and deal with global state, which is usually A Bad Thing(tm).
Here are two better approaches for when you have child processes that will be terminating asynchronously:
Approach 1 (select/poll event-based): Make sure you have a pipe to/from each child process you create. It can be either their stdin/stdout/stderr or just an extra dummy fd. When the child process terminates, its end of the pipe will be closed, and your main event loop will detect the activity on that file descriptor. From the fact that it closed, you recognize that the child process died, and call waitpid to reap the zombie.
Approach 2 (thread based): For each child process you create, also create a thread that will immediately call waitpid on the child process's pid. When waitpid returns successfully, use your favorite thread synchronization primitives to let the rest of the program know that the child terminated, or simply take care of everything you need to do in this waiter thread before it terminates.
Both of these approaches are modular and library-friendly (they avoid interfering with any other parts of your code or library code which might be making use of child processes).
You need to call the waiting syscalls like waitpid or friends -eg wait4 etc- othewise you could have zombie processes.
You could handle SIGCHLD to be notified that some child ended (or stopped, etc...) but you'll need to wait for it later.
Signal handlers are restricted to call a small set of async-signal-safe-functions (see signal(7) for more). Good advice is to just set a volatile sig_atomic_t flag inside, and test it at later and safer places.

Resources