尝试运行此代码
if (!dlopen("../lib/libMy.so", RTLD_NOW)) {
perror("dlopen FAILED!");
return 1;
}
导致此 TLS(线程本地存储)错误:
无法在静态 TLS 块中分配内存
转储线程局部存储部分
.tbss
(静态和非静态未初始化线程局部变量)显示Tls_ErrorStruct
大小为600字节(0x258),Tls_tv
为1600字节(0x640),总共2200个TLS字节:
$ objdump -C -t ../lib/libMy.so | grep -F '.tbss'
00000000000008e8 l .tbss 0000000000000008 runtime.tlsg
0000000000000010 l .tbss 0000000000000001 __tls_guard
0000000000000688 l .tbss 0000000000000001 __tls_guard
...
0000000000000690 g .tbss 0000000000000258 Tls_ErrorStruct
...
0000000000000018 g .tbss 0000000000000640 Tls_tv
为了修复块错误,这些
thread_local
变量被减少到约 600 字节。 dlopen
现在成功了。
我不确定如何找到动态链接器线程本地存储块大小,但确实找到了可能的参考here:
/* Size of the static TLS block. Giving this initialized value
preallocates some surplus bytes in the static TLS area. */
size_t _dl_tls_static_size = 2048;
是否有linux工具或一些C++方法来确定最大值 线程本地存储块大小?
有没有办法计算线程本地存储的总使用量 对于共享对象以确保它在系统的限制之内?
从技术上讲,这不是一个答案,因为我不想尝试这个,只是对可能的修复方案的一个想法。是的,当涉及到不影响我的事情时,我就是这么懒。
对于初学者来说,虽然我们在
pthread
tls API 中拥有有限数量的“线程本地键”,但该 API 并未被使用,所以这并不重要。相反,Linux 使用 ELF 部分作为其线程本地存储,这没有任何限制。
您遇到的限制来自于
dlopen()
打开初始加载程序未见过的模块。现在,您发现的 _dl_tls_static_size
符号似乎可以在某种程度上处理这种情况,只需让加载程序分配更多的内存即可。但是,跟踪该符号,您会发现以下评论:
/* That is the size of the TLS memory for this object. The initialized
value of _dl_tls_static_size is provided by dl-open.c to request some
surplus that permits dynamic loading of modules with IE-model TLS. */
这里突出的词是“IE模型”,表明可能存在其他不需要这个的“模型”。如果您查看
gcc
选项,您会发现:
-ftls-model=model
Alter the thread-local storage model to be used (see Thread-Local Storage). The model argument should be one of ‘global-dynamic’, ‘local-dynamic’, ‘initial-exec’ or ‘local-exec’. Note that the choice is subject to optimization: the compiler may use a more efficient model for symbols not visible outside of the translation unit, or if -fpic is not given on the command line.
The default without -fpic is ‘initial-exec’; with -fpic the default is ‘global-dynamic’.
是的,我们有一个模型
initial-exec
,它可以很容易地缩写为 IE
,这就是我们在评论中看到的。
因此,如果您可以控制此共享对象的编译方式,那么可能值得尝试 global-dynamic
。