如果我fork
是一个线程持有互斥锁的进程,如果我立即exec
在子进程中,我是否相对安全?在孩子面前做什么事情是安全的exec
?
如果执行的线程fork
然后子进程在调用之前继续释放互斥锁,exec
这会导致问题吗?如果我尝试在子进程中获取父进程之前拥有fork
(并且可能拥有也可能不拥有)的子进程会发生什么?
不同平台的答案是否不同?我主要关注 Unix 变体,尤其是 Linux。但我对NT很好奇。虽然,当然,NT(据我所知)没有fork
.
如果我fork
是一个线程持有互斥锁的进程,如果我立即exec
在子进程中,我是否相对安全?在孩子面前做什么事情是安全的exec
?
如果执行的线程fork
然后子进程在调用之前继续释放互斥锁,exec
这会导致问题吗?如果我尝试在子进程中获取父进程之前拥有fork
(并且可能拥有也可能不拥有)的子进程会发生什么?
不同平台的答案是否不同?我主要关注 Unix 变体,尤其是 Linux。但我对NT很好奇。虽然,当然,NT(据我所知)没有fork
.
参见pthread_atfork,尤其是 RATIONALE 部分,讨论与fork
多线程环境相关的问题。fork
它还暗示了在孩子和父母之前和之后应该有效的内容。
更新:RATIONALE 部分是非规范性的,结果与标准的其他部分相冲突。有关更多详细信息,请参阅Dave Butenhof 的此缺陷报告。
对于多线程程序的任何状态(即,任何持有任何互斥锁的线程),紧随exec
其后应该是安全的。fork
至于 和 之间的可能fork
情况exec
,情况很复杂:
最重要的是fork
子进程中只有一个线程(称为 的线程)被复制。因此,此时另一个线程持有的任何互斥锁fork
将永远锁定。也就是说(假设非进程共享互斥锁)它在子进程中的副本被永远锁定,因为没有线程可以解锁它。
在可能的情况下释放互斥锁fork
是安全的,也就是说,如果fork
ing 线程首先拥有互斥锁。这就是pthread_atfork
处理程序通常的工作方式:在之前锁定互斥锁fork
,在子级中解锁和在父级中解锁。
至于获得进程在 fork 之前拥有的互斥锁(请记住,我们讨论了子地址空间中的副本):如果它由ing 线程拥有fork
,则它是递归锁定(适用于PTHREAD_MUTEX_RECURSIVE
);如果它被另一个线程拥有,它将永远保持锁定状态并且无法重新获取。
通过注册适当pthread_atfork
的处理程序,第三方库可以保证在fork
和之间安全使用exec
。(我希望它主要来自编程语言运行时,而不是通用库)。
经过更多研究后,我建议避免以任何方式依赖,并且只在andpthread_atfork
之间进行异步信号安全调用(放弃/ for会更好)。fork
exec
fork
exec
posix_spawn
问题是,fork
它本身可以在信号处理程序中调用。它排除了对 的任何重要使用pthread_atfork
,即使它的 RATIONALE 明确提到在子进程中解锁互斥锁和重新创建线程 (!)。
我认为仍然存在不同可能解释的“灰色地带”:
pthread_atfork
。fork
fork
调用周围发生的非 pthread-atfork 操作。但是对于便携式应用程序使用哪种读数非常清楚。
在 fork() 之后执行 exec() 是安全的,前提是互斥锁由将被 exec() 替换的程序拥有。如果互斥锁是库的一部分并且正在保护必须串行访问的资源,那么它应该调用 pthread_atfork() 来注册回调:
POSIX 标准将允许在 fork() 之后和 exec() 之前的系统调用类型限制为所谓的异步信号安全系统调用。创建线程未明确列为异步信号安全系统调用,因此 POSIX 不允许子进程在 fork() 和 exec() 之前创建线程。具有讽刺意味的是,解锁互斥锁也没有明确列为异步信号安全系统调用,如果 fork() 的意图是然后 exec( ) - 可能是标准的疏忽。