线程本地存储变量的地址

Eva*_*ois 4 linux gcc thread-local-storage

好吧,说我有

__thread int myVar;
Run Code Online (Sandbox Code Playgroud)

然后我将&myVar从一个线程传递到另一个线程...如果数据真的是"本地",那么1个线程的TLS存储可能无法映射到其他线程的地址空间,事实上,你可能会认为它不应该不会.这会导致SIGSEGV或其他东西.但是,系统可以将相同的地址映射到不同的页面.这是Linux用.tbss/.tdata做的吗?在这种情况下,传递变量的地址将为您提供错误变量的地址!您将获得自己的本地副本,而不是您尝试传递的副本.或者,是否所有内容都共享并映射到不同的虚拟地址 - 允许您传递__thread vars的地址?

显然,应该通过传递其地址来尝试将线程本地存储传递到另一个线程来殴打和鞭打.还有一百万种其他方式 - 例如复制到任何其他变量!但是,如果有人知道,我很好奇.

  1. 这位官员描述了这种情况下的行为
  2. 目前的GCC/Linux实现细节

- 埃文

Bre*_*ale 6

至少对于x86,使用段寄存器执行TLS.默认段寄存器%ds隐含在寻址存储器的指令中.当访问TLS时,线程使用另一个段寄存器 - %gs对于i386和%fsx86-64 - 在调度线程时保存/恢复,就像其他寄存器在上下文切换中一样.

因此,可以通过以下方式访问流程范围的变量:

mov (ADDR) -> REG ; load memory `myVar` to REG.
Run Code Online (Sandbox Code Playgroud)

这隐含着:

mov %DS:(ADDR) -> REG
Run Code Online (Sandbox Code Playgroud)

对于TLS,编译器生成:

mov %FS:(ADDR) -> REG ; load thread-local address `myVar` to REG.
Run Code Online (Sandbox Code Playgroud)

实际上,即使变量的地址在不同的线程中看起来是相同的,例如,

fprintf(stdout, "%p\n", & myVar); /* in separate threads... */
Run Code Online (Sandbox Code Playgroud)

事实上,每个线程对段寄存器使用不同的值,这意味着它们映射到物理存储器的不同区域.如果要将地址从一个线程传递到另一个线程,则无法访问它在第一个线程中表示的内存- 另一个段寄存器值在第二个线程中生效.

Windows使用相同的方案(它可以交换%fs%gs- 不确定)和OS X 的角色.对于其他架构,有一个针对ELF ABI的TLS 的深入技术指南.它缺少对ARM体系结构的讨论,并且有关于IA-64和Alpha的详细信息,因此它显示了它的年龄.