-
TLS 线程局部存储
2019-10-14 17:32:42在学习逆向的过程中,大家可能了解到利用TLS,可以实现反调试,因为TLS回调函数会在入口函数之前执行,但是可能不知道具体的原理,以及...线程局部存储(Thread Local Storage),又简称T...在学习逆向的过程中,大家可能了解到利用TLS,可以实现反调试,因为TLS回调函数会在入口函数之前执行,但是可能不知道具体的原理,以及TLS是个什么东西,通过下面的介绍,你将会了解TLS的原理以及用法。
什么是线程局部存储
线程局部存储(Thread Local Storage),又简称TLS, 设计的目的是解决多线程之间变量的同步问题,程序员通过使用TLS机制,可以实现让程序拥有全局变量,但在不同的线程里却对应有不同的值,也就是说,进程中的所有线程都可以拥有全局变量,但这些变量其实是特定对某个线程才有意义的。
线程局部存储共分动态和静态两种,分类的主要依据是: 线程局部存储的数据所用空间,操作系统完成的是动态申请还是静态分配动态线程局部存储
动态TLS存在以下四个API函数
TlsAlloc
TlsGetValue
TlsSetValue
TlsFree
应用程序或DLL在合适的时候会调用这四个函数,通过索引对进程中每个线程的存储区进行统一操作。他们位于动态链接库文件kernel32.dll
下面通过一个例子(不用动态TLS和使用动态TLS)来说明一下TLS的作用以及这4个API的用法例子(不用TLS):
#include <stdio.h> #include <windows.h> int num; //线程函数模板 DWORD WINAPI ThreadFunc(LPVOID lParam) { num = 5; for (int i = 0; i < 5; i++) { printf("当前线程ID: %d, num = %d\n", GetCurrentThreadId(), num); num--; } return 0; } int main() { HANDLE hThread[4] = {}; for (int i = 0; i < 4; i++) { hThread[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL); } WaitForMultipleObjects(4, hThread, TRUE, INFINITE); printf("All threads has been destroyed!\n"); for (int i = 0; i < 4; i++) { CloseHandle(hThread[i]); } getchar(); return 0; }
期望的输出是每个线程num的输出都是 5 4 3 2 1,但是结果却是
使用了动态TLS例子#include <stdio.h> #include <windows.h> int num; //线程函数模板 DWORD WINAPI ThreadFunc(LPVOID lParam) { TlsSetValue(num, (LPVOID)5); //设置某个线程该索引处对应的值 for (int i = 0; i < 5; i++) { printf("当前线程ID: %d, num = %d\n", GetCurrentThreadId(), TlsGetValue(num)); TlsSetValue(num, (LPVOID)((DWORD)TlsGetValue(num) - 1)); } return 0; } int main() { HANDLE hThread[4] = {}; num = TlsAlloc(); //分配一个索引 if (num == TLS_OUT_OF_INDEXES) { printf("分配失败!\n"); return 0; } for (int i = 0; i < 4; i++) { hThread[i] = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL); } WaitForMultipleObjects(4, hThread, TRUE, INFINITE); printf("All threads has been destroyed!\n"); TlsFree(num); //释放线程Tls分配的空间 for (int i = 0; i < 4; i++) { CloseHandle(hThread[i]); } getchar(); return 0; }
输出结果为
可以看出TLS为线程绑定了数据,到了这里,大家也应该明白TLS 为啥叫线程局部存储了吧,,,就是把数据绑定到线程中,线程在哪,数据就在哪线程局部存储的具体实现原理
线程局部存储机制的示意图
进程环境块的0X44偏移处的数据为 ULONG TlsBitmapBits[2] 共64位,每个位可以是0或者1,分别代表未使用和已使用
线程环境块TEB的0XE10偏移处为 PVOID TlsSlot[0X40]; 是一个PVOID类型的数组,长度为64,数组的索引号对应着进程标志位的位索引什么意思呢?我画张图,再结合上面的代码,大家就明白了
我们通过TlsAlloc是分配一个索引,上面的代码中,num就是2,然后在PEB的TlsBitmapBits的索引是2的位就会被设置成1
然后我们通过TlsSetValue设置就是根据索引,将对应线程的TlsSlot数组中对应的索引设置成对应的数据,如上代码中就设置成5了,然后用TlsGetValue是得到成对应的值
TlsFree是释放这个索引对应的线程的空间,将PEB中TlsBitmapBits的对应位设置为0静态线程局部存储
静态线程局部存储是操作系统提供的另外一种线程与数据绑定的技术。它与动态TLS的区别在于,通过静态线程局部存储指定的数据无需使用专门的API函数,随意在易用性上会更好一些。
静态线程局部存储预先将变量定义在PE文件内部,一般使用.tls节存储(也不一定),对于相关API的调用由操作系统来完成。这种方式的有点就是从高级语言程序员角度来看更简单了。这种实现方式使得TLS数据的定义与初始化就像程序中使用普通的静态变量那样。
对静态TLS变量的定义不需要想动态线程局部存储一样,调用相关API,只需要做如下声明即可:
_declspec(thread) int tlsFlag=1;
为了支持这种编程模式。PE中的.tls节会包含以下信息:1、初始化数据
2、用于每个线程初始化和终止的回调函数
3、TLS索引可执行代码访问静态TLS数据一般需要经过一下几个步骤:
1.在链接的时候,连接器设置TLS目录中的AddressOfIndex字段。这个字段指向一个位置,在这个位置保存程序用到的TLS索引。
2.当创建线程是,加载器通过将线程环境块TEB的地址放入FS寄存器来传递线程的TLS数组地址。距TEB开头0x2c的位置处的字段ThreadLocalStoragePointer指向TLS数组。
3.加载器将TLS索引值保存到AddressOfIndex字段指向的位置处。
4.可执行代码获取TLS索引以及TLS数组的位置。
5.可执行代码将索引乘以4,并将该值作为这个数组内的偏移来使用。通过以上方法获取给定程序和模块的TLS数据区的地址。每个线程拥有他自己的TLS数据区,但这对于线程是透明的,它并不需要知道怎为单个线程分配数据的。
6.单个的TLS数据对象都位于TLS数据区的某个固定偏移处,因此可以用这种方式访问。
静态的TLS主要的应用是TLS回调函数。它可以在入口函数之前执行,所以可以做很多事情,比如反调试,加解密啥的。
下面 写代码 实验一下
#include <stdio.h> #include <windows.h> __declspec(thread) int value = 0X12345678; #define THREAD_NUM 3 //线程函数模板 DWORD WINAPI ThreadFunc(LPVOID lParam) { printf("线程ID ->%d, value = %#X\n", GetCurrentThreadId(), value); return 0; } int main() { HANDLE hThreads[THREAD_NUM] = {}; value = 5; for (int i = 0; i < THREAD_NUM; i++) { hThreads[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, 0); } WaitForMultipleObjects(THREAD_NUM, hThreads, TRUE, INFINITE); getchar(); return 0; }
这里的运行结果是:
正是因为静态线程局部存储的原因,使得每个线程都有一份value的拷贝,所以在这里即使主线程将value赋值为5,在其他线程当中value的值仍然是0X12345678
我们将生成的exe文件拖入到CFF Explorer中,查看其TLS 目录
StartAddressOfRawData 4025A4 EndAddressOfRawData 4025AC AddressOfindex 403380 AddressOfCallBacks 4020EC SizeOfZeroFill 0 Characteristics 00300000
在每个线程中都对应的着value的拷贝,那么他的拷贝原数据是在哪的,
这里StartAddressOfRawData 和 EndAddressOfRawData 之间就是拷贝的源数据,针对这个程序来说,我们找到这个StartAddressOfRawData在文件中对应的偏移
我们发现果然是这样,在代码中写的value的值0X12345678就在这里存着那么线程是怎么找到其对应的value 的,在动态线程局部存储中,是在TEB 偏移E10处(TLS槽)存着,但静态线程局部存储不是这样的
我们用OD调试,找到线程函数那里
在TEB的2C处存的一个指针, 这个指针指向一个TLS槽(注意这里不是TEB中的那个TLS槽,而是自己分配的空间)对于这个程序来说,索引是0,结构是这样的
我们在看看回调函数,由于程序中没有定义回调函数,所以AddressOfCallBacks指向的数据是0
下面我们重写代码添加回调函数#include <stdio.h> #include <windows.h> #pragma comment (linker, "/INCLUDE:__tls_used") #pragma comment (linker, "/INCLUDE:__tls_callback") __declspec(thread) int value = 0X12345678; #define THREAD_NUM 3 void NTAPI tls_callback(PVOID h, DWORD reason, PVOID pv) { if (reason == DLL_PROCESS_ATTACH) { MessageBox(NULL, "线程局部存储回调函数", "测试", MB_OK); } return; } //线程函数模板 DWORD WINAPI ThreadFunc(LPVOID lParam) { printf("线程ID ->%d, value = %#X\n", GetCurrentThreadId(), value); return 0; } int main() { HANDLE hThreads[THREAD_NUM] = {}; value = 5; for (int i = 0; i < THREAD_NUM; i++) { hThreads[i] = CreateThread(NULL, 0, ThreadFunc, 0, 0, 0); } WaitForMultipleObjects(THREAD_NUM, hThreads, TRUE, INFINITE); getchar(); return 0; } EXTERN_C #pragma data_seg (".CRT$XLB") PIMAGE_TLS_CALLBACK _tls_callback = tls_callback; #pragma data_seg ()
至于为啥子这样添加反调试,请参考https://www.jianshu.com/p/841d360777de
我们运行一下程序,会先弹出窗口,在打印
我们来到AddressOfCallBacks指向的地方
将程序拖入到OD中
会发现程序先执行TLS回调函数,然后再执行main函数
(补充,,利用这一点可以实现很多事情,比如:加解密,反调试啥的)参考资料:
《Windows PE权威指南》
https://blog.csdn.net/lixiangminghate/article/details/46770635
https://www.jianshu.com/p/841d360777de -
TLS线程局部存储
2018-08-09 16:56:55多线程程序当中,因为线程之间共享进程的数据,所以在访问全局数据的时候,可能需要加锁互斥访问。但是全局数据的互斥访问只有在多个线程之间协作进行处理的时候,才有必要。但是如果数据在各个线程之间都需要单独的...https://blog.csdn.net/dayenglish/article/details/20232131
多线程程序当中,因为线程之间共享进程的数据,所以在访问全局数据的时候,可能需要加锁互斥访问。但是全局数据的互斥访问只有在多个线程之间协作进行处理的时候,才有必要。但是如果数据在各个线程之间都需要单独的副本,比如说VC CRT库当中的errno变量,我们希望这个变量仅仅由当前线程错误来设置,并且只影响当前线程,那么这种线程之间共享的副本数据该怎么实现呢?
微软提供的一种实现就是TLS(线程局部存储)。TLS有两种实现方法,第一是静态的实现,那就是利用连接器扩展__declspec(thread)修饰所定义的变量,这使得数据在各个线程当中都有副本;另一种方法是利用系统提供的函数动态实现。
首先分析静态实现。在《windows核心编程》里面,作者提到每一个TLS的静态支持需要额外的三条指令。利用实验验证下。
#include<stdio.h>
__declspec(thread) int a;
int main()
{
a=0;
printf("%d",a);
return 0;
}
上面是一个很简单的VC环境下面的程序,利用VC的调试功能对他进行反汇编。得到的汇编代码如下所示:
1: #include<stdio.h>
2: __declspec(thread) int a;
3: int main()
4: {
00401010 push ebp
00401011 mov ebp,esp
00401013 sub esp,40h
00401016 push ebx
00401017 push esi
00401018 push edi
00401019 lea edi,[ebp-40h]
0040101C mov ecx,10h
00401021 mov eax,0CCCCCCCCh
00401026 rep stos dword ptr [edi]
5: a=0;
00401028 mov eax,[__tls_index (00427e58)]
0040102D mov ecx,dword ptr fs:[2Ch]
00401034 mov edx,dword ptr [ecx+eax*4]
00401037 mov dword ptr [edx+104h],0
6: printf("%d",a);
00401041 mov eax,[__tls_index (00427e58)]
00401046 mov ecx,dword ptr fs:[2Ch]
0040104D mov edx,dword ptr [ecx+eax*4]
00401050 mov eax,dword ptr [edx+104h]
00401056 push eax
00401057 push offset string "%d" (0042201c)
0040105C call printf (00401090)
00401061 add esp,8
7: return 0;
00401064 xor eax,eax
8: }
从编号4到5之间的是一些系统加进去的代码,就是初始化局部变量。从这里也可以看出来实际上的变量a是不占用局部变量的存储空间的。代码段5到6表示给a赋值0。总共四条指令,第一条得到索引值,第二条得到一个指针,第三条得到a所在的内存地址,第四条赋值(之所以书上说多三条是因为不论怎样赋值语句都是必须的,多的是前面的寻址指令)。
有了上面的反汇编代码,接着开始分析三条寻址指令的含义。第一个得到索引,因为是在用户控件内,所以很正常,但是第二条指令通过FS寄存器来寻址,就有疑问了。为什么利用FS寻址?在微软的官方文档里面的解释是:fs:[0]在内核状态指向KPCB,而在用户状态指向TEB。如何使得一个指针指向两个结构体呢?通过查看reactOS的源代码可以知道KPCR的第一个成员是NT_TIB,很巧的是TEB的第一个成员也是NT_TIB,这样的话上面的就不难理解
了。接着往下看TEB的偏移为0X2C的成员变量,这个成员变量是PVOID,因为是以四个字节为单位,所以EAX需要乘以4。 下面开始分析动态实现,TLS的动态实现利用到四个函数TlsAlloc,TlsFree,TlsGetValue和TlsSetValue。通过ReactOS的源代码我们可以有一个比较清晰的了解这四个函数的实现。TlsAlloc分配一个索引值给当前的线程,首先函数申请当前进程的互斥锁,以便互斥访问索引,然后通过PEB的设置位图找到未被使用的索引值,如果返回0XFFFFFFFF那么,标示都被使用了,则到扩展位图里面搜索,直到找到或者失败为止。而之所以会有一个扩展过程是因为如果所有的都需要临时分配的话那么开销太大,所以只有浪费一部分内存,预先分配64果然存储槽。
DWORD WINAPI TlsAlloc(VOID)
{
ULONG Index;
PTEB Teb;
PPEB Peb;
Teb = NtCurrentTeb();
Peb = Teb->ProcessEnvironmentBlock;
RtlAcquirePebLock();
Index = RtlFindClearBitsAndSet(Peb->TlsBitmap, 1, 0);
if (Index != 0xFFFFFFFF)
{
Teb->TlsSlots[Index] = 0;初始化所有的槽为空
RtlReleasePebLock();
return Index;
}
Index = RtlFindClearBitsAndSet(Peb->TlsExpansionBitmap, 1, 0);
if (Index != 0xFFFFFFFF)
{
if (!Teb->TlsExpansionSlots)
{
Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS *
sizeof(PVOID));
}
if (!Teb->TlsExpansionSlots)
{
RtlClearBits(Peb->TlsExpansionBitmap, Index, 1);
Index = 0xFFFFFFFF;
BaseSetLastNTError(STATUS_NO_MEMORY);
}
else
{
Teb->TlsExpansionSlots[Index] = 0;
Index += TLS_MINIMUM_AVAILABLE;
}
}
else
{
BaseSetLastNTError(STATUS_NO_MEMORY);
}
RtlReleasePebLock();
return Index;
}
BOOL WINAPI TlsFree(IN DWORD Index)
{
BOOL BitSet;
PPEB Peb;
ULONG TlsIndex;
PVOID TlsBitmap;
NTSTATUS Status;
Peb = NtCurrentPeb();
RtlAcquirePebLock();
if (Index >= TLS_MINIMUM_AVAILABLE)
{
TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
if (TlsIndex >= TLS_EXPANSION_SLOTS)
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
else
{
TlsBitmap = Peb->TlsExpansionBitmap;
Index = TlsIndex;
}
}
else
{
TlsBitmap = Peb->TlsBitmap;
}
BitSet = RtlAreBitsSet(TlsBitmap, Index, 1);最主要的是这一句,将位映射图改为FREE,前面都是对位映射以及索引的设置,由于TlsGetValue不测试Index的值所以在free之后,如果没改变数值的话,可以的到原有的值。
if (BitSet)
{
Status = NtSetInformationThread(NtCurrentThread(),
ThreadZeroTlsCell,
&Index,
sizeof(DWORD));
if (!NT_SUCCESS(Status))
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
RtlClearBits(TlsBitmap, Index, 1);
}
else
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
RtlReleasePebLock();
return FALSE;
}
RtlReleasePebLock();
return TRUE;
}
LPVOID WINAPI TlsGetValue(IN DWORD Index)
{
PTEB Teb;
Teb = NtCurrentTeb();//此处得到当前线程的环境块,所以各个线程之间的数据是不共享的
Teb->LastErrorValue = 0;
if (Index < TLS_MINIMUM_AVAILABLE)
{
return Teb->TlsSlots[Index];
}
if (Index >= TLS_EXPANSION_SLOTS + TLS_MINIMUM_AVAILABLE)
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return NULL;
}
Teb->LastErrorValue = 0;
if (!Teb->TlsExpansionSlots) return NULL;
return Teb->TlsExpansionSlots[Index - TLS_MINIMUM_AVAILABLE];
}
BOOL WINAPI TlsSetValue(IN DWORD Index,
IN LPVOID Value)
{
DWORD TlsIndex;
PTEB Teb = NtCurrentTeb();
if (Index < TLS_MINIMUM_AVAILABLE)
{
Teb->TlsSlots[Index] = Value;
return TRUE;
}
TlsIndex = Index - TLS_MINIMUM_AVAILABLE;
if (TlsIndex >= TLS_EXPANSION_SLOTS)
{
BaseSetLastNTError(STATUS_INVALID_PARAMETER);
return FALSE;
}
if (!Teb->TlsExpansionSlots)
{
RtlAcquirePebLock();
if (!Teb->TlsExpansionSlots)
{
Teb->TlsExpansionSlots = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TLS_EXPANSION_SLOTS *
sizeof(PVOID));
if (!Teb->TlsExpansionSlots)
{
RtlReleasePebLock();
BaseSetLastNTError(STATUS_NO_MEMORY);
return FALSE;
}
}
RtlReleasePebLock();
}
Teb->TlsExpansionSlots[TlsIndex] = Value;
return TRUE;
}
以上实现当中,由于微软觉得64个TLS存储太少,后来进行了扩充。在超过64个TLS的需求之后,进行动态分配,这样就会浪费内存。每个线程需要额外的4K来存储TLS槽。
-
Tls 线程局部存储
2016-04-06 09:35:04如果需要在一个线程内部的各个函数调用都能访问、但其他线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。 TLS声明的空间,其他线程无法访问,...什么是TLS?
如果需要在一个线程内部的各个函数调用都能访问、但其他线程不能访问的变量(被称为static memory local to a thread 线程局部静态变量),就需要新的机制来实现。这就是TLS。
TLS声明的空间,其他线程无法访问,增加了可移植性。
TlsAlloc(): 返回TLS索引
TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue):将一块空间与索引进行绑定。
TlsGetValue(DWORD dwTlsIndex): 获取与索引绑定的空间
TlsFree(DWORD dwTlsIndex):释放索引,使索引由占用转变为未占用,供下次索引申请使用。
这几个函数中,比较有有意思的是,TlsSetValue(DWORD dwTlsIndex ,LPVOID lpTlsValue),这个函数的具体意思是将索引与某个地址进行绑定,当再一次进行绑定的时候,上一个绑定地址不会释放,仍然会占用空间。在非第一次对索引与地址进行绑定时,需要检查就地址是否需要释放(TlsFree(DWORD dwTlsIndex)只是将索引进行释放,不释放绑定地址空间)。
注意两点:
使用TlsSetValue(DWORD dwTlsIndex,LPVOID lpTlsValue)时,思考是否以前绑定地址,重新绑定地址时,是否需要释放旧地址。
使用TlsFree(DWORD dwTlsIndex)时,思考是否需要释放绑定地址。
理论验证代码:
int main() { DWORD dwTlsIndex; LPVOID lpvSomeValue; dwTlsIndex = TlsAlloc(); LPTSTR pszBuf = NULL; pszBuf = (LPTSTR)LovalAlloc(LPTR,sizeof(DWOR)); DWOR s = 0x12345678; char k[100] = "hello my world ! "; TlsSetValue(dwTlsIndex,(LPVOID)k); //TlsSetValue(dwTlsIndex,pszBuf); //TlsSetValue(dwTlsIndex,(lpvoid)&s); lpvSomeValue = TlsGetValue(dwTlsIndex); TlsFree(dwTlsIndex); cout<<k<<endl; return 0; }
-
C++采用TLS线程局部存储的用法实例
2020-09-04 03:19:04主要介绍了C++采用TLS线程局部存储的用法实例,详细讲述了TLS索引及线程的操作,非常具有实用价值,需要的朋友可以参考下 -
TLS线程局部存储的使用
2012-05-11 16:38:12TLS线程局部存储的使用 // useTLS.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include #include //声明 VOID InitStartTime(); DWORD GetUserTime(); //TLS索引,作全局变量 DWORD g_...TLS线程局部存储的使用
// useTLS.cpp : 定义控制台应用程序的入口点。 // #include "stdafx.h" #include <Windows.h> #include <process.h> //声明 VOID InitStartTime(); DWORD GetUserTime(); //TLS索引,作全局变量 DWORD g_dwTlsIndex; VOID InitStartTime() { DWORD dwStartTime = GetTickCount(); ::TlsSetValue(g_dwTlsIndex,(LPVOID)dwStartTime); } DWORD GetUserTime() { DWORD dwNowTime = GetTickCount(); DWORD dwStartTime = (DWORD)::TlsGetValue(g_dwTlsIndex); return dwNowTime - dwStartTime; } UINT WINAPI ThreadProc(LPVOID lpParameter) { //模拟线程的工作过程 DWORD i = 1000 * 1000 *100; while (i--) { } printf("Thread ID:%-5d,Use Time:%d\n",::GetCurrentThreadId(), GetUserTime()); return 0; } int _tmain(int argc, _TCHAR* argv[]) { HANDLE hThread[10]; //得到TLS索引 g_dwTlsIndex = ::TlsAlloc(); //开启十个线程,计算每个线程运行的时间 for (int i=0;i<10;i++) { hThread[i] = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, NULL, 0, NULL); } //等待工作线程 ::WaitForMultipleObjects(10, hThread, TRUE, INFINITE); for (int i=0;i<10;i++) { //::WaitForSingleObject(hThread[i], INFINITE); ::CloseHandle(hThread[i]); } //释放TLS ::TlsFree(g_dwTlsIndex); return 0; }
-
35 windows_35_Thread_Tls 线程局部存储
2016-06-10 13:15:00windows_35_Thread_Tls 线程局部存储 // windows_35_Thread_Tls.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <windows.h>#include <stdio.h>#include <string.h>int... -
TLS线程局部存储--thread_specific_ptr
2018-07-26 17:40:28这也就是说在某一个线程已经调用了一个函数时,如果你再调用同一个函数,那么这样是不安全的。一个不可重入的函数通过连续的调用来保存静态变量或者是返回一个指向静态数据的指针。 举例来说,std::strtok就是不可重... -
TLS(线程局部存储).zip
2020-04-02 22:33:45《Windows PE权威指南》TLS学习代码 实现TLS表定位,枚举; 实现动态线程存储 实现静态线程存储 可与《PE文件:TLS线程局部存储》配套使用 https://blog.csdn.net/weixin_43742894/article/details/105235426 -
线程局部存储(TLS)
2015-03-17 17:54:37线程局部存储,Part ...线程局部存储,Part 2:显式TLS 线程局部存储,Part 3:编译器和链接器对隐式TLS的支持 线程局部存储,Part 4:访问__declspec(thread)变量 线程局部存储,Part 5:加载器对__dec -
线程局部存储TLS
2015-07-06 09:17:57windows线程局部存储TLS原理与解释 -
TLS(线程局部存储)
2020-05-16 15:46:40TLS全称线程局部存储器,它用来保存变量或回调函数。 TLS里面的变量和回调函数都在程序入口点(AddressOfEntry)之前执行,也就是说程序在被调试时,还没有在入口点处断下来之前,TLS中的变量和回调函数就已经... -
线程局部存储TLS
2019-10-08 08:58:15线程局部存储(thread-local storage, TLS)是一个使用很方便的存储线程局部数据的系统。利用TLS机制可以为进程中所有的线程关联若干个数据,各个线程通过由TLS分配的全局索引来访问与自己关联的数据。这样,每个... -
TLS--线程局部存储
2019-02-12 13:05:37TLS--线程局部存储 概念:线程局部存储(Thread Local Storage,TLS)用来将数据与一个正在执行的指定线程关联起来。 进程中的全局变量与函数内定义的静态(static)变量,是各个线程都可以访问的共享变量。在一个... -
[转]TLS: 线程局部存储TLS
2010-03-11 10:16:00线程局部存储TLS 堆栈中定义的局部变量,对多线程是安全的,因为不同的线程有自己的堆栈。而通常定义的全局变量,所有线程都可以作读写访问,这样它就不是线程安全的,为安全就有必要加锁互斥访问。而何为线程局部... -
线程局部存储 TLS
2017-08-07 20:57:00C/C++运行库提供了TLS(线程局部存储),在多线程还未产生时,可以将数据与正在执行的线程关联。strtok()函数就是一个很好的例子。与它一起的还有strtok_s(),_tcstok_s()等等函数,其实_tcs 是 wcs 的另外一种写法,... -
TLS--线程局部存储 学习
2018-11-29 11:51:51参考:https://www.cnblogs.com/stli/archive/2010/11/03/1867852.html TLS--线程局部存储 https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B%E5%B1%80%E9%83%A8%E5%AD%98%E5%82%A8/10484278?fr=aladdin 概念:... -
java线程局部存储_线程局部存储
2021-02-28 07:18:31线程局部存储线程局部存储(TLS) 是一种存储持续期(storage duration),对象的存储是在线程开始时分配,线程结束时回收,每个线程有该对象自己的实例。这种对象的链接性(linkage)可以是静态的也可是外部的。TLS的一个...