我真的不确定POSIX在存在线程和信号时对 fork
的安全性的要求 . fork
被列为异步信号安全函数之一,但如果库代码有可能注册了非同步信号安全的 pthread_atfork
处理程序,这是否会否定 fork
的安全性?答案取决于运行信号处理程序的线程是否正在使用atfork处理程序所需的资源?或者换句话说,如果atfork处理程序使用同步资源(互斥体等),但是从一个永远不会访问这些资源的线程中执行的信号处理程序调用 fork
,程序是否符合要求?
在这个问题的基础上,如果"thread-safe" forking在系统库内部使用 pthread_atfork
建议的习语实现(获取prefork处理程序中的所有锁并释放父和子postfork处理程序中的所有锁),那么 fork
可以安全使用来自线程程序中的信号处理程序?处理信号的线程是否可能在调用 malloc
或 fopen
/ fclose
并持有全局锁定的过程中,导致 fork
期间出现死锁?
最后,即使 fork
在信号处理程序中是安全的,信号处理程序中的 fork
是安全的,然后从信号处理程序返回,或者在信号处理程序中调用 fork
总是需要后续调用 _exit
或其中一个 exec
信号处理程序返回之前的函数族?
5 回答
尽力回答所有子问题;我很抱歉,其中一些比理想应该更为模糊:
是 . fork documentation明确提到:
当然,这意味着您实际上不能将
pthread_atfork()
用于使多线程库对于认为它们是单线程的进程透明的目的,因为没有任何pthread同步函数是异步信号安全的 . 这被指出为规范中的缺陷,请参阅http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt(搜索"L16723") .严格来说答案是否定的,因为根据规范,功能要么是异步信号安全的,要么是不是;没有“在某些情况下安全”的概念 . 在实践中,你可能会侥幸逃脱,但是你很容易受到一种笨重但正确的实现的影响,而这种实现并没有按照你期望的方式划分资源 .
如果以这种方式实现,那么你是对的,来自信号处理程序的
fork()
永远不会是安全的,因为如果调用线程已经持有它,尝试获取锁可能会死锁 . 但这意味着使用这种方法的实现不符合要求 .以glibc为例,它没有那样做 - 相反,它需要两种方法:首先,它获得的锁是递归的(所以如果当前线程已经有它们,它们的锁定数将会增加);此外,在子进程中,它只是单方面覆盖所有锁 - 请参阅
nptl/sysdeps/unix/sysv/linux/fork.c
中的这个摘录:其中每个
resetlock
和lock_initialize
函数最终调用glibc的内部等价物pthread_mutex_init()
,无论是否有任何服务员,都会有效地重置互斥锁 .我认为理论是,通过获得(递归)锁,它保证没有其他线程会触及数据结构(至少以可能导致崩溃的方式),然后重置单个锁确保资源不是'永久封锁(重置当前线程的锁是安全的,因为现在没有其他线程可以争用数据结构,并且实际上直到使用锁的任何函数都没有返回) .
我'm not 100% convinced that this covers all eventualities (not least because if/when the signal handler returns, the function that'只是把它的锁被盗将尝试解锁它和内部递归解锁函数不能防止解锁太多次!) - 但似乎可以在异步信号安全递归锁之上构建一个可行的方案 .
我假设你在谈论孩子的过程? (如果
fork()
是异步信号安全意味着什么,那么应该可以在父母中返回!)没有在规范中发现任何其他情况(虽然我可能已经错过了)我认为它应该是安全的 - 至少,'safe'在某种意义上说,从孩子的信号处理程序返回并不意味着未定义的行为本身,虽然多线程进程刚刚分叉的事实可能意味着
exec*()
或_exit()
可能是最安全的行动方案 .我正在添加这个答案,因为看起来
fork()
可能不再被视为异步安全 . 至少这似乎是glibc
的情况,但POSIX中可能不再存在支持 . 目前标记为"accepted"的答案似乎得出结论认为它是安全的,但至少在glibc
中可能并非如此 .确实!从this very reason列表中看起来好像是The Open Group resolved to remove it .
IEEE 1003.1c-1995解释请求#37关于
pthread_atfork
.glibc Bug 4737标识_664503_从异步安全函数列表中逐出的分辨率,
posix_spawn()
用于填充其位置 . 不幸的是,它被解决为WONTFIX
所以甚至连更新的联机帮助页都没有 .在信号处理程序中使用fork()应该没问题 .
pthread_atfork听起来像个坏主意 .
为了回答你原来的问题,pthread不能保证调用任何pthread_atfork函数的安全是异步信号安全的,因为内核的信号实现使得这是不可能的 .
哦,如果你在信号处理程序中分叉,不要让子进程从信号处理程序返回 . 这是未定义的 .
据我所知,fork source in glibc它使用信号状态临界区来确保分叉过程不会被信号中断 .
由于
pthread_atfork
处理程序在关键部分锁定后执行 - 它们自动变为信号安全 .可能是我错了,我会很感激修正 .
这里https://www.securecoding.cert.org/confluence/display/seccode/SIG30-C.+Call+only+asynchronous-safe+functions+within+signal+handlers
fork被列为异步信号安全,因此可以使用它 .