好的,说我有
__thread int myVar;
然后我将 &myVar 从一个线程传递到另一个……如果数据是真正的“本地”,那么 1 个线程的 TLS 存储可能不会映射到其他线程的地址空间,事实上,您可能会争论它不应该是。这将导致 SIGSEGV 或其他东西。但是,系统可以将相同的地址映射到不同的页面。这是 Linux 对 .tbss/.tdata 所做的吗?在那种情况下,传递变量的地址会给你错误的变量地址!你会得到你自己的本地副本,而不是你试图传递的副本。或者,是否所有内容都共享并映射到不同的虚拟地址 - 允许您传递 __thread 变量的地址?
显然,如果有人试图通过传递其地址来将线程本地存储传递给另一个线程,则应该受到殴打和鞭打。还有一百万种其他方法 - 例如复制到任何其他变量!但是,我很好奇是否有人知道..
- 官员描述了这种情况下的行为
- 当前的 GCC/Linux 实现细节
-- 埃文
最佳答案
至少对于 x86,TLS 是使用段寄存器执行的。默认段寄存器 %ds
隐含在寻址内存的指令中。访问 TLS 时,线程使用另一个段寄存器 - %gs
用于 i386 和 %fs
用于 x86-64 - 在调度线程时保存/恢复,就像其他寄存器处于上下文切换中。
所以一个进程范围的变量可以通过类似的方式访问:
mov (ADDR) -> REG ; load memory `myVar` to REG.
这是隐含的:
mov %DS:(ADDR) -> REG
对于 TLS,编译器生成:
mov %FS:(ADDR) -> REG ; load thread-local address `myVar` to REG.
实际上,即使变量的地址在不同的线程中看起来是相同的,例如,
fprintf(stdout, "%p\n", & myVar); /* in separate threads... */
事实上每个线程都使用不同的段寄存器值,这意味着它们映射到物理内存的不同区域。
Windows(它可能会互换 %fs
和 %gs
的角色 - 不确定)和 OS X 使用相同的方案。至于其他架构,有一个深入的technical guide到 ELF ABI 的 TLS。它缺少对 ARM 架构的讨论,但包含有关 IA-64 和 Alpha 的详细信息,因此它显示了它的年代。
关于linux - 线程本地存储变量的地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24793556/