1. 程式人生 > >TLS--執行緒區域性儲存

TLS--執行緒區域性儲存

這個東西並不陌生了,之前寫過了一個關於這個的應用,利用靜態TLS姿勢實現程式碼段靜態加密免殺或者所謂的加殼思路。地址在這:http://blog.csdn.net/u013761036/article/details/53967943今天就簡單的整理下TLS的相關概念和常規應用。一開始說了一大堆的Windows的程序與執行緒啥啥啥的概念和原理,這裡直接省略。

什麼是執行緒區域性儲存?

    執行緒區域性儲存(Thread Local Storage,TLS)很好的解決了多執行緒設計中變數同步問題,比如你寫一個exe裡面有N個執行緒,你可以放棄使用TLS,因為你對自己設計的程式有比較全面的把握。你清楚自己設計的程序裡總共有多少個執行緒,每個執行緒使用了哪些資料結構,記憶體空間申請、釋放都在你的掌控之下,全域性變數的訪問全部都採用了同步技術,那是沒問題的。如果你是一個DLL開發者,你無法確定呼叫這個DLL的素質程式裡到底有多少個執行緒,每個執行緒的資料是如何定義的,這時,可以考慮使用執行緒據儲存技術。

TLS分為靜態和動態兩種:

動態TLS,主要是使用這幾個API TlsAlloc ,TlsGetValue,TlsSetValue,TlsFree

下面是微軟MSDN上的一個例子,看下理解下:

  1. <code class="language-cpp">#include <windows.h>   
  2. #include <stdio.h>   
  3. #define THREADCOUNT 4   
  4. DWORD dwTlsIndex;   
  5. VOID ErrorExit(LPSTR);   
  6. VOID CommonFunc(VOID)   
  7. {   
  8.    LPVOID lpvData;   
  9. // Retrieve a data pointer for the current thread.   
  10.    lpvData = TlsGetValue(dwTlsIndex);   
  11.    if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))   
  12.       ErrorExit("TlsGetValue error");   
  13. // Use the data stored for the current thread.   
  14.    printf("common: thread %d: lpvData=%lx\n",   
  15.       GetCurrentThreadId(), lpvData);   
  16.    Sleep(5000);   
  17. }   
  18. DWORD WINAPI ThreadFunc(VOID)   
  19. {   
  20.    LPVOID lpvData;   
  21. // Initialize the TLS index for this thread.   
  22.    lpvData = (LPVOID) LocalAlloc(LPTR, 256);   
  23.    if (! TlsSetValue(dwTlsIndex, lpvData))   
  24.       ErrorExit("TlsSetValue error");   
  25.    printf("thread %d: lpvData=%lx\n", GetCurrentThreadId(), lpvData);   
  26.    CommonFunc();   
  27. // Release the dynamic memory before the thread returns.   
  28.    lpvData = TlsGetValue(dwTlsIndex);   
  29.    if (lpvData != 0)   
  30.       LocalFree((HLOCAL) lpvData);   
  31.    return 0;   
  32. }   
  33. int main(VOID)   
  34. {   
  35.    DWORD IDThread;   
  36.    HANDLE hThread[THREADCOUNT];   
  37.    int i;   
  38. // Allocate a TLS index.   
  39.    if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)   
  40.       ErrorExit("TlsAlloc failed");   
  41. // Create multiple threads.   
  42.    for (i = 0; i < THREADCOUNT; i++)   
  43.    {   
  44.       hThread[i] = CreateThread(NULL, // default security attributes   
  45.          0,                           // use default stack size   
  46.          (LPTHREAD_START_ROUTINE) ThreadFunc, // thread function   
  47.          NULL,                    // no thread function argument   
  48.          0,                       // use default creation flags   
  49.          &IDThread);              // returns thread identifier   
  50.    // Check the return value for success.   
  51.       if (hThread[i] == NULL)   
  52.          ErrorExit("CreateThread error\n");   
  53.    }   
  54.    for (i = 0; i < THREADCOUNT; i++)   
  55.       WaitForSingleObject(hThread[i], INFINITE);   
  56.    TlsFree(dwTlsIndex);  
  57.    return 0;   
  58. }   
  59. VOID ErrorExit (LPSTR lpszMessage)   
  60. {   
  61.    fprintf(stderr, "%s\n", lpszMessage);   
  62.    ExitProcess(0);   
  63. }</code>  

靜態TLS

    靜態執行緒區域性儲存是作業系統提供的另外一種執行緒與資料繫結的技術。它與動態TLS的區別在於,通過靜態執行緒區域性儲存指定的資料無需使用專門的API函式,隨意在易用性上會更好一些。

    靜態執行緒區域性儲存預先將變數定義在PE檔案內部,一般使用.tls節儲存,對於相關API的呼叫由作業系統來完成。這種方式的有點就是從高階語言程式設計師角度來看更簡單了。這種實現方式使得TLS資料的定義與初始化就像程式中使用普通的靜態變數那樣。

    對靜態TLS變數的定義不需要想動態執行緒區域性儲存一樣,呼叫相關API,只需要做如下宣告即可:

_declspec(thread) int tlsFlag=1;

     為了支援這種程式設計模式。PE中的.tls節會包含一下資訊:

初始化資料

用於每個執行緒初始化和終止的回撥函式

TLS索引