gcc:编译为 BSS 的线程局部变量

gcc: Thread-local variable compiled as BSS

我是新手,正在测试 线程本地存储 (TLS) class gcc(版本 4.8.2)在我的 Ubuntu 具有 i686/32 位架构的 14.04 计算机。

为了找出 __thread 关键字是否具有预期的效果,我用 gcc test.c 编译了这个简约的测试程序(没有错误或警告):

#include <stdio.h>

__thread int i;

int main() {
  i = 7;
  printf("%d\n",i);
}

并使用工具nm查看目标代码中符号i的存储class:

nm a.out | grep ' i'

结果是

00000000 B i

这意味着i被当作一个公共的全局未初始化变量(存储在BSS部分)。根据 man nm,线程本地存储变量由字母 L 表示,而不是 B.

这是怎么回事?

这是nm问题还是真正的问题?

您的代码太少了。直到运行时才知道线程数,因此您在可执行文件中看到的变量就是 main 将使用的变量。创建附加线程时,将分配该变量的附加副本。

这是一个演示线程变量的最小程序。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

__thread int i;

void *foo( void *args )
{
    i = 8;
    printf( "foo: %d\n", i );
    return NULL;
}

int main( void )
{
    i = 7;
    printf( "main:%d\n", i );

    pthread_t pid;
    if ( pthread_create( &pid, NULL, foo, NULL ) != 0 )
        exit( 1 );

    pthread_join( pid, NULL );
    printf( "main:%d\n", i );
}

没问题,就是nm(1)写输出的方式。

nm(1) 的默认输出格式(和信息)在平台之间是不同的(例如 nm(1) 在我的 Linux 桌面上的联机帮助页甚至没有提到 L 用于线程本地存储)。

但是,如果您使用 -fs 启用 SysV 输出格式,您会得到更详细的输出:

$ nm -fs a.out
Symbols from a.out:

Name                  Value           Class        Type         Size             Line  Section

...

i                   |0000000000000000|   B  |               TLS|0000000000000004|     |.tbss
...

如您所见,使用此输出格式 iType 列下被识别为本地线程,它位于 .tbss.

如果您的发行版的联机帮助页提到线程本地存储的 L 标志,而您没有在默认输出格式中看到它,我会说这是 nm(1) 中的错误.