1. 程式人生 > >《Windows核心程式設計》の執行緒區域性儲存TLS

《Windows核心程式設計》の執行緒區域性儲存TLS

執行緒區域性儲存(Thread Local StorageTLS)用來將資料與一個正在執行的指定執行緒關聯起來。我們在應用程式和DLL中可以使用兩種型別的TLS:動態TLS和靜態TLS。但一般來說,這兩項技術在建立DLL的時候更加有用,這是因為DLL通常並不知道它們被連結到的應用程式的結構是什麼樣的。但是在編寫應用程式時,我們一般都知道自己要建立多少執行緒,自己會如何使用這些執行緒。然後我們就可以設計一些替代方案來為每個執行緒關聯資料,或者設計得好一點的話,可以使用基於棧的方法(區域性變數)來為每個執行緒關聯資料。

動態TLS

一般通過呼叫一組4API函式來使用動態TLS,這些函式實際上最經常為DLL

使用。系統中每個程序都有一組正在使用標誌(in-use flags),每個標誌可以被設為FREEINUSE,表示該TLS元素是否正在被使用。Microsoft保證至少TLS_MINIMUM_AVAILABLE個位標誌可供使用。其中TLS_MINIMUM_AVAILABLEWinNT.h中被定義為64,系統會在需要時分配更多的TLS元素,最多可達1000多個!

1)要使用動態TLS,必須先呼叫TlsAlloc函式:

DWORD WINAPI TlsAlloc(void);

這個函式讓系統對程序中的位標誌進行檢索並找到一個FREE標誌,然後系統會將該標誌從FREE改為INUSE並讓TlsAlloc

返回該標誌在位陣列中的索引。一個DLL(或應用程式)通常將這個索引儲存在一個全域性變數中。由於這個值會在整個程序地址範圍內使用,而不是線上程範圍內使用,因此這種情況下全域性變數是一個更好的選擇。

如果TlsAlloc無法在列表中找到一個FREE標誌,那麼它會返回TLS_OUT_OF_INDEXES(在WinBase.h中被定義為0xFFFFFFFF)。

當系統建立一個執行緒的時候,會分配TLS_MINIMUM_AVAILABLEPVOID值,將它們都初始化為0,並與執行緒關聯起來。每個執行緒都有自己的PVOID陣列,陣列中的每個PVOID可以儲存任意值。在能夠將資訊儲存到執行緒的PVOID陣列中之前,我們必須知道陣列中的哪個索引可供使用---

這就是呼叫TlsAlloc的目的。TlsAlloc為我們預定了一個索引,如果為2,即TlsAlloc返回值為2,那麼無論是程序中當前正在執行的執行緒,還是今後可能會建立的執行緒,都不能再使用該索引2了。

2)為了把一個值放到執行緒的PVOID陣列中,應該呼叫TlsSetValue函式:

BOOL WINAPI TlsSetValue(

__inDWORD dwTlsIndex, //索引值,表示在陣列中的具體位置

__in_optLPVOID lpTlsValue //要設定的值

);

當一個執行緒呼叫TlsSetValue函式成功時,它會修改自己的PVOID陣列,但它無法修改另一個執行緒的TLS值。在呼叫TlsSetValue時,我們應該總是傳入前面在呼叫TlsAlloc時返回的索引。因為Windows為了效率犧牲了對輸入值的錯誤檢測。

3)為了從執行緒的陣列中取回一個值,應該呼叫函式TlsGetValue

LPVOID WINAPI TlsGetValue(

__inDWORD dwTlsIndex //索引值

);

這個函式會返回在索引為dwTlsIndexTLS元素中儲存的值。TlsGetValue只會檢視屬於呼叫執行緒的陣列。

4)當不再需要一個已經預定的TLS元素時,應該呼叫TlsFree函式:

BOOL WINAPI TlsFree(

__inDWORD dwTlsIndex //索引值

);

這個函式告訴系統已經預定的這個TLS元素現在不需要了,函式會將程序內的位標誌陣列中對應的INUSE標誌重新設回FREE。此外,函式還會將所有執行緒中該元素的內容設為0.

使用動態TLS

通常,如果DLL要使用TLS,那它會在DllMain函式處理DLL_PROCESS_ATTACH的時候呼叫TlsAlloc,在DllMain處理DLL_PROCESS_DETACH的時候呼叫TlsFree。而TlsSetValueTlsGetValue的呼叫則最有可能發生在DLL所提供的其他函式中。

而嚮應用程式中新增TLS的一種方法是直到需要時才新增。

下面是在應用程式中使用動態TLS的例項程式碼:

#include<windows.h>

#include<stdio.h>

#define THREADCOUNT 4

DWORD dwTlsIndex;

VOID ErrorExit(LPSTR);

VOID CommonFunc(VOID)

{

LPVOID lpvData;

// Retrieve a data pointer for the current thread.

lpvData = TlsGetValue(dwTlsIndex);

if ((lpvData == 0) && (GetLastError() != ERROR_SUCCESS))

ErrorExit("TlsGetValue error");

// Use the data stored for the current thread.

printf("common: thread %d: lpvData=%lx/n",

GetCurrentThreadId(), lpvData);

Sleep(5000);

}

