简体   繁体   中英

Signal missed by child process

To initialize an application the parent process forks 3 child processes, the child processes then set up their signal handlers and signal back to the parent that they are ready to start activity. SIGUSR1 signal is used to achieve this.

The parent process in the meanwhile is waiting for these signals from the child processes. As soon as a signal is received, the parent matches its pid with the child pids it has stored and increments a counter. Once the parent knows that go-ahead signals from all child processes have been received it starts to send each one of them a SIGUSR1 signal to indicate to start activity.

The fact that all signals are sent from the parent for each child is verified; however, most times one of the child processes misses the signal. Over multiple trials, I have identified that the process to which the parent sends the signal first, misses it. However sometimes it also occurs that all child processes miss their signals. I have also used the 'strace' tool to check the flow of all signals but still can't seem to identify why the child processes fail to catch the signals sent by the parent.

Any feedback will be appreciated.

SIGUSR1 and other POSIX signals are not queued. If the process has already one pending, any other signals will be discarded.

You can avoid this by using "realtime signals". You use them just like you use the standard POSIX signals; the first one is named SIGRTMIN+0 , and the last one is named SIGRTMAX-0 . If you use sigqueue() , you can even attach one int (or a void pointer) as a payload.

POSIX realtime signals are queued (up to a limit), so you are less likely to lose them.

However, I would not use signals for tracking the child processes. I would use pipes, with the child having the write ends, and parent having the read ends, and all descriptors marked for close-on-exec using fcntl(descriptor, O_SETFD, O_CLOEXEC) on each.

The children update the parent on their status via one-byte messages. If the child exits or executes another program, the parent will see it as end-of-file condition ( read() returning zero). If the parent exits, the write end will become unwritable for the child write end, and any attempt at writing to the pipe will fail with EPIPE error. (It will also raise the SIGPIPE signal, so you might wish to use sigaction() to ignore the SIGPIPE signal.)

The parent can monitor the child process statuses in parallel using select() or poll() . Whenever a child process sends data, or exits or executes another program (which closes the write end of the pipe), the parent descriptor (read end of the pipe) will become readable. Personally, I also mark the parent descriptors nonblocking using fcntl(rfd, F_SETFL, O_NONBLOCK) , so that if there is a glitch, instead of blocking on a mis-read, the read on a parent will just fail with EWOULDBLOCK in errno.

If you want bidirectional data flow, it is easiest to use an additional pipe for each child, parent writing and child reading from it.

It is also possible to use unnamed Unix domain datagram sockets (created via socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) . (See also man 2 socket and man 7 unix for details on the parameters.) Also use fcntl(fds[0], F_SETFL, O_CLOEXEC) and fcntl(fds[1], F_SETFL, O_CLOEXEC) to make the descriptors close-on-exec, just as in the pipe case, too.

The problem with Unix domain socket pairs (of any type -- SOCK_STREAM , SOCK_DGRAM , SOCK_SEQPACKET ), is that they can contain ancillary data . This ancillary data can contain additional file descriptors, which are a limited commodity. If there is a possibility of an untrustworthy or nasty child process, it might kill its parent by sending it a few thousand file descriptors. For security, the parent process should monitor what it receives from the child process, and if it contains ancillary data, immediately close that descriptor (as the child process is obviously hostile!), and if any file descriptors were provided, close those too. You can only avoid this if you trust your child processes as much as you trust your original process for not doing anything nefarious.

Securing unix domain sockets is not hard, but checking each received datagram or receive for ancillary data is a couple of dozen additional lines of code.. Pipes are simpler.

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