【问题标题】:Mixing threads, fork, and mutexes, what should I watch out for?混合线程、fork 和互斥体,我应该注意什么?
【发布时间】:2013-01-02 16:08:02
【问题描述】:

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

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

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

【问题讨论】:

    标签: multithreading fork mutex


    【解决方案1】:

    参见pthread_atfork,尤其是RATIONALE 部分,讨论多线程环境中与fork 相关的问题。它还提示了孩子和父母在fork 之前和之后应该有效的内容。

    更新:RATIONALE 部分是非规范性的,结果与标准的其他部分发生冲突。有关更多详细信息,请参阅 Dave Butenhof 的this defect report

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

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

    在可能的情况下,在fork 之后释放互斥锁是安全的,也就是说,如果forking 线程首先拥有互斥锁。这就是pthread_atfork 处理程序通常的工作方式:在fork 之前锁定互斥锁,在子进程中解锁在父进程中解锁。

    在 fork 之前获得进程拥有的互斥体(请记住,我们讨论了子地址空间中的 副本):如果它是由@987654335 拥有 @ing 线程,它是递归锁定(适用于PTHREAD_MUTEX_RECURSIVE);如果它被另一个线程拥有,它会永远锁定并且无法重新获取。

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


    经过更多研究,我建议避免以任何方式依赖pthread_atfork,并且在forkexec 之间只做异步信号安全调用(放弃@ 987654343@/exec for posix_spawn 会更好)。

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

    我认为仍然存在不同可能解释的“灰色区域”:

    1. 对于一个程序中的pthread_atfork 处理程序,已知永远不会在信号处理程序中调用fork
    2. 对于在信号处理程序中不是fork 调用周围发生的非pthread-atfork 操作。

    但是对于便携式应用程序使用哪种读数非常清楚。

    【讨论】:

      【解决方案2】:

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

      1. 在 fork 本身之前以及在进行 fork() 系统调用的线程的上下文中调用的函数。通常,此函数将获取保护临界区的互斥锁,从而保证在分叉期间没有线程位于临界区内
      2. 要在调用 fork() 的线程上下文中调用的函数,在创建子进程之后但在 fork() 系统调用返回之前。然后,此函数可以解锁父进程中的互斥锁。
      3. 如果/当进程分叉时在子进程的线程上下文中调用的函数 - 它在子进程创建之后但在 fork() 系统调用返回之前被调用。然后子进程可以解锁它的互斥体副本。

      POSIX 标准将允许在 fork() 之后和 exec() 之前的系统调用类型限制为所谓的异步信号安全系统调用。创建线程未明确列为异步信号安全系统调用,因此 POSIX 不允许子进程在 fork() 和 exec() 之前创建线程。具有讽刺意味的是,解锁互斥锁也没有明确列为异步信号安全系统调用,并且如果 fork() 的意图是然后 exec( ) - 可能是对标准的疏忽。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-10-08
        • 2013-10-10
        • 2016-12-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-05-06
        相关资源
        最近更新 更多