C++-线程局部存储TLS是如何实现的?

C++-线程局部存储TLS是如何实现的?

清晨说ぺ晚安 发布于 2017-11-04 字数 92 浏览 1258 回复 2

在windows和linux下,都可以使用TLS,请问具体是怎么实现的?使用起来有什么需要注意的吗?

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

想挽留 2017-11-07 2 楼

线程局部存储在不同的平台有不同的实现,可移植性不太好。但线程局部存储实现并不难,最简单的办法就是建立一个全局表,通过当前线程ID去查询相应的数据,因为各个线程的ID不同,查到的数据自然也不同了。
但Windows系统采用了每个线程建线程专享的索引表,表的条目为线程局部存储的地址。每个线程创建时系统给它分配一个LPVOID指针的数组(叫做TLS索引数组),这个数组从C编程角度是隐藏着的不能直接访问(实际上该数组地址写入了线程信息块 thread information block,缩写TIB或TEB),需要通过一些Kernel32 API函数调用访问。

瑾兮 2017-11-06 1 楼

对于Windows系统来说,正常情况下一个全局变量或静态变量会被放到".data"或".bss"段中,但当我们使用__declspec(thread)定义一个线程私有变量的时候,编译器会把这些变量放到PE文件的".tls"段中。当系统启动一个新的线程时,它会从进程的堆中分配一块足够大小的空间,然后把".tls"段中的内容复制到这块空间中,于是每个线程都有自己独立的一个".tls"副本。所以对于用__declspec(thread)定义的同一个变量,它们在不同线程中的地址都是不一样的。

我们知道对于一个TLS变量来说,它有可能是一个C++的全局对象,那么每个线程在启动时不仅仅是复制".tls"的内容那么简单,还需要把这些TLS对象初始化,必须逐个地调用它们的全局构造函数,而且当线程退出时,还要逐个地将它们析构,正如普通的全局对象在进程启动和退出时都要构造、析构一样。

Windows PE文件的结构中有个叫数据目录的结构,我们在第2部分已经介绍过了。它总共有16个元素,其中有一元素下标为IMAGE_DIRECT_ENTRY_TLS,这个元素中保存的地址和长度就是TLS表(IMAGE_TLS_DIRECTORY结构)的地址和长度。TLS表中保存了所有TLS变量的构造函数和析构函数的地址,Windows系统就是根据TLS表中的内容,在每次线程启动或退出时对TLS变量进行构造和析构。TLS表本身往往位于PE文件的".rdata"段中。

更详细的描述