21.1 Dynamic TLS
21.1.1 Why use thread-local storage
When writing a multithreaded program, you want to store some thread-private data, and we know that the data belonging to each thread is private, including the thread's stack and the current register, but both of these are very unreliable, and the stack is changed when each function exits and enters, and the registers are much less pathetic. What if we want to use a global variable in a thread, but want the global variable to be private and not shared by all threads? This will require the use of thread-local storage (Tls,thread local Storage).
21.1.2 Dynamic TLS
(1) Each process has a set of flags being used, a total of tls_minimum_available. Each flag can be set to free or inuse, indicating whether the TLS element is in use. (Note that this set of marks belongs to the process)
(2) When the system creates a thread, the thread is assigned to the thread's own pvoid array (a total of tls_minimum_availbale elements) associated with the thread, and each pvoid in the array can hold any value.
21.1.3 using dynamic TLS
(1) Call the TlsAlloc function
① The function retrieves the bit flags in the system process and finds a free flag, then changes the flag from free to InUse and returns the index in the in-place array, usually saving the index in a global variable, because this value is used throughout the process, not in the thread range.
② If TlsAlloc cannot find a free flag in the list, it returns tls_out_of_indexes.
③ above is the work of tlsalloc99%, the remaining 1% of the work is to go through each thread in the process before the function returns, and set the corresponding modesty to 0 for each thread in the TLS array for the newly allocated index (see the following analysis for specific reasons).
(2) Call TlsSetValue (Dwtlsindex,pvtlsvalue) to place a value in the thread's array
① The function puts a pvoid value that Pvtlsvalue flags in the thread's array, Dwtlsindex specifies the exact position in the array (obtained by TlsAlloc)
② when a thread calls TlsSetValue, it modifies its own array. However, it cannot modify the value in the TLS array of another thread.
(3) Retrieve a value from the array of threads: PVOID TlsGetValue (Dwtlsindex)
① is similar to TlsSetValue, the array that belongs to the calling thread is viewed in TlsGetValue
(4) Release the TLS element that has been booked: TlsFree (Dwtlsindex)
① This function resets the INUSE flag corresponding to the array of bit flags in the process back to the free
② The function also sets the content of the element in all threads to 0.
③ attempting to release a TLS element that has not been assigned will result in an error
21.1.4 write a similar _tcstok_s function
DWORD G_dwtlsindex;//assume that this global variable is initialized by the TlsAlloc function .voidMyFunction (psomestruct psomestruct) {if(Psomestruct! =NULL) { //the caller is enabling the function, as if the Strok function passed in non-null for the first time,//pass to NULL later//Check if you have allocated storage space for your data if(TlsGetValue (g_dwtlsindex) = =NULL)//the first time a thread calls the function, the space is not yet assigned//before the TlsAlloc function returns, the G_dwtlsindex element of all threads in the process will be//Zero, to ensure that the code will not be error-free phenomenon! //TLS ensures that the allocated space is only associated with the calling threadTlsSetValue (G_dwtlsindex, HeapAlloc (GetProcessHeap (),0,sizeof(*psomestruct)); } //Save the incoming psomestruct data in the storage space that was just related to the calling threadmemcpy (TlsGetValue (G_dwtlsindex), Psomestruct,sizeof(*psomestruct)); } Else{ //the caller has already called the function for the second (or more) time, passing in a null parameter//Remove DataPsomestruct =(psomestruct) TlsGetValue (G_dwtlsindex); //The following can start psomestruct this data. ... }}
21.2 Static TLS
(1) Declaration of a static TLS variable
①__thread int NUMBER;//GCC using __thread keyword declaration
②__declspec (thread) int number; Msvc using __declspec (thread) declaration
(2) How to implement static TLS in Windows
① for Windows systems, a global or static variable is normally placed in the ". Data" or ". BSS" segment, but when we use __declspec (thread) to define a thread-private variable, the compiler places the variables in the PE file. TLS "section.
② when the system starts a new thread, it allocates a sufficient amount of space from the process's heap and copies the contents of the ". TLS" segment into this space, so that each thread has its own separate ". TLS" copy. So for the same variable defined with __declspec (thread), they are not the same address in different threads.
③ for a TLS variable, it could be a C + + global object, so that each thread is not just replicating the ". TLS" content at startup, it needs to initialize the TLS objects, and must call their global constructors one by one, and when the threads exit, they are also destructor- , just as normal global objects are constructed and refactored as the process starts and exits.
The structure of the ④windows PE file has a structure called the Data directory. It has a total of 16 elements, one of which is labeled Image_direct_entry_tls, and the address and length stored in this element are the address and length of the TLS table (image_tls_directory structure). The TLS table holds the address of the constructors and destructors for all TLS variables, and the Windows system constructs and reconstructs the TLS variables each time the thread starts or exits, based on the contents of the TLS table. The TLS table itself is often located in the ". Rdata" segment of the PE file.
⑤ Another problem is, since the same TLS variable has different addresses for each thread, how do threads access these variables? In fact, for each Windows thread, the system creates a structure about thread information called the Thread Environment block (teb,thread environment blocks). This structure holds information about the thread's stack address, thread ID, and so on, where a domain is a TLS array, and its offset in TEB is 0x2c. For each thread, the segment referred to by the FS segment Register of x86 is the teb of the thread, so that the address of the TLS array of the threads can be accessed through fs:[0x2c].
⑥ This TLS array is a fixed size for each thread and typically has 64 elements. The first element of the TLS array is the address of the ". TLS" copy that points to the thread. So the step to get a TLS variable address is to get the address of the TLS array first through FS:[0X2C] and then the address of the ". TLS" copy based on the address of the TLS array, and then add the offset in the ". TLS" segment to the address in the thread of the TLS variable.
21st Thread Local Storage area