日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁(yè) > 科技  > 軟件

聊一聊 C# 的線程本地存儲(chǔ)TLS到底是什么

來(lái)源: 責(zé)編: 時(shí)間:2024-01-02 17:28:07 240觀看
導(dǎo)讀一:背景1. 講故事有朋友在后臺(tái)留言讓我說(shuō)一下C#的 ThreadStatic 線程本地存儲(chǔ)是怎么玩的?這么說(shuō)吧,C#的ThreadStatic是假的,因?yàn)镃#完全是由CLR(C++)承載的,言外之意C#的線程本地存儲(chǔ),用的就是用C++運(yùn)行時(shí)提供的 __declspec(th

一:背景

1. 講故事

有朋友在后臺(tái)留言讓我說(shuō)一下C#的 ThreadStatic 線程本地存儲(chǔ)是怎么玩的?這么說(shuō)吧,C#的ThreadStatic是假的,因?yàn)镃#完全是由CLR(C++)承載的,言外之意C#的線程本地存儲(chǔ),用的就是用C++運(yùn)行時(shí)提供的 __declspec(thread) 或 __thread 來(lái)虛構(gòu)的一套玩法,這一篇我們就來(lái)簡(jiǎn)單聊一聊。EYS28資訊網(wǎng)——每日最新資訊28at.com

二:C# 的線程本地存儲(chǔ)

1. 虛構(gòu)在哪里

在 C# 中使用ThreadStatic就可以將變量和線程進(jìn)行綁定,參考代碼如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

internal class Program    {        [ThreadStatic]        public static int num = 10;        static void Main(string[] args)        {            Console.WriteLine($"num={num}");            Debugger.Break();        }    }

在 CLR 中如何將 num 與 Thread 綁定呢?研究過(guò) CLR 源碼的朋友應(yīng)該知道是用 ThreadLocalInfo 的,參考代碼如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

#ifdef _MSC_VER__declspec(selectany) __declspec(thread) ThreadLocalInfo gCurrentThreadInfo;#elseEXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo;#endifstruct ThreadLocalInfo{    Thread* m_pThread;    AppDomain* m_pAppDomain; // This field is read only by the SOS plugin to get the AppDomain    void** m_EETlsData; // ClrTlsInfo::data};

上面的 m_pThread 就是 C# Thread 在 CLR 層面的承載,怎么去驗(yàn)證呢?可以把代碼跑起來(lái),然后用 windbg 驗(yàn)證一下。EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dt coreclr!gCurrentThreadInfo   +0x000 m_pThread        : 0x000001e3`506c5fa0 Thread   +0x008 m_pAppDomain     : 0x000001e3`506ba9b0 AppDomain   +0x010 m_EETlsData      : 0x000001e3`506aa360  -> (null) 0:000> !tThreadCount:      3UnstartedThread:  0BackgroundThread: 2PendingThread:    0DeadThread:       0Hosted Runtime:   no                                                                                                            Lock   DBG   ID     OSID ThreadOBJ           State GC Mode     GC Alloc Context                  Domain           Count Apt Exception   0    1     2e04 000001E3506C5FA0    2a020 Preemptive  000001E3521DCE80:000001E3521DD4A8 000001e3506ba9b0 -00001 MTA    6    2     4ef8 000001E3506F1A30    21220 Preemptive  0000000000000000:0000000000000000 000001e3506ba9b0 -00001 Ukn (Finalizer)    7    3     3550 000001E3726A0AE0    2b220 Preemptive  0000000000000000:0000000000000000 000001e3506ba9b0 -00001 MTA

從卦中可以清楚的看到 m_pThread=0x000001e3506c5fa0 就是我們的主線程,最后的 num 就是放在與之關(guān)聯(lián)的 ThreadLocalModule 中,這個(gè)比較簡(jiǎn)單,關(guān)注下匯編代碼就好了,下面的 rax 就是 ThreadLocalModule。EYS28資訊網(wǎng)——每日最新資訊28at.com

00007ffb`218d2c2c 48b9b07b9921fb7f0000 mov rcx,7FFB21997BB0h00007ffb`218d2c36 ba04000000      mov     edx,400007ffb`218d2c3b e8001fb55f      call    coreclr!JIT_GetSharedNonGCThreadStaticBase (00007ffb`81424b40)00007ffb`218d2c40 8b4820          mov     ecx,dword ptr [rax+20h]00007ffb`218d2c43 894dfc          mov     dword ptr [rbp-4],ecx0:000> dp rax+0x20 L100000294`d0539790  abababab`0000000a

