线程局部存储,Part 1:概述

原文网址:http://www.nynaeve.net/?p=180

线程局部存储,Part 1:概述

和其它主流多线程操作系统一样,Windows为大家提供一个机制,该机制允许程序员实现基于线程的局部状态存储。这种能力通常称为线程局部存储(Thread Local Storage,TLS),这对于那些需要保存线程相关信息但需要全局可见的应用场景非常有用。

尽管TLS的介绍有很好的文档可参考,但关于其实现细节的介绍并不多(尽管也有一些非官方文档进行比较表面的介绍)。

至少从高层来将,TLS在概念上并不复杂。常规设计是将所有对TLS的访问都通过TEB中的指针来进行间接访问,TEB为操作系统定义的每个线程一份的数据结构,用于保存一些线程相关的信息。

TEB相对有比较多的文档介绍,为了对用户透明,一般使用一个段寄存器(X86上使用fs,X64上使用gs)指向当前线程的TEB地址。这样通过fs:[0x0](gs:[0x0] X64)将始终访问当前线程的TEB结构。TEB实际可以不出现在进程的平坦地址空间当中(TEB有一个域存放本身的平坦线性地址_TEB._NT_TIB.Self),但是这里段机制仅仅用来提供快速访问TEB结构,从而不需要搜索线程ID来确定TEB的地址(或其它相对较慢的机制来查找线程对应的TEB地址)。(%我猜作者这里想表达的意思是TEB并不需要在平坦地址空间中出现,所以想通过线性地址访问TEB会出错%)

在非X86和非X64架构中,访问TEB的底层机制是不同的,但是通常也是使用寄存器存放TEB的地址,从而使其更易于访问。

TEB本身可算是Windows中文档化最好的未公开结构,这主要是因为在最近构建的ntdll和ntoskrnl中该结构为调试器提供了一些类型信息(type information)。通过这些信息和一点反汇编工作,即可很容易的理解TLS的背后的实现细节。

在着手了解TLS如何工作之前,有必要看一下其文档化的使用方法。首先是通过kernel32中的一组用于显示使用TLS的函数:TlsGetValue、TlsSetValue、TlsAlloc、TlsFree。这些函数的使用非常直观。TlsAlloc为所有线程预留一个指针大小的空间,TlsGetValue用于读取线程相关的变量。(其它两个函数完成功能类似)。

其次,是由加载器、编译器和链接器所支持的隐式使用线程局部存储的方法,方法是在变量上加__declspec(thread)修饰。这比使用TLS APIs要方便的多,因为不需要每次都使用TLS函数去访问线程局部存储变量。这种方式同样解除了需要显示调用TlsAlloc和TlsFree的困扰,提供了一种有效的使用线程局部存储的方式(隐式TLS通过分配一大块内存来实现,内存大小由所有线程局部变量占用空间总和,对于一个模块中的所有线程局部变量仅需要在TLS数组中的一个索引就可以了)。

既然隐式TLS具有这些优点,那为何我们需要显示TLSAPI了?原因是在Vista之前,在加载器的TLS实现中含有一些讨厌的限制。尤其是隐式TLS在模块不是在进程初始化时载入会不起作用(即不能动态加载或延迟加载)。这意味着在实际应用当中除了exe文件和保证会静态连接的dll库,其它都无法使用。

 

下回:近观显式TLS和其工作的机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值