Processes, Threads, and Jobs in the Windows Operating System
- 6/17/2009
Thread Internals
Now that we’ve dissected processes, let’s turn our attention to the structure of a thread. Unless explicitly stated otherwise, you can assume that anything in this section applies to both user-mode threads and kernel-mode system threads (which are described in Chapter 2).
Data Structures
At the operating-system level, a Windows thread is represented by an executive thread (ETHREAD) block, which is illustrated in Figure 5-7. The ETHREAD block and the structures it points to exist in the system address space, with the exception of the thread environment block (TEB), which exists in the process address space (again, because user-mode components need to have access to it).
In addition, the Windows subsystem process (Csrss) also maintains a parallel structure for each thread created in a Windows subsystem application. Also, for threads that have called a Windows subsystem USER or GDI function, the kernel-mode portion of the Windows subsystem (Win32k.sys) maintains a per-thread data structure (called the W32THREAD structure) that the ETHREAD block points to.
Figure 5-7. Structure of the executive thread block
Most of the fields illustrated in Figure 5-7 are self-explanatory. The first field is the kernel thread (KTHREAD) block. Following that are the thread identification information, the process identification information (including a pointer to the owning process so that its environment information can be accessed), security information in the form of a pointer to the access token and impersonation information, and finally, fields relating to ALPC messages and pending I/O requests. As you can see in Table 5-9, some of these key fields are covered in more detail elsewhere in this book. For more details on the internal structure of an ETHREAD block, you can use the kernel debugger dt command to display the format of the structure.
Table 5-9. Key Contents of the Executive Thread Block
Field Name |
Value Taken from Image Header |
Additional Information |
KTHREAD |
See Table 5-10. |
|
Thread time |
Thread create and exit time information. |
|
Process identification |
Process ID and pointer to EPROCESS block of the process that the thread belongs to. |
|
Start address |
Address of thread start routine. |
|
Impersonation information |
Access token and impersonation level (if the thread is impersonating a client). |
Chapter 6 |
ALPC information |
Message ID that the thread is waiting for and address of message. |
Advanced local procedure calls (ALPC) (Chapter 3) |
I/O information |
List of pending I/O request packets (IRPs). |
I/O system (Chapter 7) |
Let’s take a closer look at two of the key thread data structures referred to in the preceding text: the KTHREAD block and the TEB. The KTHREAD block (also called the TCB, or thread control block) contains the information that the Windows kernel needs to access to perform thread scheduling and synchronization on behalf of running threads. Its layout is illustrated in Figure 5-8.
Figure 5-8. Structure of the kernel thread block
The key fields of the KTHREAD block are described briefly in Table 5-10.
Table 5-10. Key Contents of the KTHREAD Block
Element |
Description |
Additional Reference |
Dispatcher header |
Because the thread is an object that can be waited on, it starts with a standard kernel dispatcher object header. |
Kernel dispatcher objects (Chapter 3) |
Execution time |
Total user and kernel CPU time. |
|
Cycle time |
Total CPU cycle time. |
Thread scheduling |
Pointer to kernel stack information |
Base and upper address of the kernel stack. |
Memory management (Chapter 9) |
Pointer to system service table |
Each thread starts out with this field service table pointing to the main system service table (KeServiceDescriptorTable). When a thread first calls a Windows GUI service, its system service table is changed to one that includes the GDI and USER services in Win32k.sys. |
System service dispatching (Chapter 3) |
Scheduling information |
Base and current priority, quantum target, quantum reset, affinity mask, ideal processor, deferred processor, next processor, scheduling state, freeze count, suspend count, adjust increment and adjust reason. |
Thread scheduling |
Wait blocks |
The thread block contains four built-in wait blocks so that wait blocks don’t have to be allocated and initialized each time the thread waits for something. (One wait block is dedicated to timers.) |
Synchronization (Chapter 3) |
Wait information |
List of objects the thread is waiting for, wait reason, IRQL at the time of wait, result of the wait, and time at which the thread entered the wait state. |
Synchronization (Chapter 3) |
Mutant list |
List of mutant objects the thread owns. |
Synchronization (Chapter 3) |
APC queues |
List of pending user-mode and kernel-mode APCs, alerted flag, and flags to disable APCs. |
Asynchronous procedure call (APC) interrupts (Chapter 3) |
Timer block |
Built-in timer block (also a corresponding wait block). |
|
Suspend APC and semaphore |
Built-in APC and semaphore used when suspending and resuming a thread. |
Synchronization (Chapter 3) |
Queue |
Pointer to queue object that the thread is associated with. |
Synchronization (Chapter 3) |
Gate |
Pointer to gate object that the thread is waiting on. |
Synchronization (Chapter 3) |
Pointer to TEB |
Thread ID, TLS and FLS information, PEB pointer, and Winsock, RPC, GDI, OpenGL, and other user-mode information. |
The TEB, illustrated in Figure 5-9, is the only data structure explained in this section that exists in the process address space (as opposed to the system space).
The TEB stores context information for the image loader and various Windows DLLs. Because these components run in user mode, they need a data structure writable from user mode. That’s why this structure exists in the process address space instead of in the system space, where it would be writable only from kernel mode. You can find the address of the TEB with the kernel debugger !thread command.
Figure 5-9. Fields of the thread environment block
Kernel Variables
As with processes, a number of Windows kernel variables control how threads run. Table 5-11 shows the kernel-mode kernel variables that relate to threads.
Table 5-11. Thread-Related Kernel Variables
Variable |
Type |
Description |
PspCreateThreadNotifyRoutine |
Array of executive callback objects |
Array of callback objects describing the routines to be called on thread creation and deletion (maximum of 64) |
PspCreateThreadNotifyRoutineCount |
32-bit integer |
Count of registered thread-notification routines |
Performance Counters
Most of the key information in the thread data structures is exported as performance counters, which are listed in Table 5-12. You can extract much information about the internals of a thread just by using the Reliability and Performance Monitor in Windows.
Table 5-12. Thread-Related Performance Counters
Object: Counter |
Function |
Process: Priority Base |
Returns the current base priority of the process. This is the starting priority for threads created within this process. |
Thread: % Privileged Time |
Describes the percentage of time that the thread has run in kernel mode during a specified interval. |
Thread: % Processor Time |
Describes the percentage of CPU time that the thread has used during a specified interval. This count is the sum of % Privileged Time and % User Time. |
Thread: % User Time |
Describes the percentage of time that the thread has run in user mode during a specified interval. |
Thread: Context Switches/Sec |
Returns the number of context switches per second that the system is executing. |
Thread: Elapsed Time |
Returns the amount of CPU time (in seconds) that the thread has consumed. |
Thread: ID Process |
Returns the process ID of the thread’s process. |
Thread: ID Thread |
Returns the thread’s thread ID. This ID is valid only during the thread’s lifetime because thread IDs are reused. |
Thread: Priority Base |
Returns the thread’s current base priority. This number might be different from the thread’s starting base priority. |
Thread: Priority Current |
Returns the thread’s current dynamic priority. |
Thread: Start Address |
Returns the thread’s starting virtual address (Note: This address will be the same for most threads.) |
Thread: Thread State |
Returns a value from 0 through 7 relating to the current state of the thread. |
Thread: Thread Wait Reason |
Returns a value from 0 through 19 relating to the reason why the thread is in a wait state. |
Relevant Functions
Table 5-13 shows the Windows functions for creating and manipulating threads. This table doesn’t include functions that have to do with thread scheduling and priorities—those are included in the section Thread Scheduling later in this chapter.
Table 5-13. Windows Thread Functions
Function |
Description |
CreateThread |
Creates a new thread |
CreateRemoteThread |
Creates a thread in another process |
OpenThread |
Opens an existing thread |
ExitThread |
Ends execution of a thread normally |
TerminateThread |
Terminates a thread |
IsThreadAFiber |
Returns whether the current thread is a fiber |
GetExitCodeThread |
Gets another thread’s exit code |
GetThreadTimes |
Returns timing information for a thread |
QueryThreadCycleTime |
Returns CPU clock cycle information for a thread |
GetCurrentThread |
Returns a pseudo handle for the current thread |
GetCurrentProcessId |
Returns the thread ID of the current thread |
GetThreadId |
Returns the thread ID of the specified thread |
Get/SetThreadContext |
Returns or changes a thread’s CPU registers |
GetThreadSelectorEntry |
Returns another thread’s descriptor table entry (applies only to x86 systems) |
Birth of a Thread
A thread’s life cycle starts when a program creates a new thread. The request filters down to the Windows executive, where the process manager allocates space for a thread object and calls the kernel to initialize the kernel thread block. The steps in the following list are taken inside the Windows CreateThread function in Kernel32.dll to create a Windows thread.
CreateThread converts the Windows API parameters to native flags and builds a native structure describing object parameters (OBJECT_ATTRIBUTES). See Chapter 3 for more information.
CreateThread builds an attribute list with two entries: client ID and TEB address. This allows CreateThread to receive those values once the thread has been created. (For more information on attribute lists, see the section Flow of CreateProcess earlier in this chapter.)
NtCreateThreadEx is called to create the user-mode context and probe and capture the attribute list. It then calls PspCreateThread to create a suspended executive thread object. For a description of the steps performed by this function, see the descriptions of Stage 3 and Stage 5 in the section Flow of CreateProcess.
CreateThread allocates an activation stack for the thread used by side-by-side assembly support. It then queries the activation stack to see if it requires activation, and does so if needed. The activation stack pointer is saved in the new thread’s TEB.
CreateThread notifies the Windows subsystem about the new thread, and the subsystem does some setup work for the new thread.
The thread handle and the thread ID (generated during step 3) are returned to the caller.
Unless the caller created the thread with the CREATE_SUSPENDED flag set, the thread is now resumed so that it can be scheduled for execution. When the thread starts running, it executes the steps described in the earlier section Stage 7: Performing Process Initialization in the Context of the New Process before calling the actual user’s specified start address.