ZHCADC4A September 2011 – March 2014
如果线程可以使用具有静态存储持续时间且特定于线程的变量,则复杂的多线程程序在结构上可以更加清晰,开发也更加容易。也就是说,其他线程无法看到或访问此类静态存储持续时间且特定于线程的变量。考虑以下 C 代码:
int global_x;
foo() {
int local_x;
static int static_x = 0;
...
}
global_x
和 static_x
变量为每个进程分配一次,并且所有线程均共享同一个实例。相比之下,local_x
是从堆栈分配的。由于每个线程都具有自己的堆栈,因此变量 local_x 特定于线程,而 static_x 则不是。但是,目前尚无简单的方法能够根据每个线程来定义全局/静态变量。POSIX 线程接口允许使用 pthread getspecific
和 pthread setspecific
创建特定于线程的静态存储变量。但该接口使用不方便。
为了解决此问题,我们引入了线程局部存储 (TLS),这是一个存储类,允许程序定义具有静态存储持续时间且特定于线程的变量。TLS 变量或“线程局部变量”是一个每线程实例化一次的全局/静态变量。
用于 TLS 的存储器在整个程序运行期间都是静态分配的。每个线程都有自己的所有线程局部变量(甚至那些它没有声明或使用的变量)实例,这些变量由创建线程时加载的所有动态模块定义。创建线程后,其 TLS 块由底层操作系统 (OS) 线程支持库分配和初始化。如果线程完成,然后在同一程序运行中再次运行,则线程的 TLS 块将重新初始化。如果线程暂停或被其他线程阻止,然后在解除阻止后恢复执行,则 TLS 变量不会重新初始化。
访问 TLS 变量的方式取决于 OS 或 RTOS 如何为每个线程创建和管理线程局部存储。Linux 系统需要支持对多个动态库和运行时使用 dlopen() 加载的库进行 TLS 分配。此外,Linux 系统可能要求仅在访问线程本地时才缓慢地分配 TLS 存储。这需要复杂的 TLS 存储管理,并影响线程本地的访问方式。另一方面,包含 RTOS 的静态可执行文件只需管理单个 TLS 块,访问可以很简单。
在概述线程本地概念之后,本文档介绍了如何在源代码中指定线程本地,以及如何在 ELF 目标文件(节 7.4)中表示线程本地。然后,本文档介绍了如何针对 C6x Linux、静态可执行文件和裸机动态链接 TLS 模型(节 7.5)访问线程本地,以及如何解析对线程局部变量的弱引用。(节 7.6)。
C6000 TLS 机制基于行业通用约定,例如 Ulrich Drepper 编写的线程局部存储的 ELF 处理白皮书中所述的机制。