为什么应该将fork()设计为返回文件描述符?


16

在他的关于网页自管绝招,丹·伯恩斯坦解释了竞争条件select()和信号,提供一个解决办法,并得出结论认为,

当然,正确的事情是fork()返回文件描述符,而不是进程ID。

他的意思是-能够select()在子进程上处理其状态更改,而不必使用信号处理程序来通知那些状态更改,这是否有意义?


那篇文章的输入和输出混淆了吗,还是我看错了?
ctrl-alt-delor

您可以要求信号通过管道传递。那就是我所做的。
ctrl-alt-delor

@ ctrl-alt-delor,是的,他似乎有点奇怪地使用“管道输入/输出”,但是我认为他在哪里写和从管道里读取是很清楚的。那是2003年的文字,我不确定signalfds当时是这样吗?
ilkkachu

5
Dan知道他在说什么,尽管他可能会故意挑衅。如果我故意挑衅,我会认为,当然,正确的事情是摆脱SIGCHLD。
史蒂夫·萨米特

1
@mosvy我有点夸张,但是我见过的每个尝试使用SIGCHLD的程序和程序员都遇到了问题。这是一个等待发生的比赛条件。早在我们所有的工作都受阻时wait(),有些事情是您做不到的,所以有人发明了SIGCHLD,但这是一项糟糕的工作。根据我的经验,现在它们的存在,洒不错,无阻塞wait3()wait4()和/或waitpid()在关键的地方调用(也许你的主事件循环)是一个大大优于替代。
史蒂夫·萨米特

Answers:


14

该问题已在您的源代码中进行了描述,select()应该被信号打断,例如SIGCHLD,但在某些情况下效果不佳。因此,解决方法是将信号写入管道,然后由监视select()。监视文件描述符是select()为了解决这个问题。

解决方法实质上将信号事件转换为文件描述符事件。如果fork()刚开始返回fd,则不需要解决方法,因为该fd可以直接与一起使用select()

所以是的,您对上一段的描述对我来说似乎是正确的。


fd(或某种其他类型的内核句柄)比普通的进程ID号更好的另一个原因是,在进程死后,PID可以重新使用。在将信号发送到进程时,在某些情况下可能会出现问题,可能无法确定该进程是否是您认为的那个进程,而不是另一个使用相同PID的进程。(尽管我认为在向子进程发送信号时这应该不是问题,因为父级必须wait()在子级上运行才能释放其PID。)


就是说,我不记得我所读过的有关PID重用是一个问题的确切案例,因此,如果有人想对此进行详细说明或澄清,甚至进行编辑,请随时这样做。
ilkkachu

2
正如您已经说过的那样,父母没有任何借口发现自己的孩子pid已被重用。它完全可以控制这种情况,因为它是呼叫者wait()
约书亚

这些称为“ 僵尸进程”:“已完成执行但仍在进程表中具有条目的进程:它是处于“终止状态”的进程。这对于子进程会发生,在此子进程中仍需要该条目以允许父进程读取孩子的出口状态的过程”
Lassi,

6
值得一提的是,现在Linux 可以从中返回文件描述符(pidfd)clone,这是fork在LInux上调用的实际系统调用。启用此功能的标志称为CLONE_PIDFD-参见例如lwn.net/Articles/784831
KJ Tsanaktsidis

1
@Lie Ryan,Re“ 从概念上讲让fork()返回全局句柄而不是局部句柄是更正确的,因为 Windows使用进程句柄。pid和退出代码一直停留到关闭进程的所有句柄为止(而不是等待父进程获得),从而避免了unix系统中常见的竞争条件。当句柄使进程保持活动状态时,将它们用作本地句柄而不是全局句柄更为有意义。
ikegami,

9

这只是“如果Unix的设计与原设计有所不同,那将是很棒的”。

PID的问题在于它们位于全局名称空间中,可以在其中将其重用于另一个进程,如果fork()在父级中返回某种可以保证始终引用子进程的句柄,那将是很好的选择。可以通过继承或unix套接字/ SCM_RIGHTS[1] 传递给其他进程。

另请参阅此处的讨论,以了解最近在Linux中“修复”该问题的努力,包括添加一个标志,clone()该标志将导致该标志返回pid-fd而不是PID。

但是即使那样,仍不能消除对自管道骇客[2]或更好接口的需求,因为通知主进程有关子状态的信号并不是您希望在主循环中处理的唯一信号该程序。不幸的是,epoll(7) + signalfd(2)Linux或kqueue(2)BSD 上的东西不是标准的-唯一的标准接口(但在较旧的系统上不受支持)差很多pselect(2)

[1] waitpid()可以在更新的系统上通过使用waitid(.., WNOWAIT)替代方法来防止在系统调用返回并使用其返回值之前重新使用PID 。

[2]我不会评论DJ伯恩斯坦(DJ Bernstein)的说法,他是他发明的(对不起,对不起;-)。


8

伯恩斯坦(Bernstein)对于“正确的事情”的评论并没有给出太多的上下文,但是我会冒险猜测:让fork(2)返回PID与Open(2),creat(2)等返回文件描述符不一致。Unix系统的其余部分可以使用代表进程的文件描述符而不是PID来完成进程操作。存在系统调用signalfd(2),它可以使信号和文件描述符之间的交互更好一些,并表明可以表示代表进程的文件描述符。


signalfd(2)看起来很棒,感谢您提到它!太糟糕了,它仅适用于Linux。
拉西

1
pidfd_open在Linux中也有讨论,例如,参见lwn.net/Articles/789023
dhag
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.