简体   繁体   English

子进程错过了信号

[英]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. 为了初始化应用程序,父进程会派生3个子进程,然后子进程设置其信号处理程序,并向父进程发信号通知它们已准备好开始活动。 SIGUSR1 signal is used to achieve this. SIGUSR1信号用于实现此目的。

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. 收到信号后,父级将其pid与已存储的子级pid进行匹配,并增加一个计数器。 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. 一旦父进程知道已经收到所有子进程的许可信号,它就会开始向每个进程发送SIGUSR1信号,以指示开始活动。

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. 我还使用了“ strace”工具来检查所有信号的流向,但似乎仍然无法确定为什么子进程无法捕获父进程发送的信号。

Any feedback will be appreciated. 任何反馈将不胜感激。

SIGUSR1 and other POSIX signals are not queued. SIGUSR1和其他POSIX信号未排队。 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; 您可以像使用标准POSIX信号那样使用它们。 the first one is named SIGRTMIN+0 , and the last one is named SIGRTMAX-0 . 第一个名为SIGRTMIN+0 ,最后一个名为SIGRTMAX-0 If you use sigqueue() , you can even attach one int (or a void pointer) as a payload. 如果使用sigqueue() ,甚至可以附加一个int (或void指针)作为有效负载。

POSIX realtime signals are queued (up to a limit), so you are less likely to lose them. POSIX实时信号已排队(上限),因此您丢失信号的可能性较小。

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. 我将使用管道,其中子级具有写端,父级具有读端,并且所有描述符都使用fcntl(descriptor, O_SETFD, O_CLOEXEC)标记为执行时关闭。

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). 如果子级退出或执行另一个程序,则父级将其视为文件结束条件( read()返回零)。 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. 如果父级退出,则子级写端的写端将变得不可写,并且任何尝试写入管道的尝试都将失败,并出现EPIPE错误。 (It will also raise the SIGPIPE signal, so you might wish to use sigaction() to ignore the SIGPIPE signal.) (它也会引发SIGPIPE信号,因此您可能希望使用sigaction()忽略SIGPIPE信号。)

The parent can monitor the child process statuses in parallel using select() or poll() . 父级可以使用select()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. 就我个人而言,我还使用fcntl(rfd, F_SETFL, O_NONBLOCK)标记了父描述符的非阻塞状态,因此,如果出现故障,而不是由于误读而阻塞,对父对象的读取将因errno中的EWOULDBLOCK而失败。

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. 也可以使用未命名的Unix域数据报套接字(通过socketpair(AF_UNIX, SOCK_DGRAM, 0, fds) 。)(有关参数的详细信息,另请参见man 2 socketman 7 unix 。)还要使用fcntl(fds[0], F_SETFL, O_CLOEXEC)fcntl(fds[1], F_SETFL, O_CLOEXEC)来使描述符在执行时关闭,就像在管道情况下一样。

The problem with Unix domain socket pairs (of any type -- SOCK_STREAM , SOCK_DGRAM , SOCK_SEQPACKET ), is that they can contain ancillary data . 用Unix域套接字对(任何类型的-问题SOCK_STREAMSOCK_DGRAMSOCK_SEQPACKET ),是它们可以包含辅助数据 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. 保护unix域套接字并不难,但是检查每个接收到的数据报或接收辅助数据是另外几十行代码。管道更简单。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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