Nuta
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować się zalogować lub zmienić katalog.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
W tej sekcji przedstawiono użycie funkcji punktu wejścia biblioteki DLL do skonfigurowania indeksu magazynu lokalnego wątku (TLS) w celu zapewnienia magazynu prywatnego dla każdego wątku wielowątkowego procesu.
Indeks TLS jest przechowywany w zmiennej globalnej, dzięki czemu jest dostępny dla wszystkich funkcji DLL. W tym przykładzie przyjęto założenie, że dane globalne biblioteki DLL nie są udostępniane, ponieważ indeks TLS nie musi być taki sam dla każdego procesu, który ładuje bibliotekę DLL.
Funkcja punktu wejścia używa funkcji TlsAlloc do przydzielenia indeksu TLS za każdym razem, gdy proces ładuje bibliotekę DLL. Każdy wątek może następnie użyć tego indeksu do przechowywania wskaźnika do własnego bloku pamięci.
Gdy funkcja punktu wejścia jest wywoływana z wartością DLL_PROCESS_ATTACH, kod wykonuje następujące akcje:
- Używa funkcji TlsAlloc w celu przydzielenia indeksu TLS.
- Przydziela blok pamięci do użycia wyłącznie przez wątek początkowy procesu.
- Używa indeksu TLS w wywołaniu funkcji TlsSetValue, aby przechować adres bloku pamięci w gnieździe TLS związanym z tym indeksem.
Za każdym razem, gdy proces tworzy nowy wątek, funkcja punktu wejścia jest wywoływana z wartością DLL_THREAD_ATTACH. Funkcja punktu wejścia przydziela następnie blok pamięci dla nowego wątku i przechowuje do niego wskaźnik przy użyciu indeksu TLS.
Gdy funkcja wymaga dostępu do danych skojarzonych z indeksem TLS, określ indeks w wywołaniu funkcji TlsGetValue. Spowoduje to pobranie zawartości gniazda PROTOKOŁU TLS dla wątku wywołującego, co w tym przypadku jest wskaźnikiem do bloku pamięci dla danych. Gdy proces używa łączenia w czasie ładowania dla tej biblioteki DLL, funkcja punktu wejścia jest wystarczająca do zarządzania pamięcią lokalną wątków. Problemy mogą wystąpić w przypadku procesu, który używa łączenia w czasie wykonywania, ponieważ funkcja punktu wejścia nie jest wywoływana dla wątków istniejących przed wywołaniem funkcji LoadLibrary, w wyniku czego pamięć TLS nie jest przydzielana dla tych wątków. W tym przykładzie rozwiązano ten problem, sprawdzając wartość zwróconą przez funkcję TlsGetValue i przydzielając pamięć, jeśli wartość wskazuje, że miejsce protokołu TLS dla tego wątku nie jest ustawione.
Gdy każdy wątek nie musi już używać indeksu TLS, musi zwolnić pamięć, której wskaźnik jest przechowywany w gnieździe TLS. Gdy wszystkie wątki zostały zakończone przy użyciu indeksu TLS, użyj funkcji TlsFree, aby zwolnić indeks.
Po zakończeniu wątku funkcja punktu wejścia jest wywoływana z wartością DLL_THREAD_DETACH, a pamięć dla tego wątku zostaje zwolniona. Po zakończeniu procesu funkcja punktu wejścia jest wywoływana z wartością DLL_PROCESS_DETACH, a pamięć, do której odwołuje się wskaźnik w indeksie TLS, jest zwalniana.
// 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.
case DLL_THREAD_ATTACH:
// Initialize the TLS index for this thread.
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData != NULL)
fIgnore = TlsSetValue(dwTlsIndex, lpvData);
break;
// The thread of the attached process terminates.
case DLL_THREAD_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
break;
// DLL unload due to process termination or FreeLibrary.
case DLL_PROCESS_DETACH:
// Release the allocated memory for this thread.
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData != NULL)
LocalFree((HLOCAL) lpvData);
// Release the TLS index.
TlsFree(dwTlsIndex);
break;
default:
break;
}
return TRUE;
UNREFERENCED_PARAMETER(hinstDLL);
UNREFERENCED_PARAMETER(lpvReserved);
}
// The export mechanism used here is the __declspec(export)
// method supported by Microsoft Visual Studio, but any
// other export method supported by your development
// environment may be substituted.
#ifdef __cplusplus // If used by C++ code,
extern "C" { // we need to export the C interface
#endif
__declspec(dllexport)
BOOL WINAPI StoreData(DWORD dw)
{
LPVOID lpvData;
DWORD * pData; // The stored memory pointer
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData == NULL)
{
lpvData = (LPVOID) LocalAlloc(LPTR, 256);
if (lpvData == NULL)
return FALSE;
if (!TlsSetValue(dwTlsIndex, lpvData))
return FALSE;
}
pData = (DWORD *) lpvData; // Cast to my data type.
// In this example, it is only a pointer to a DWORD
// but it can be a structure pointer to contain more complicated data.
(*pData) = dw;
return TRUE;
}
__declspec(dllexport)
BOOL WINAPI GetData(DWORD *pdw)
{
LPVOID lpvData;
DWORD * pData; // The stored memory pointer
lpvData = TlsGetValue(dwTlsIndex);
if (lpvData == NULL)
return FALSE;
pData = (DWORD *) lpvData;
(*pdw) = (*pData);
return TRUE;
}
#ifdef __cplusplus
}
#endif
Poniższy kod demonstruje użycie funkcji DLL zdefiniowanych w poprzednim przykładzie.
#include <windows.h>
#include <stdio.h>
#define THREADCOUNT 4
#define DLL_NAME TEXT("testdll")
VOID ErrorExit(LPSTR);
extern "C" BOOL WINAPI StoreData(DWORD dw);
extern "C" BOOL WINAPI GetData(DWORD *pdw);
DWORD WINAPI ThreadFunc(VOID)
{
int i;
if(!StoreData(GetCurrentThreadId()))
ErrorExit("StoreData error");
for(i=0; i<THREADCOUNT; i++)
{
DWORD dwOut;
if(!GetData(&dwOut))
ErrorExit("GetData error");
if( dwOut != GetCurrentThreadId())
printf("thread %d: data is incorrect (%d)\n", GetCurrentThreadId(), dwOut);
else printf("thread %d: data is correct\n", GetCurrentThreadId());
Sleep(0);
}
return 0;
}
int main(VOID)
{
DWORD IDThread;
HANDLE hThread[THREADCOUNT];
int i;
HMODULE hm;
// Load the DLL
hm = LoadLibrary(DLL_NAME);
if(!hm)
{
ErrorExit("DLL failed to load");
}
// 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");
}
WaitForMultipleObjects(THREADCOUNT, hThread, TRUE, INFINITE);
FreeLibrary(hm);
return 0;
}
VOID ErrorExit (LPSTR lpszMessage)
{
fprintf(stderr, "%s\n", lpszMessage);
ExitProcess(0);
}
Tematy pokrewne