CLR層面用了太多的高層虛構(gòu)來(lái)玩了一套線程本地存儲(chǔ),其實(shí)最核心的還要理解再下一層的 __declspec(selectany) ,接下來(lái)聊聊這玩意是怎么玩的。EYS28資訊網(wǎng)——每日最新資訊28at.com

2. __declspec(selectany) 是怎么玩的

在Windows層面的術(shù)語(yǔ)中,有兩種 TLS 技術(shù)。EYS28資訊網(wǎng)——每日最新資訊28at.com

  • 動(dòng)態(tài)TLS

借助 Windows 提供的 TlsAlloc, TlsSetValue 之類(lèi)的方法來(lái)實(shí)現(xiàn),并且存放在線程 _TEB.TlsSlots 的槽位中,參考代碼如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dt 0x000000f4f0ca6000 ntdll!_TEB   +0x000 NtTib            : _NT_TIB   ...   +0x1480 TlsSlots         : [64] (null)    ...
  • 靜態(tài)TLS

C#的線程本地存儲(chǔ)用的就是靜態(tài)TLS,也就是在編譯時(shí)就已經(jīng)聲明好的,在 PE 文件里面有一個(gè) .tls 節(jié)點(diǎn),這個(gè)節(jié)點(diǎn)的數(shù)據(jù)會(huì)被每個(gè)線程在heap堆上copy一份,存放在 _TEB.ThreadLocalStoragePointer 來(lái)指向的指針數(shù)組中,參考代碼如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dt 0x000000f4f0ca6000 ntdll!_TEB   +0x000 NtTib            : _NT_TIB   +0x058 ThreadLocalStoragePointer : 0x00000294`d0536ab0 Void   ...

動(dòng)態(tài)的TLS我就不介紹了,這里著重說(shuō)一下靜態(tài)的TLS。EYS28資訊網(wǎng)——每日最新資訊28at.com

3. 靜態(tài)TLS詳解

為了方便講解,先上一段測(cè)試代碼。EYS28資訊網(wǎng)——每日最新資訊28at.com

#include <windows.h>#include <stdio.h>#include <limits.h>__declspec(thread) int i = INT_MAX;__declspec(thread) int j = INT_MAX;int main() { int num1 = i; int num2 = j; printf("i=%d,j=%d", num1, num2);}

上面的 i,j 值在編譯時(shí)就已經(jīng)放到了 PE 頭的 .tls 節(jié),可以用 PPEE 觀察下對(duì)象頭。EYS28資訊網(wǎng)——每日最新資訊28at.com

圖片EYS28資訊網(wǎng)——每日最新資訊28at.com

從卦中可以看到 .tls 占用了 0x400 字節(jié)大小,并且用 WinHex 真的觀察到了 i,j 的值,挺有意思。EYS28資訊網(wǎng)——每日最新資訊28at.com

在內(nèi)存中TLS區(qū)比這個(gè)還小一點(diǎn),可以觀察一下 DIRECTORY_ENTRY_TLS 節(jié)的 StartAddressOfRawData 和 EndAddressOfRawData 字段,這也是每個(gè)線程copy的原始內(nèi)存區(qū)域,可以看到只有 0x20D ,大概少了一半,截圖如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

圖片EYS28資訊網(wǎng)——每日最新資訊28at.com

有了這些前置知識(shí),接下來(lái)觀察內(nèi)存中的地址,在運(yùn)行之前先把 ASLR 關(guān)掉,匯編代碼參考如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

//int num1 = i;   14 00411895 a1b4a14100      mov     eax,dword ptr [ConsoleApplication2!_tls_index (0041a1b4)]   14 0041189a 648b0d2c000000  mov     ecx,dword ptr fs:[2Ch]   14 004118a1 8b1481          mov     edx,dword ptr [ecx+eax*4]   14 004118a4 8b8208010000    mov     eax,dword ptr [edx+108h]   14 004118aa 8945f8          mov     dword ptr [ebp-8],eax   //int num2 = j;   15 004118ad a1b4a14100      mov     eax,dword ptr [ConsoleApplication2!_tls_index (0041a1b4)]   15 004118b2 648b0d2c000000  mov     ecx,dword ptr fs:[2Ch]   15 004118b9 8b1481          mov     edx,dword ptr [ecx+eax*4]   15 004118bc 8b8204010000    mov     eax,dword ptr [edx+104h]   15 004118c2 8945ec          mov     dword ptr [ebp-14h],eax

