C 如何获取死子进程的PID并在父进程中使用它?

C 如何获取死子进程的PID并在父进程中使用它?,c,unix,fork,freebsd,sigchld,C,Unix,Fork,Freebsd,Sigchld,我正在尝试制作一个C程序(用于FreeBSD,Unix),它在一个循环中创建4个子进程。每个孩子都会做一些事情,当他们死后,他们会立即被其他孩子取代。最后,我有4个孩子一直在工作 每个子PID必须在表中注册,并且在它们失效后,必须删除表的PID。这是我遇到麻烦的部分 因此,在第一次尝试中,我尝试创建一个处理程序,该处理程序向全局变量发送死孩子的PID: int global_variable; void handler_SIGCHLD(int sig) { pid_t son;

我正在尝试制作一个C程序(用于FreeBSD,Unix),它在一个循环中创建4个子进程。每个孩子都会做一些事情,当他们死后,他们会立即被其他孩子取代。最后,我有4个孩子一直在工作

每个子PID必须在表中注册,并且在它们失效后,必须删除表的PID。这是我遇到麻烦的部分

因此,在第一次尝试中,我尝试创建一个处理程序,该处理程序向全局变量发送死孩子的PID:

int global_variable;

void handler_SIGCHLD(int sig)
{
    pid_t son;
    int e;

    do {
        son = wait3(&e, WNOHANG, NULL);
        if ((son > (pid_t)0) && (WIFEXITED(e) || WIFSIGNALED(e)))
        {
            global_variable = son;
        }
    } while (son > (pid_t)0);
}
然后我在父级中使用它从表中删除死亡子级的PID。但后来我意识到,如果两个孩子同时死亡,其中一个PID将被另一个PID覆盖


我怎样才能避免这种情况

您不应该从信号处理程序执行此操作。你不应该在信号处理器上做任何事情(如果你想保持理智的话)

一种可靠的方法是将
SIGCHLD
信号转换为文件描述符事件,并将其集成到事件循环中(
select
poll
epoll
…)。观察事件时,在循环中使用
waitpid(…,WNOHANG)
来收集所有死亡的孩子。有关我的答案,请参见此处:

上面的答案假设您在Linux上,并使用
signalfd
(Linux专用工具)将信号转换为文件描述符。但是,您也可以使用“自我管道”技巧(非常小心),或
kqueue
(FreeBSD、OSX)。请注意,对于
kqueue
,没有中间文件描述符,您直接接收信号事件,就像文件描述符事件一样


另一方面,通过使用可移植的事件循环库,您可以绕过所有这些低级细节。一些库已经提供了启动和监视子进程的工具。我推荐
libuv
(node.js)。

对不起,我忘了说我使用的是FreeBSD(Unix)。@Vertice然后你可以使用kqueue,请参阅更新的答案。请等待在任何处理程序之外没有WNOHANG的孩子。在SIGHCLD处理程序中不执行任何操作。或者,如果必须在处理程序中执行此操作,请不要使用单个全局变量,而是使用一个死PID数组,这样它们就不会相互覆盖。另一种选择是。在处理程序中,设置一个
sig_atomic_t
标志并返回。在处理程序外部,如果设置了标志,则按住SIGCHLD,执行
while(son=wait3(&e,WNOHANG,NULL)){…}
操作,然后重新启用SIGCHLD。