windows线程局部栈

TLS

本文内容引述《windows核心编程》
TLS Thread Local Storage

需求

软件开发中,需要将数据与一个对象的实例关联起来。如使用SetWindowWord和SetWindowLong函数,将数据与某个指定窗口关联起来。
同样,我们可以用TLS将数据与指定的线程关联起来。例如,获知线程的运行时长。
而C/C++库中也实现了这些内容,比如_tcstok_s,asctime,gmtime等。

动态TLS

windows管理TLS的内部数据结构
TLS内部数据结构
系统中每个进程都有一组正在使用标志,每个标志可以被设置为FREE或者INUSE,表示TLS元素是否正在使用。
应用程序有一组4个函数,可以使用动态TLS,分别是

// 分配tls
DWORD TlsAlloc();
// 置值
BOOL TlsSetValue(DWORD dwTlsIndex, PVOID pvTlsValue);
// 取值
PVOID TlsGetValue(DWORD dwTlsIndex);
// 释放tls
BOOL TlsFree(DWORD dwTlsIndex);

当系统创建一个线程的时候,会分配TLS_MINUMUM_AVALIABLEPVOID值,将他们都初始化为0,并与线程关联起来。每个线程都有自己的PVOID数组,数组中的每个PVOID可以保存任意值。
函数TlsAlloc会让系统对进程的位标志进行检索并找到一个Free标志,然后将其改成INUSE并返回索引。该值会在整个进程范围内使用,所以使用全局变量比较好。
如果TlsAlloc返回的索引为3,就是说索引3已经被我预定了,无论是进程中当前正在运行的线程,还是今后会创建的线程,都不能再使用索引3了。

使用动态TLS

通常,如果DLL要使用TLS,会在DllMain函数处理DLL_PROCESS_ATTACH的时候调用TlsAlloc,在DllMain处理DLL_PROCESS_DETACH的时候调用TlsFree。
向应用程序中添加TLS的一种方法是直到需要的时候才添加。

DWORD g_dwTlsIndx;
void MyFunction(PSOMESTRUCT pSomeStruct) {
	if(pSomeStruct != NULL) {
		if(TlsGetValue(g_dwIndex) == NULL) 
			TlsSetValue(g_dwTlsIndex, HeapAlloc(GetProcessHeap(), 0, sizeof(*pSomeStrcut));
		memcpy(TlsGetValue(g_dwTlsIndex), pSomeStruct, sizeof(*pSomeStruct));
	}
	else {
		pSomeStruct = (PSOMESTRUCT)TlsGetValue(g_dwIndex);
	}
}

应用程序可能会动态链接到多个DLL,初始分配的0x40个TLS可能不够。如果不够用,windows会在需要的时候动态分配TLS元素。

TlsAlloc在返回之前,会遍历进程的每个线程,并根据新分配的索引,在每个线程的数组中把对应的元素设置为0.

静态TLS

静态TLS也将数据与线程关联起来,但是由于使用的时候不必在代码中调用任何函数,因此静态TLS更容易使用。

__declspec(thread) DWORD gt_dwStartTime = 0;

declspec(thread)前缀是Microsoft为Visual C++编译器增加的一个修饰符,告诉编译器在可执行文件或DLL文件中,把对应的变量放到它自己的段中。
我们不能将局部变量声明为__declspec(thread)类型。常用**gt
前缀表示全局TLS变量,用st
**表示静态TLS变量。
静态TLS需要编译器和操作系统的配合。
首先编译器需要对程序编译时,会将所有TLS变量放到他们自己的段中,这个段名为.tls,链接器会将所有对象模块中的tls段合并成一个大的tls段,保存到可执行文件或DLL文件中。
应用程序载入到内存的时候,查看tls段,分配内存来保存静态tls变量。需要编译器生成额外的代码来引入静态tls变量,使得应用程序不仅变得更大,而且执行起来也很慢。
当系统加载DLL时,必须查看进程中所有已有的线程,并扩大他们的TLS内存块;释放DLL也需要进程中的每个线程相关联的内存块也应该相应地缩减。

补充

对于TLS的运用,可能见到的比较少,内存泄露检查工具Visual Leak Detector中使用TLS检查每个线程的内存泄露情况。
但是在大型项目的使用中,也存在一些问题,就是动态TLS的拓展可能会因为系统调用陷入爆栈的情况,大致内容可参见下文《VLD中引入TLS导致爆栈》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值