4

我正在实现一个非常轻量级的 pthread 替换库。我想完全禁用 __thread 有几个原因。

  1. 这是浪费内存。如果我要创建一千个线程,它与用 __thread 声明变量的上下文无关, 它们仍将分配 程序仍将分配1000* 数据字节的大小并且从不使用它。它与大规模并发模型根本不兼容。如果我们需要只有 8K 堆栈的极轻量级光纤,那么只有 4K 的 TLS 块将是每个线程使用的内存的 50% 的开销。在某些情况下,TLS 开销会很大。
  2. TLS 是一个复杂的标准,我根本没有时间/资源来支持它。太贵了 我个人认为这个标准设计得很糟糕。它应该定义了必须由链接器提供的标准函数,以便线程库可以控制 TLS 分配发生的位置并插入所需的相关偏移量和地址。标准的 ELF 实现也被 pthread 感染了,期望 pthread 大小的结构来计算偏移量,这使得它很难适应其他东西。
  3. 这只是一个糟糕的模式。它鼓励使用全局变量并创建具有静态函数/副作用的函数。如果我们正在创建易于分析的正确程序,这不是我们想要进入的领域。
  4. 如果我们真的需要“线程上下文”来实现一些在幕后跟踪线程状态的魔法(比如分配或取消跟踪),为什么不首先公开 TLS 用来理解该上下文的魔法呢?就我个人而言,我只是%fs直接使用寄存器。由于显而易见的原因,这在库中是不可能的,但为什么他们一开始就应该知道线程呢?为什么不正确地设计它们,以便它们首先在参数列表中获得所需的上下文相关数据?

我的问题很简单:如果您不小心使用了 __thread 支持并让 clang 发出错误,最简单的方法是什么?如果我加载恰好需要 TLS 的动态库,我怎么会出错?

4

1 回答 1

3

我相信最简单的方法是无条件地将这样的内容添加到您的 CFLAGS 中(如果您希望它是系统全局的,可能来自 clang 的等效 gcc 规范文件):

-D__thread='^-^'

其中右侧可以是 C 程序中任何时候在语法上无效的任何内容(违反约束)。

至于防止使用 TLS 加载库,您必须修补链接器和/或动态链接器以拒绝它们。如果您只是在谈论dlopen,您的程序可以首先读取文件并解析 ELF 标头以进行 TLS 重定位,然后拒绝该库(不将其传递给dlopen)(如果有)。这甚至可以通过LD_PRELOAD包装器实现。

我同意你的观点,尤其是在其当前的实现中,通常应该避免使用 TLS,但请问你是否衡量了成本?我认为在设计用于使用它的系统上完全消除它是相当困难的,并且有很多低垂的果实可以减少臃肿。你用的是哪个libc?如果它是 glibc,我很确定 glibc 有很多 TLS,它现在在内部使用它......当然,如果你正在编写自己的线程实现,那将需要与标准库的其余部分进行大量交互,所以也许你已经在修补它了......?


顺便说一下(无耻的插件如下),我们在musl libc中有一个非常轻量级的线程实现,目前没有 TLS。但如果您可以按原样使用整个库,它可能会满足您对特定项目的需求,或者有可以借用的有用代码(许可证是 MIT)。

于 2012-09-22T12:05:10.930 回答