7

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

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

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

4

2 回答 2

8

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

更新:RATIONALE 部分是非规范性的,结果与标准的其他部分相冲突。有关更多详细信息,请参阅Dave Butenhof 的此缺陷报告

对于多线程程序的任何状态(即,任何持有任何互斥锁的线程),紧随exec其后应该是安全的。fork至于 和 之间的可能fork情况exec,情况很复杂:

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

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

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

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


经过更多研究后,我建议避免以任何方式依赖,并且只在andpthread_atfork之间进行异步信号安全调用(放弃/ for会更好)。forkexecforkexecposix_spawn

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

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

  1. 对于已知从不调用信号处理程序的程序中的处理程序pthread_atforkfork
  2. 对于不在信号处理程序中的fork调用周围发生的非 pthread-atfork 操作。

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

于 2013-01-18T21:34:51.627 回答
1

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

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

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

于 2013-01-18T21:37:59.887 回答