R..*_*R.. 21 c deadlock fork signals pthreads
我真的不确定POSIX对fork存在线程和信号的安全性的要求.fork被列为异步信号安全功能之一,但如果库代码有可能注册了pthread_atfork非异步信号安全的处理程序,这是否会否定安全性fork?答案取决于运行信号处理程序的线程是否可以使用atfork处理程序所需的资源?或者换句话说,如果atfork处理程序使用同步资源(互斥体等)但是fork从一个从不访问这些资源的线程中执行的信号处理程序调用,程序是否符合要求?
在这个问题的基础上,如果"线程安全"分叉在系统库内部使用建议的惯用语实现pthread_atfork(获取prefork处理程序中的所有锁并释放父和子postfork处理程序中的所有锁),那么就是fork安全的在线程程序中使用信号处理程序?处理信号的线程是否可能处于调用malloc或fopen/ fclose和持有全局锁的过程中,从而导致死锁fork?
最后,即使fork在信号处理程序中是安全的,在信号处理程序中是否安全fork然后从信号处理程序返回,或者fork在信号处理程序中进行调用总是需要后续调用函数_exit或其中一个exec函数.信号处理程序返回
psm*_*ars 12
尽力回答所有子问题; 我很抱歉,其中一些比理想应该更为模糊:
如果库代码有可能注册了非异步信号安全的pthread_atfork处理程序,这会否定fork的安全性吗?
是.该叉的文件明确提到了这一点:
When the application calls fork() from a signal handler and any of the
fork handlers registered by pthread_atfork() calls a function that is
not asynch-signal-safe, the behavior is undefined.
Run Code Online (Sandbox Code Playgroud)
当然,这意味着您实际上无法pthread_atfork()实现其预期目的,即使多线程库对于认为它们是单线程的进程是透明的,因为没有任何pthread同步函数是异步信号安全的.这是规范中的缺陷,请参见http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt(搜索"L16723").
答案取决于运行信号处理程序的线程是否可以使用atfork处理程序所需的资源?或者换句话说,如果atfork处理程序使用同步资源(互斥体等),但是从一个永远不会访问这些资源的线程中执行的信号处理程序调用fork,程序是否符合要求?
严格来说答案是否定的,因为根据规范,功能要么是异步信号安全的,要么是不是; 没有"在某些情况下安全"的概念.在实践中,你可能会侥幸逃脱它,但你很容易受到一种笨重但正确的实现的影响,而这种实现没有按照你期望的方式划分资源.
在这个问题的基础上,如果使用pthread_atfork建议的习语在系统库内部实现"线程安全"分叉(获取prefork处理程序中的所有锁并释放父和子postfork处理程序中的所有锁),那么就是fork在线程程序中使用信号处理程序是否安全?处理信号的线程是否可能在调用malloc或fopen/fclose并持有全局锁定的过程中,导致fork期间出现死锁?
如果它以这种方式实现,那么你是对的,fork()从信号处理程序永远不会是安全的,因为如果调用线程已经持有它,尝试获取锁可能会死锁.但这意味着使用这种方法的实现不符合要求.
以glibc为例,它没有那样做 - 相反,它需要两种方法:首先,它获得的锁是递归的(所以如果当前线程已经有它们,它们的锁定数将会简单地增加); 此外,在子进程中,它只是单方面覆盖所有锁 - 请参阅以下摘录nptl/sysdeps/unix/sysv/linux/fork.c:
/* Reset the file list. These are recursive mutexes. */
fresetlockfiles ();
/* Reset locks in the I/O code. */
_IO_list_resetlock ();
/* Reset the lock the dynamic loader uses to protect its data. */
__rtld_lock_initialize (GL(dl_load_lock));
Run Code Online (Sandbox Code Playgroud)
其中每个的resetlock和lock_initialize功能的调用最终的glibc的内部等效pthread_mutex_init(),有效地复位所述互斥无论任何服务员的.
我认为理论是,通过获得(递归)锁定,可以保证没有其他线程会触及数据结构(至少以可能导致崩溃的方式),然后重置单个锁定确保资源不受限制.永久阻止.(重置当前线程的锁是安全的,因为现在没有其他线程可以争用数据结构,并且实际上直到使用锁返回的任何函数都没有).
我不是100%确信这涵盖了所有可能性(尤其是因为如果/当信号处理程序返回时,刚被锁定的函数将尝试解锁它,并且内部递归解锁功能无法防止解锁太多次!) - 但似乎可以在异步信号安全的递归锁之上构建一个可行的方案.
最后,即使fork在信号处理程序中是安全的,在信号处理程序中分叉然后从信号处理程序返回是安全的,还是在信号处理程序中调用fork总是需要后续调用_exit或其中一个exec信号处理程序返回之前的函数族?
我假设你在谈论孩子的过程?(如果fork()异步信号安全意味着什么,那么它应该可以在父母中返回!)
没有在规范中发现任何其他情况(尽管我可能已经错过了)我相信它应该是安全的 - 至少,'安全'在某种意义上说,从孩子的信号处理程序返回并不意味着未定义的行为虽然多线程进程刚刚分叉的事实可能意味着一个exec*()或者_exit()可能是最安全的行动方案.
我添加这个答案是因为它看起来fork()可能不再被认为是异步安全的。至少glibc在 POSIX 中似乎是这种情况,但可能不再提供支持。目前标记为“已接受”的答案似乎得出的结论是它是安全的,但至少在glibc这种情况下可能并非如此。
基于这个问题,如果使用 pthread_atfork 建议的习惯用法在系统库内部实现“线程安全”分叉(获取 prefork 处理程序中的所有锁并释放父和子 postfork 处理程序中的所有锁),那么 fork在线程程序中使用信号处理程序是否安全?处理信号的线程是否可能在调用 malloc 或 fopen/fclose 并持有全局锁的中间,导致 fork 期间死锁?
的确!看起来Open Group正因为这个原因决定将其从列表中删除。
IEEE 1003.1c-1995 Interpretation Request #37 关于pthread_atfork.
解释委员会认为……做出以下解释性补充: 第 78 页第 864 行“此外,从信号处理程序调用的分叉中调用 pthread_atfork 设置的分叉处理程序必须是异步安全的。”
glibc错误 4737标识了fork()从异步安全函数列表中逐出并posix_spawn()用于填充其位置的分辨率。不幸的是,它已解决,WONTFIX因此甚至连联机帮助页都没有更新。
| 归档时间: |
|
| 查看次数: |
3026 次 |
| 最近记录: |