使用waitpid还是sigaction?

7
我已经理解: 1)waitpid用于等待子进程死亡并收集SIGCHLD和子进程的退出状态等信息。 2)当我们为SIGCHLD设置了信号处理程序时,我们会做一些与清理子进程相关的工作或其他工作(由程序员自定),然后执行waitpid,以防止子进程成为僵尸进程,然后返回。
现在,当我们进行fork / exec并且子进程返回时,我们是否需要在程序中同时包含1和2? 如果我们都有,那么首先获取SIGCHLD信号,因此将首先调用信号处理程序,从而成功地调用了它的waitpid,而不是父进程代码中的waitpid,如下所示:
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
}

希望有人能帮助我理解这个。

2个回答

21
如果您的目的是运行子进程,执行一些操作,然后等待其完成,那么您真的不需要处理SIGCHLD。在这种情况下,只需在准备好同步时调用waitpid即可。 SIGCHLD唯一有用的地方就是异步通知子进程终止,例如,如果您有一个交互式(或长时间运行的守护进程)应用程序,它正在生成各种子进程并需要知道它们何时完成。但是,SIGCHLD也非常不适合此目的,因为如果您使用创建子进程的库代码,则可能会捕获该库子进程终止的事件并干扰其处理。信号处理程序本质上是进程全局的,并处理全局状态,这通常是不好的(tm)。

以下是两种更好的方法,用于异步终止子进程的情况:

方法1(基于select/poll事件):确保每个创建的子进程都有一个管道与之通信。它可以是它们的stdin/stdout/stderr,也可以只是额外的虚拟文件描述符。当子进程终止时,它的管道将被关闭,您的主事件循环将检测到该文件描述符上的活动。从它关闭的这一事实,您就能知道该子进程已经死亡,并调用waitpid收割僵尸进程。

方法2(基于线程):对于每个创建的子进程,还要创建一个线程,该线程将立即对子进程的pid调用waitpid。当waitpid成功返回时,使用您喜欢的线程同步原语让程序的其余部分知道子进程终止了,或在此等待线程中处理您需要做的所有事情,然后终止它。

这两种方法都是模块化且适合库使用(避免干扰代码或库代码的任何其他部分,这些部分可能正在使用子进程)。


嗨,谢谢你的回答。但是我实际上想要的是:当我在父进程中同时拥有sig_handler和waitpid时,由于sigchld而调用了sig_handler,然后像我在问题中所示的那样进行第二个waitpid会返回-1。所以我可以只删除我现在拥有的第二个waitpid吗? - Vin
是的,你只能成功等待一个给定的子进程一次。再次等待实际上是一个错误,因为pid在第一次等待时被“释放”,可能已经被重新用于新的子进程(如果你碰巧创建了另一个子进程)。 - R.. GitHub STOP HELPING ICE
@R.. 我们能详细说明一下当子进程终止时,它的管道末端将被关闭吗? 要非常精确,难道不应该是 当子进程关闭连接到这个管道末端的FD,并且没有其他进程具有连接到该管道末端的FD,则管道另一端将被标记为关闭(接收EOF)? 我相信这将更准确地解释发生了什么,以及超过2个具有与管道相连的FD的进程的重要特殊情况。 - nh2
请注意,我尝试了第一种方法,并在使用WNOHANG选项进行waitpid()时遇到了一个罕见的竞态条件,以使其非阻塞。问题是子进程关闭了管道,但子进程几乎还没有退出,因此waitpid()调用无法回收子进程。 - undefined
因为我在大学受到人为限制,只能使用特定的C函数,所以我最终选择了Qt推荐的方法,即使用一个全局管道。我的SIGCHLD处理程序会向管道中写入一个字节,而我的poll()代码会从管道中读取一个字节,具体的方法在这里提到:https://stackoverflow.com/a/43555661/13279557 - undefined

1

你需要调用像waitpid或者类似的等待系统调用,例如wait4等,否则你可能会遇到僵尸进程

你可以处理SIGCHLD信号来通知你某个子进程已经结束(或停止等),但是你需要稍后等待它。

信号处理程序只能调用一小组异步信号安全函数(请参见signal(7)了解更多信息)。一个好的建议是在其中设置一个volatile sig_atomic_t标志,并在稍后和更安全的地方测试它。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接