可以看到每一句大概會(huì)生成 5 行匯編代碼,我們簡(jiǎn)單分析下。EYS28資訊網(wǎng)——每日最新資訊28at.com

  • ConsoleApplication2!_tls_index (0041a1b4)

這個(gè)值就是 PE 頭的 AddressOfIndex 值,可以再回頭觀察下,里面存的就是 tls 索引,當(dāng)前是 0 ,參考如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dp 0041a1b4 L10041a1b4  00000000
  • fs:[2Ch]

在用戶(hù)態(tài)層面上 fs 指向的是當(dāng)前線程的 TEB 結(jié)構(gòu),其中的 2C 偏移指的就是 ThreadLocalStoragePointer 結(jié)構(gòu),windbg 觀察如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dg fs                                  P Si Gr Pr LoSel    Base     Limit     Type    l ze an es ng Flags---- -------- -------- ---------- - -- -- -- -- --------0053 002bc000 00000fff Data RW Ac 3 Bg By P  Nl 000004f30:000> dt 0x002bc000 ntdll!_TEB   +0x000 NtTib            : _NT_TIB   +0x01c EnvironmentPointer : (null)    +0x020 ClientId         : _CLIENT_ID   +0x028 ActiveRpcHandle  : (null)    +0x02c ThreadLocalStoragePointer : 0x00664400 Void   ...
  • edx,dword ptr [ecx+eax*4]

這句匯編是一個(gè)數(shù)組操作,翻譯成 C 就是 ThreadLocalStoragePointer[tls]。EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dp 0x00664400 L100664400  00664448

這里要提醒的是:上面的 00664448 所在的 heap 位置其實(shí)就是 PE 頭里的 StartAddressOfRawData~EndAddressOfRawData內(nèi)存區(qū)域的 copy,截圖如下:EYS28資訊網(wǎng)——每日最新資訊28at.com

圖片EYS28資訊網(wǎng)——每日最新資訊28at.com

  • eax,dword ptr [edx+108h]

這句話的意思就是在 數(shù)組元素1 這個(gè)結(jié)構(gòu)上偏移108的位置存放著我們的 num 值,用 windbg 觀察之后果然就是的。EYS28資訊網(wǎng)——每日最新資訊28at.com

0:000> dp 00664448+0x108 L100664550  7fffffff

三:總結(jié)

C# 屬于一種業(yè)務(wù)高層抽象的語(yǔ)言,它的很多底層被C++再次隔離了,想要理解本篇的TLS,還得需要往下一層一層的擊穿,作為C#程序員太難了。EYS28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-56404-0.html聊一聊 C# 的線程本地存儲(chǔ)TLS到底是什么

聲明:本網(wǎng)頁(yè)內(nèi)容旨在傳播知識(shí),若有侵權(quán)等問(wèn)題請(qǐng)及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。郵件:2376512515@qq.com

上一篇: 用Go實(shí)現(xiàn)一個(gè)帶緩存的REST API服務(wù)端

下一篇: 利用Go傳統(tǒng)RPC和gRPC框架分別實(shí)現(xiàn)一個(gè)RPC服務(wù)端

標(biāo)簽:
  • 熱門(mén)焦點(diǎn)
Top 主站蜘蛛池模板: 彭阳县| 宁武县| 建水县| 汽车| 灌南县| 类乌齐县| 湾仔区| 水城县| 龙陵县| 淮安市| 湟源县| 威海市| 澄江县| 长治市| 竹山县| 临清市| 江川县| 汕头市| 屯昌县| 玉林市| 新宾| 浑源县| 伊春市| 柳林县| 永州市| 名山县| 吴忠市| 花莲市| 花莲县| 保靖县| 板桥市| 鄯善县| 荥阳市| 河津市| 盐源县| 桦川县| 镇雄县| 汪清县| 工布江达县| 视频| 平湖市|