为了初始化一个应用程序,父进程分叉3个子进程,子进程然后设置它们的信号处理程序并向父进程发回信号表明它们已准备好开始活动。 SIGUSR1信号用于实现此目的。
同时,父进程正在等待来自子进程的这些信号。一旦收到信号,父母就会将其pid与其存储的子pid相匹配并递增计数器。一旦父母知道已收到来自所有子进程的反超信号,它就会开始向每个子进程发送一个SIGUSR1信号,以指示开始活动。
验证了每个孩子从父母发送的所有信号的事实;但是,大多数情况下,其中一个子进程错过了信号。在多次试验中,我已经确定父母首先发送信号的过程错过了它。然而,有时也会发生所有子进程错过其信号。我还使用'strace'工具来检查所有信号的流量,但似乎无法确定子进程无法捕获父进程发送的信号的原因。
我们将不胜感激。
答案 0 :(得分:0)
SIGUSR1
和其他POSIX信号未排队。如果进程已经有一个待处理,则任何其他信号都将被丢弃。
您可以使用“实时信号”来避免这种情况。您可以像使用标准POSIX信号一样使用它们;第一个名为SIGRTMIN+0
,最后一个名为SIGRTMAX-0
。如果您使用sigqueue()
,您甚至可以附加一个int
(或无效指针)作为有效负载。
POSIX实时信号排队(最多限制),因此您不太可能丢失它们。
但是,我不会使用信号来跟踪子进程。我会使用管道,子进程具有写入结束,父进程具有读取结束,并且所有描述符都使用fcntl(descriptor, O_SETFD, O_CLOEXEC)
标记为close-on-exec。
孩子们通过单字节消息更新父级的状态。如果子进程退出或执行另一个程序,则父进程将其视为文件结束条件(read()
返回零)。如果父进程退出,则写入结束对于子写入结束将变为不可写,并且任何写入管道的尝试都将失败,并显示EPIPE
错误。 (它还会提升SIGPIPE
信号,因此您可能希望使用sigaction()
来忽略SIGPIPE
信号。)
父母可以使用select()
或poll()
并行监控子流程状态。每当子进程发送数据,或退出或执行另一个程序(关闭管道的写入端)时,父描述符(读取管道的末尾)将变得可读。就个人而言,我还使用fcntl(rfd, F_SETFL, O_NONBLOCK)
将父描述符标记为非阻塞,这样如果出现故障,而不是在错误读取时阻塞,则对父进行的读取将在errno中以EWOULDBLOCK
失败。
如果您想要双向数据流,最简单的方法是为每个孩子,父母写作和孩子阅读使用额外的管道。
也可以使用未命名的Unix域数据报套接字(通过socketpair(AF_UNIX, SOCK_DGRAM, 0, fds)
创建。)(有关参数的详细信息,另请参阅man 2 socket和man 7 unix。)另外使用{{1} }和fcntl(fds[0], F_SETFL, O_CLOEXEC)
使描述符接近执行,就像在管道情况下一样。
Unix域套接字对(任何类型 - fcntl(fds[1], F_SETFL, O_CLOEXEC)
,SOCK_STREAM
,SOCK_DGRAM
)的问题在于它们可以包含辅助数据。此辅助数据可以包含其他文件描述符,这些是有限的商品。如果有可能存在不值得信任或讨厌的子进程,它可能会通过发送几千个文件描述符来杀死它的父进程。为了安全起见,父进程应该监视它从子进程接收的内容,如果它包含辅助数据,则立即关闭该描述符(因为子进程显然是敌对的!),如果提供了任何文件描述符,也要关闭它们。如果您信任您的孩子,只要您信任您的原始流程,而不做任何恶意的事情,您就可以避免这种情况。
保护unix域套接字并不难,但检查每个接收到的数据报或接收辅助数据是另外几行代码。管道更简单。