Multithreading 混合线程、fork和互斥体,我应该注意什么?

Multithreading 混合线程、fork和互斥体,我应该注意什么?,multithreading,fork,mutex,Multithreading,Fork,Mutex,如果我使用fork一个进程,其中一个线程持有互斥锁,那么如果我在子进程中立即执行exec,我是否相对安全?在Iexec之前,在孩子身上做什么事情是安全的 如果执行fork的线程在调用exec之前继续释放互斥锁,这会导致问题吗?如果我试图在父进程在fork之前拥有的子进程中获取互斥锁(可能拥有也可能不拥有),会发生什么情况 不同平台上的答案是否不同?我主要关注Unix变体,尤其是Linux。但我对NT很好奇。当然,据我所知,NT没有fork有关多线程环境中fork相关问题的讨论,请参见,尤其是基本

如果我使用
fork
一个进程,其中一个线程持有互斥锁,那么如果我在子进程中立即执行
exec
,我是否相对安全?在I
exec
之前,在孩子身上做什么事情是安全的

如果执行
fork
的线程在调用
exec
之前继续释放互斥锁,这会导致问题吗?如果我试图在父进程在
fork
之前拥有的子进程中获取互斥锁(可能拥有也可能不拥有),会发生什么情况

不同平台上的答案是否不同?我主要关注Unix变体,尤其是Linux。但我对NT很好奇。当然,据我所知,NT没有
fork

有关多线程环境中
fork
相关问题的讨论,请参见,尤其是基本原理部分。它还提供了一个提示,说明在子级和父级中,在
fork
之前和之后什么应该是有效的

更新:基本原理部分不规范,结果与标准的其他部分冲突。有关更多详细信息,请参见Dave Butenhof

fork
之后立即
exec
对于多线程程序的任何状态(即,持有任何互斥锁的任何线程)都是安全的。至于
fork
exec
之间可能发生的事情,情况很复杂:

最重要的是在子进程中只复制一个线程(称为
fork
)。因此,在
fork
时刻由另一个线程持有的任何互斥锁都将被永久锁定。也就是说(假设非进程共享互斥体),它在子进程中的副本被永久锁定,因为没有线程来解锁它

如果可能,在
fork
之后释放互斥锁是安全的,也就是说,如果
fork
ing线程首先拥有互斥锁。这就是
pthread\u atfork
处理程序通常的工作方式:在
fork
之前锁定互斥体,在子级解锁,在父级解锁

在获取fork之前进程拥有的互斥锁时(请记住,我们讨论的是子级地址空间中的一个副本):如果它属于
fork
ing线程,则它是递归锁定(适用于
PTHREAD\u mutex\u recursive
);如果它被另一个线程拥有,它将永远被锁定,无法重新获取

通过注册适当的
pthread\u atfork
处理程序,第三方库可以保证在
fork
exec
之间安全使用。(我希望它主要来自编程语言运行时,而不是通用库)


经过进一步的研究,我建议不要以任何方式依赖
pthread_atfork
,而只在
fork
exec
之间进行异步信号安全调用(放弃
fork
/
exec
for
posix_spawn
会更好)

问题是,
fork
本身可以在信号处理程序中调用。它排除了对
pthread_atfork
的任何非常规使用,即使其基本原理明确提到在子进程中解锁互斥锁和重新创建线程(!)

我认为不同解释的“灰色地带”仍然存在:

  • 对于已知从未在信号处理程序中调用
    fork
    的程序中的
    pthread\u atfork
    处理程序
  • 对于非pthread atfork操作,发生在不在信号处理程序中的
    fork
    调用周围
  • 但是,对于便携式应用程序,使用哪种读取是非常清楚的。

    在fork()之后执行exec()是安全的,前提是互斥锁由将被exec()替换的程序拥有。如果互斥体是库的一部分,并且正在保护必须串行访问的资源,那么它应该调用pthread_atfork()来注册回调:

  • 在fork本身之前以及在进行fork()系统调用的线程上下文中调用的函数。通常,此函数将获取保护关键部分的互斥锁,从而保证在fork期间没有线程位于关键部分内
  • 在创建子进程之后但在fork()系统调用返回之前,在调用fork()的线程上下文中调用的函数。然后,此函数可以解锁父进程中的互斥锁
  • 如果/当进程分叉时,在子进程线程上下文中调用的函数-在创建子进程之后,但在fork()系统调用返回之前调用该函数。然后,子进程可以解锁其互斥体副本
  • POSIX标准将fork()之后和exec()之前允许的系统调用类型限制为所谓的异步信号安全系统调用。创建线程没有明确列为异步信号安全系统调用,因此POSIX不允许子进程在fork()之后和exec()之前创建线程。具有讽刺意味的是,解锁互斥锁也没有明确列为异步信号安全系统调用,而且如果fork()的目的是执行(),则子进程中fork()之后也不严格允许使用refer-这可能是标准中的一个疏忽