DWORD WINAPI ThreadFunc(VOID)

{

LPVOID lpvData;

// Initialize the TLS index for this thread.

lpvData = (LPVOID) LocalAlloc(LPTR, 256);

if (! TlsSetValue(dwTlsIndex, lpvData))

ErrorExit("TlsSetValue error");

printf("thread %d: lpvData=%lx/n", GetCurrentThreadId(), lpvData);

CommonFunc();

// Release the dynamic memory before the thread returns.

lpvData = TlsGetValue(dwTlsIndex);

if (lpvData != 0)

LocalFree((HLOCAL) lpvData);

return 0;

}

int main(VOID)

{

DWORD IDThread;

HANDLE hThread[THREADCOUNT];

int i;

// Allocate a TLS index.

if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)

ErrorExit("TlsAlloc failed");

// Create multiple threads.

for (i = 0; i < THREADCOUNT; i++)

{

hThread[i] = CreateThread(NULL, // default security attributes

0,// use default stack size

(LPTHREAD_START_ROUTINE) ThreadFunc, // thread function

NULL,// no thread function argument

0,// use default creation flags

&IDThread);// returns thread identifier

// Check the return value for success.

if (hThread[i] == NULL)

ErrorExit("CreateThread error/n");

}

for (i = 0; i < THREADCOUNT; i++)

WaitForSingleObject(hThread[i], INFINITE);

TlsFree(dwTlsIndex);

return 0;

}

VOID ErrorExit (LPSTR lpszMessage)

{

fprintf(stderr, "%s/n", lpszMessage);

ExitProcess(0);

}

下面的例項程式碼是在DLL中使用TLS的:

========================DLL程式碼==========================

// The DLL code

#include<windows.h>

static DWORD dwTlsIndex; // address of shared memory

// DllMain() is the entry-point function for this DLL.

BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle

DWORD fdwReason,// reason called

LPVOID lpvReserved)// reserved

{

LPVOID lpvData;

BOOL fIgnore;

switch (fdwReason)

{

// The DLL is loading due to process

// initialization or a call to LoadLibrary.

case DLL_PROCESS_ATTACH:

// Allocate a TLS index.

if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)

return FALSE;

// No break: Initialize the index for first thread.

// The attached process creates a new thread.

相關推薦

Windows核心程式設計》の執行區域性儲存TLS

執行緒區域性儲存(Thread Local Storage,TLS)用來將資料與一個正在執行的指定執行緒關聯起來。我們在應用程式和DLL中可以使用兩種型別的TLS:動態TLS和靜態TLS。但一般來說,這

TLS--執行區域性儲存

這個東西並不陌生了,之前寫過了一個關於這個的應用,利用靜態TLS姿勢實現程式碼段靜態加密免殺或者所謂的加殼思路。地址在這:http://blog.csdn.net/u013761036/article/details/53967943今天就簡單的整理下TLS的相關概念

執行區域性儲存TLS)

1.      執行緒區域性儲存(TLS)是簡單的儲存執行緒區域性資料的系統。 2.      利用TLS可以為每個程序關聯一個數據。一般來說,為執行緒分配TLS索引在主執行緒中完成,而分配的索引值應儲存在全域性變數中。 3.      使用到的函式介紹: 1)      主執行緒呼叫TlsAlloc為執行

TLS執行區域性儲存--thread_specific_ptr

大多數函式都不是可重入的。這也就是說在某一個執行緒已經呼叫了一個函式時,如果你再呼叫同一個函式,那麼這樣是不安全的。一個不可重入的函式通過連續的呼叫來儲存靜態變數或者是返回一個指向靜態資料的指標。 舉例來說,std::strtok就是不可重入的,因為它使用靜態變數來儲存要被分

TLS(執行區域性儲存)模擬控制代碼的實現

這是今天做一個郵件傳送模組把一個類包裝成Win32 API形式呼叫時想到的。簡單的實現了一下控制代碼的概念,對於理解核心物件和控制代碼的概念有一定幫助,看起來還頗像那麼回事,呵呵。不過這裡的“控制代碼表”是藉助了TLS實現的。 值得一提的是,這個小例子也實現了引用計數的概念

[轉]大內高手—共享記憶體與執行區域性儲存

2007-11-06 城裡的人想出去,城外的人想進來。這是《圍城》裡的一句話,它可能比《圍城》本身更加有名。我想這句話的前提是,要麼住在城裡,要麼住 在城外,二者只能居其一。否則想住在城裡就可以住在城裡,想住在城外就可以住在

執行區域性儲存

執行緒區域性儲存 英文為Thread Local Storage [1] ,縮寫為TLS。為什麼要有TLS?原因在於,全域性變數與函式內定義的靜態變數,是各個執行緒都可以訪問的共享變數。 在一個執行緒修改的記憶體內容,對所有執行緒都生效。這是一個優點也是一個缺點。說它是優點,執行緒的資料

