Inside Microsoft Windows 2000, Third Edition (Microsoft Programming Series)
A thread's life cycle starts when a program creates a new thread. The request filters down to the Windows 2000 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 Win32 CreateThread function in Kernel32.dll to create a Win32 thread. The work that occurs inside the Windows 2000 executive are substeps of step 3, and the work that occurs in the context of the new thread are substeps of step 7. Because process creation includes creating a thread, some of the information here is repeated from the earlier description of the flow of CreateProcess.
- CreateThread creates a user-mode stack for the thread in the process's address space.
- CreateThread initializes the thread's hardware context (CPU architecture-specific). (For further information on the thread context block, see the Win32 API reference documentation on the CONTEXT structure.)
- NtCreateThread is called to create the executive thread object in the suspended state. The following steps execute in kernel mode inside the Windows 2000 executive and kernel:
- The thread count in the process object is incremented.
- An executive thread block (ETHREAD) is created and initialized.
- A thread ID is generated for the new thread.
- The thread's kernel stack is allocated from the nonpaged pool.
- The TEB is set up in the user-mode address space of the process.
- The thread start address (KiThreadStartup) is stored on the kernel stack. (The kernel stack address is stored in the KTHREAD.) The user's specified Win32 start address is stored in the ETHREAD block.
- KeInitializeThread is called to set up the KTHREAD block. The thread's initial and current base priorities are set to the process's base priority, and its affinity and quantum are set to that of the process. This function also sets the initial thread ideal processor based on the process thread seed (a random number set during execution of CreateProcess). The seed is then incremented so that each thread in the process will have a different ideal processor, assuming the system has more than one. KeInitializeThread next sets the thread's state to Initialized and initializes the machine-dependent hardware context for the thread, including the context, trap, and exception frames. The thread's context is set up so that the thread will start in kernel mode in SwapContext, the context switch code. SwapContext loads the thread's context from the thread's kernel stack, which results in the thread starting its execution in the systemwide startup routine KiThreadStartup (described in step 6a), the function that was stored in the stack by step 3f.
- Any registered systemwide thread creation notification routines are called.
- The thread's access token is set to point to the process access token, and an access check is made to determine whether the caller has the right to create the thread. This check will always succeed if you're creating a thread in the local process but might fail if you're using CreateRemoteThread to create a thread in another process and the process creating the thread doesn't have the debug privilege enabled.
- CreateThread notifies the Win32 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 following additional steps (in the context of the new thread) before calling the actual user's specified start address. (A flowchart of this final part of thread creation is shown in Figure 6-10.)
- KiThreadStartup lowers the thread's IRQL level from DPC/dispatch level to APC level and then calls the system initial thread routine, PspUserThreadStartup. The user-specified thread start address is passed as a parameter to this routine.
- The system initial thread routine enables working set expansion and then queues a user-mode APC to run the image loader initialization routine (LdrInitializeThunk in Ntdll.dll). The IRQL is lowered to 0, thus causing the pending APC to fire.
- The loader initialization routine then performs a number of additional thread-specific initialization steps, such as calling loaded DLLs to notify them of the new thread. (The detailed steps of the initialization of the Win32 subsystem DLLs, such as USER32, KERNEL32, and GDI32, are beyond the scope of this book.)
- If the process has a debugger attached, the thread startup routine suspends all other active threads in the process and notifies the Win32 subsystem so that it can deliver the thread startup debug event (CREATE_THREAD_DEBUG_INFO) to the appropriate debugger process. The startup routine then waits for the Win32 subsystem to get the reply from the debugger (via the ContinueDebugEvent function). When the Win32 subsystem receives a reply from the debugger, it in turn replies to the thread startup routine and all the threads are resumed.
- Finally, the main thread begins execution in user mode at the entry point to the image being run. Execution begins when the trap that started the thread execution, using a trap frame (built earlier when the kernel thread block was being initialized) that specifies previous mode as user and the PC as the start address of the thread, is dismissed.
Figure 6-10 In-context thread initialization