首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在线程程序中,fork (应该是)信号处理程序是安全的吗?

在线程程序中,fork (应该是)信号处理程序是安全的吗?
EN

Stack Overflow用户
提问于 2010-12-16 03:08:43
回答 5查看 3.7K关注 0票数 21

我真的不确定POSIX对fork在存在线程和信号的情况下的安全性的要求。fork被列为异步信号安全函数之一,但是如果库代码有可能注册了不是异步信号安全的pthread_atfork处理程序,这会否定fork的安全性吗?答案是否取决于运行信号处理程序的线程是否正在使用atfork处理程序所需的资源?或者说是另一种方式,如果atfork处理程序使用同步资源(互斥等)。但是fork是从一个信号处理程序调用的,而这个信号处理程序是在一个永远不会访问这些资源的线程中执行的,那么程序是否符合呢?

基于这个问题,如果在系统库中使用pthread_atfork建议的习惯用法(获取prefork处理程序中的所有锁并释放父和子postfork处理程序中的所有锁)在系统库中实现“线程安全”forking,那么在线程化程序中从信号处理程序使用fork是否安全?有没有可能处理信号的线程正在调用mallocfopen/fclose并持有全局锁,从而导致fork期间的死锁

最后,即使fork在信号处理程序中是安全的,在信号处理程序中执行fork然后从信号处理程序返回是否安全,或者在信号处理程序返回之前,在信号处理程序中调用fork是否总是需要后续调用_exitexec系列函数之一?

EN

回答 5

Stack Overflow用户

回答已采纳

发布于 2010-12-31 21:26:33

尽我最大的努力回答所有的子问题;我很抱歉,其中一些问题比理想情况下更模糊:

如果库代码有可能注册了不是异步信号安全的pthread_atfork处理程序,这会否定

的安全性吗?

是。fork documentation明确提到这一点:

代码语言:javascript
复制
   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.

当然,这意味着你不能实际使用pthread_atfork()来达到让多线程库对那些认为它们是单线程的进程透明的预期目的,因为没有一个pthread同步函数是异步信号安全的;这是规范中的一个缺陷,请参阅http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt (搜索"L16723")。

是否取决于运行信号处理程序的线程是否正在使用atfork处理程序需要的资源?或者说是另一种方式,如果atfork处理程序使用同步资源(互斥等)。但是fork是从一个信号处理程序调用的,而这个信号处理程序是在一个从不访问这些资源的线程中执行的,程序是否符合?

严格来说,答案是否定的,因为根据规范,函数要么是异步信号安全的,要么不是异步信号安全的;没有“在某些情况下安全”的概念。在实践中,您可以很好地摆脱它,但您很容易受到一个笨拙但正确的实现的影响,该实现没有按照您期望的方式对其资源进行分区。

基于这个问题,如果在系统库中使用pthread_atfork建议的习惯用法在内部实现了“线程安全”的fork (获取prefork处理程序中的所有锁,并释放父和子postfork处理程序中的所有锁),那么在线程化程序中从信号处理程序中使用fork会安全吗?

处理信号的线程有没有可能在调用malloc或fopen/fclose的过程中持有全局锁,从而导致fork期间的死锁?

如果它是以这种方式实现的,那么您是对的,来自信号处理程序的fork()将永远不会安全,因为如果调用线程已经持有锁,则尝试获取锁可能会死锁。但这意味着使用这种方法的实现将是不符合的。

以glibc为例,它并没有这样做--相反,它采用了两种方法:首先,它获得的锁是递归的(因此,如果当前线程已经有锁,那么它们的锁计数就会增加);此外,在子进程中,它只是单边地覆盖所有锁-请参阅nptl/sysdeps/unix/sysv/linux/fork.c中的摘录

代码语言:javascript
复制
  /* 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));

其中,每个resetlocklock_initialize函数最终都会调用glibc的内部pthread_mutex_init()等效项,从而有效地重置互斥锁,而无需考虑任何等待程序。

我认为原理是,通过获得(递归)锁,可以保证没有其他线程会接触到数据结构(至少在某种程度上可能会导致崩溃),然后重置各个锁可以确保资源不会永久阻塞。(重置当前线程的锁是安全的,因为现在没有其他线程争用数据结构,而且实际上在使用锁的任何函数返回之前都不会)。

我不是百分之百确信这涵盖了所有的可能性(尤其是因为如果/当信号处理程序返回时,刚刚被盗的锁的函数将试图解锁它,并且内部递归解锁函数不能防止解锁太多次!)-但似乎可以在异步信号安全的递归锁之上构建一个可行的方案。

最后,即使fork在信号处理程序中是安全的,在信号处理程序中派生然后从信号处理程序返回是否安全,或者在信号处理程序返回之前,调用信号处理程序中的fork是否总是需要后续调用_exit或exec系列函数之一?

我想你说的是子进程吧?(如果fork()是异步信号安全意味着什么,那么应该可以在父级中返回!)

在规范中没有发现任何其他声明(尽管我可能错过了),我认为它应该是安全的--至少,“安全”的意义在于,从子进程中的信号处理程序返回本身并不意味着未定义的行为,尽管多线程进程刚刚派生的事实可能意味着exec*()_exit()可能是最安全的操作过程。

票数 14
EN

Stack Overflow用户

发布于 2014-01-22 06:40:49

我添加这个答案是因为看起来fork()可能不再被认为是异步安全的。至少在glibc中看起来是这样的,但是也许POSIX中不再存在支持。目前标记为“已接受”的答案似乎得出这样的结论:它是安全的,但至少在glibc中可能不是这样。

基于这个问题,如果在系统库中使用pthread_atfork建议的习惯用法在内部实现了“线程安全”的fork (获取prefork处理程序中的所有锁,并释放父和子postfork处理程序中的所有锁),那么在线程化程序中从信号处理程序中使用fork会安全吗?

处理信号的线程有没有可能在调用malloc或fopen/fclose的过程中持有全局锁,从而导致fork期间的死锁?

确实是这样!它看起来像是this very reason列表中的The Open Group resolved to remove it

IEEE1003.1c-1995解释请求#37涉及pthread_atfork

解释委员会认为...添加以下解释性内容: Pg 78 864行“此外,从信号处理程序调用的fork调用pthread_atfork设置的fork处理程序的调用必须是异步安全的。

glibc Bug 4737确定了一种解决方案,即将fork()从异步安全函数列表中逐出,并使用posix_spawn()来填补它的位置。不幸的是,它被解析为WONTFIX,所以连手册页都没有更新。

票数 5
EN

Stack Overflow用户

发布于 2010-12-16 03:36:44

在信号处理程序中使用fork()应该可以。

使用pthread_atfork听起来不是个好主意。

回答你最初的问题,pthread不能保证调用任何pthread_atfork函数的安全性是异步信号安全的,因为内核的信号实现使得这是不可能的。

哦,如果您派生了信号处理程序,就不要允许孩子从信号处理程序返回。这是不确定的。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/4453822

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档