Linux中的執行區域性儲存

在Linux系統中使用C/C++進行多執行緒程式設計時,我們遇到最多的就是對同一變數的多執行緒讀寫問題,大多情況下遇到這類問題都是通過鎖機制來處理,但這對程式的效能帶來了很大的影響,當然對於那些系統原生支援原子操作的資料型別來說,我們可以使用原子操作來處理,這能

Windows 核心 程序 執行 初探~

對於WIndows的初學者的我來說,執行緒、程序是一個很抽象的概念,理解他們是比較困難的。接下來我就講一下程序、執行緒的概念及關係。 查閱資料得出,在windows下這兩個概念都和系統核心物件脫不開關係。 那麼什麼是系統核心物件呢? 核心物件是系統核心分配的一個記憶體塊,該

Windows核心執行的排程,優先順序,親緣性

1 排程          Windows不是實時作業系統,它是搶佔式多執行緒作業系統。在假設所有優先順序相同的情況下,CPU對執行緒的排程原則是每隔20m就會切換到下一個執行緒,根據Context中的IP和SP來接著執行上次的東西。Windows永遠不會讓1個執行緒去獨佔

每天進步一點點——Linux中的執行區域性儲存(二)

凡是帶有__thread的變數,每個執行緒都擁有該變數的一份拷貝,且互不干擾。執行緒區域性儲存中的變數將一直存在,直至執行緒終止,當執行緒終止時會自動釋放這一儲存。__thread並不是所有資料型別都可以使用的,因為其只支援POD(Plain old data structure)[1]型別,不支援clas

Windows核心程式設計執行

執行緒組成兩部分: 1. 一個執行緒的核心物件,作業系統用它管理執行緒。 2. 一個執行緒棧,用於維護執行緒執行時所需的所有函式引數和區域性變數。 何時建立執行緒?舉例: 作業系統的Windows Indexing Services,磁碟碎片整理程式等,都是使用多執行緒進行效能優化的

Windows核心程式設計筆記(七) 執行排程 優先順序 關聯性

在搶佔式多工作業系統中,執行緒的執行是有限制的,系統會排程一個執行緒在一個時間塊內佔用CPU,在時間到了之後將執行緒的上下文(CONTEXT結構,儲存執行緒切換前的CPU個暫存器的值)儲存到執行緒核心物件中,從另一個可排程執行緒的CONTEXT中獲取屬於它的CPU各暫存器

Windows核心程式設計 第九章 執行核心物件的同步(上)

第9章 執行緒與核心物件的同步     上一章介紹瞭如何使用允許執行緒保留在使用者方式中的機制來實現執行緒同步的方法。使用者方式同步的優點是它的同步速度非常快。如果強調執行緒的執行速度,那麼首先應該確

windows核心程式設計之使用執行APC回撥安全退出多個等待執行

前言 程式開發中經常遇到需要這些情況:輔助執行緒正在等待核心物件的觸發,主執行緒需要強制終止輔助執行緒。我們常常做的就是使用:TerminateThread來強制終止執行緒。這樣做當然是不太好的,強制

Windows核心程式設計 第七章 執行的排程、優先順序和親緣性(下)

7.6 運用結構環境     現在應該懂得環境結構線上程排程中所起的重要作用了。環境結構使得系統能夠記住執行緒的狀態,這樣,當下次執行緒擁有可以執行的C P U時,它就能夠找到它上次中斷執行的地方。

Windows核心程式設計_遠執行方式實現Dll注入

之前有介紹過HOOK的方式注入,這次介紹以其它方式注入,而無須HOOK,要知道在Windows這個浩蕩的海洋裡,API就是寶藏,找到足夠多的寶藏那麼你就是海賊王~! 實現思路如下: 首先開啟一個程序的地址空間,然後開闢一塊空間將動態庫copy進去,然後找到動態庫要呼叫

Windows核心程式設計筆記(5)----執行排程,優先順序

1、作業系統執行緒排程過程 每個執行緒都有一個上下文CONTEXT結構體,儲存線上程的核心物件中,這個上下文中儲存了執行緒上一次執行時CPU暫存器的狀態。每隔固定時間,Windows會檢視所有當前存在的執行緒核心物件,其中只有一些是可排程的。Windows在可排程的執行緒中

Windows核心程式設計學習六:程序優先順序組和執行優先順序

注:原始碼為學習《Windows核心程式設計》的一些嘗試,非原創。若能有助於一二訪客,幸甚。 1.基本框架 使用CreateDialog和MAKEINTERESOURCE /* * File: SchedLab.cpp * Time: 2013-04-20 * D

Java多執行程式設計核心技術 —— 執行間通訊

執行緒是作業系統中獨立的個體,但這些個體如果不經過特殊的處理就不能成為一個整體。執行緒間的通訊就是成為整體的比用方案之一,可以說,使執行緒間進行通訊後,系統之間的互動性會更強大,在大大提高CPU利用率的同時還會使程式設計師對個執行緒任務在處理的過程中進行有效的把控與監督。 1、