我正在实现一个非常轻量的pthread替换库.为什么要完全禁用__thread有几个原因.
>这是浪费内存.如果我要创建一千个与使用__thread声明变量的上下文无关的线程,它们仍将分配程序,但仍将分配该数据字节大小的1000 *,并且从不使用它.它根本不是与大容量并发模型兼容的内存.如果我们需要只有8K堆栈的极轻量级的光纤,那么只有4K的TLS块将占用每个线程所用内存的50%.在某些情况下,TLS开销会很大.
> TLS是一个复杂的标准,我根本没有时间/资源来支持它.太贵了.我个人认为该标准设计不当.它应该定义了链接程序必须提供的标准功能,以便线程库可以控制TLS分配的位置,并插入相关的偏移量和所需的地址.同样,标准ELF实现也感染了pthread,希望pthread大小的结构能够计算偏移量,从而使其很难适应其他东西.
>这只是一个糟糕的模式.它鼓励使用全局变量并创建具有静态函数/副作用的函数.如果我们正在创建易于分析的正确程序,那么这不是我们想要的领域.
>如果我们真的需要用于跟踪幕后线程状态的魔术的“线程上下文”(例如分配或取消跟踪),为什么不仅仅公开TLS用于首先了解该上下文的魔术呢?我个人将直接使用%fs注册.由于明显的原因,这在库中是不可能的,但是为什么它们从一开始就应该具有线程意识?为什么不正确地设计它们,以便它们在参数列表中首先获得所需的上下文相关数据?
我的问题很简单:如果您不小心使用__thread支持并让clang发出错误,最简单的方法是什么?如果加载刚需要TLS的动态库,如何得到错误?
解决方法:
我相信最简单的方法是无条件地向CFLAGS中添加这样的内容(如果您希望它是系统全局的,则可能来自clang的gcc specfile等效文件):
-D__thread='^-^'
在C程序的任何位置,右侧可以是语法上无效的任何东西(违反约束).
为了防止使用TLS加载库,您必须修补链接程序和/或动态链接程序以拒绝它们.如果您只是在谈论dlopen,则程序可以首先读取文件并解析TLS重定位的ELF标头,然后拒绝该库(不将其传递给dlopen)(如果有).甚至可以使用LD_PRELOAD包装器来实现.
我同意您的看法,特别是在当前实施中,TLS是应该避免使用的一种方法,但是请问您是否已衡量成本?我认为,在旨在使用它的系统上将其完全淘汰非常困难,而且还有很多悬而未决的东西可以减少膨胀.您正在使用哪个libc?如果是glibc,我可以肯定的是,这些天glibc本身在内部使用了很多TLS …当然,如果您正在编写自己的线程实现,则将需要与标准库的其余部分进行大量交互,所以也许您已经在修补它…?
顺便说一下(后面是无耻的插件),我们在musl libc中有an extremely light-weight threads implementation,目前没有TLS.我认为与另一个libc集成并不容易(而且我敢肯定,如果您正在编写自己的libc,您将发现与glibc集成非常困难,尤其是glibc的动态链接器,它希望支持TLS),但是如果您可以按原样使用整个库,则它可能满足您对特定项目的需求,或者可以借用有用的代码(许可证为MIT).