ILD

per thread variable
作者:Yuan Jianpeng 邮箱:yuanjp89@163.com
发布时间:2024-5-9 站点:Inside Linux Development

每线程变量,官方叫法是:Thread-local storage (TLS)。这种变量每个线程都会有一个实例,很多场景需要每线程变量,最典型的就是errno。


每线程变量,使用__thread关键字定义,这个关键字不是c语言标准定义的,而是业界(编译器开发者)根据实际需要扩展的。


下面例子,主线程和子线程,变量a的地址不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ cat test.c
#include <pthread.h>
#include <stdio.h>
 
static __thread int a;
 
static void *start_routine(void *arg)
{
        printf("child thread %p\n", &a);
}
 
int main(int argc, char **argv)
{
        pthread_t tid;
        printf("main thread %p\n", &a);
        pthread_create(&tid, NULL, start_routine, NULL);
        pthread_join(tid, NULL);
        return 0;
}
 
$ cc test.c -lpthread
$ ./a.out
main thread 0x7f438288b73c
child thread 0x7f438288a6fc


__thread的支持,不仅需要编译器(gcc),还需要linker (ld),dynamic linker loader (ld.so),系统库(libc.so libpthread.so)的支持。而且需要elf格式的扩展支持(见参考2)


对每线程变量取地址,并共享给其它线程是合法的,但是地址的生命周期,在线程结束后也跟着结束了。


在x64系统上,没线程变量,依赖于segment register实现,

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
$ cat test.c
 
static __thread int a;
 
void *addr()
{
        return &a;
}
 
$ cc -S test.c -Os
$ cat test.s
        .file   "test.c"
        .text
        .globl  addr
        .type   addr, @function
addr:
.LFB0:
        .cfi_startproc
        endbr64
        movq    %fs:0, %rax
        addq    $a@tpoff, %rax
        ret
        .cfi_endproc
.LFE0:
        .size   addr, .-addr
        .section        .tbss,"awT",@nobits
        .align 4
        .type   a, @object
        .size   a, 4
a:
        .zero   4
        .ident  "GCC: (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0"
        .section        .note.GNU-stack,"",@progbits
        .section        .note.gnu.property,"a"
        .align 8
        .long    1f - 0f
        .long    4f - 1f
        .long    5
0:
        .string  "GNU"
1:
        .align 8
        .long    0xc0000002
        .long    3f - 2f
2:
        .long    0x3
3:
        .align 8
4:


可以看到访问a,使用了%fs寄存器


参考:

GCC manual. 6.68 Thread-Local Storage

https://gcc.gnu.org/onlinedocs/gcc/Thread-Local.html


ELF Handling For Thread-Local Storage

https://www.akkadia.org/drepper/tls.pdf


https://stackoverflow.com/questions/10810203/what-is-the-fs-gs-register-intended-for


Copyright © linuxdev.cc 2017-2024. Some Rights Reserved.