尽力回答所有子问题;我很抱歉其中一些内容比理想情况下更模糊:
如果有一个
库代码具有的可能性
注册 pthread_atfork 处理程序
这不是异步信号安全的,
这否定了fork的安全性吗?
是的。这分叉文档 http://pubs.opengroup.org/onlinepubs/009695399/functions/fork.html明确提到这一点:
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 http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt(搜索“L16723”)。
做
答案取决于是否
信号处理程序所在的线程
跑步可能会在中间
使用 atfork 的资源
处理程序需要?或者说不同的
方式,如果 atfork 处理程序使用
同步资源(互斥锁、
等)但是 fork 是从 a 调用的
在a中执行的信号处理程序
从不访问这些的线程
资源,程序是否符合要求?
严格来说答案是否定的,因为根据规范,函数要么是异步信号安全的,要么不是;不存在“在某些情况下安全”的概念。在实践中,您很可能会侥幸逃脱,但您可能会受到笨重但正确的实现的影响,该实现没有按照您期望的方式分区其资源。
基于这个问题,如果
实现了“线程安全”分叉
在系统库内部使用
pthread_atfork 建议的习惯用法
(获取预叉中的所有锁
处理程序并释放两者中的所有锁
父子后叉
处理程序),那么 fork 是否安全
在线程中使用信号处理程序
程序?是不是有可能
处理信号的线程可能位于
调用 malloc 或的中间
fopen/fclose 并持有全局
锁,导致期间死锁
叉?
如果按照这种方式实施,那么你是对的,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));
其中每个resetlock
and lock_initialize
函数最终调用 glibc 的内部等效函数pthread_mutex_init()
,无论是否有任何服务员,都会有效地重置互斥锁。
我认为理论是,通过获取(递归)锁,可以保证没有其他线程会接触数据结构(至少以可能导致崩溃的方式),然后重置各个锁以确保资源不被访问。 t 永久被阻止。 (重置当前线程的锁是安全的,因为现在没有其他线程来争夺数据结构,并且实际上直到使用锁的任何函数返回为止都不会发生)。
我不是 100% 相信这涵盖了所有可能性(尤其是因为如果/当信号处理程序返回时,刚刚被盗锁的函数将尝试解锁它,并且内部递归解锁函数不能防止解锁太多次了!) - 但这似乎是一个可行的方案could构建在异步信号安全递归锁之上。
最后,即使 fork 是安全的
信号处理程序,分叉是否安全
一个信号处理程序,然后从
信号处理程序,或者调用
始终在信号处理程序中分叉
需要随后调用 _exit
或 exec 函数系列之一
在信号处理程序返回之前?
我假设你在谈论子进程? (如果fork()
异步信号安全意味着任何事情都应该可以在父级中返回!)
在规范中没有发现任何其他说明(尽管我可能错过了)我相信它应该安全 - 至少,从子级中的信号处理程序返回的意义上来说“安全”并不意味着其本身存在未定义的行为,尽管多线程进程刚刚分叉的事实可能意味着exec*()
or _exit()
可能是最安全的行动方案。