【发布时间】:2010-11-19 16:55:47
【问题描述】:
在大多数 POSIX 线程实现中,新创建的线程需要进行一些初始化,然后才能处于能够运行应用程序代码的一致状态。这可能涉及解锁线程结构中的锁,在使用一个的实现中初始化“线程寄存器”,初始化线程本地数据(编译器级 TLS 或 POSIX 线程特定数据)等。我找不到明确的保证所有这些初始化将在线程可以接收任何信号之前完成;我能找到的最接近的是 2.4.3:
下表定义了一组异步信号安全的函数。因此,应用程序可以不受限制地从信号捕获函数中调用它们:
...
据推测,其中一些函数(至少 fork,它必须检查由 pthread_atfork 函数建立的全局状态)依赖于处于一致的初始化状态的线程。
困扰我的一件事是我已经阅读了很多 glibc/nptl 源代码,但找不到任何显式同步来阻止新创建的线程在完全初始化之前对其进行处理。我希望调用pthread_create 的线程在调用clone 之前阻塞所有信号,并让新线程在初始化完成后解除对它们的阻塞,但我找不到任何具有这种效果的代码,也没有在@ 中看到它987654325@输出。
【问题讨论】:
-
哇,当
pthread_atfork()处理程序已设置时,从信号处理程序调用fork()...您真的必须知道自己在做什么(并相信你的图书馆实现)!特别是如果(通常情况下)prefork 处理程序抓取一堆锁以确保它们表示的数据在 fork 之前是一致的 - 原则上可以持有这些锁中的任何一个(或者更糟糕的是,在被获取的过程中)由线程处理信号,这意味着数据是不可挽回的不一致(或者进程可能死锁!)。一切都很有趣:-) -
好吧,
fork被列为异步信号安全函数之一,但我同意pthread_atfork注册函数可以做的几乎所有有用的事情都不是异步的信号安全。仍然有一些有效的用途,例如,如果您的pthread_atfork处理程序只是用固定值重新初始化数据,破坏控制互斥体并初始化新的互斥体(当然都是在子进程中)。 -
更令人不安的是,如果一个库(不知道被调用应用程序线程化;甚至可能作为另一个库的依赖项间接动态加载)设置了
pthread_atexit处理程序,而这些处理程序不是异步信号安全。调用应用程序可以期望fork是异步信号安全的(如文档所述)并从信号处理程序中调用它。我想我通过这个思想实验得到的结果是,它暴露了引入创建的线程使用透明度模型pthread_atfork中的一个缺陷。 -
完全同意您的两位 cmets - 只是为了强化我的观点,即您真的需要在冒险进入那些浑浊的水域之前知道自己在做什么 :-)
-
Modulo 一次不小心输入了
pthread_atexit而不是pthread_atfork.. :-)
标签: c linux pthreads posix signals