One set of kernel objects, called control objects, establishes semantics for controlling various operating system functions. This set includes the APC object, the deferred procedure call (DPC) object, and several objects the I/O manager uses, such as the interrupt object.
Another set of kernel objects, known as dispatcher objects, incorporates synchronization capabilities that alter or affect thread scheduling. The dispatcher objects include the kernel thread, mutex (called mutant internally), event, kernel event pair, semaphore, timer, and waitable timer. The executive uses kernel functions to create instances of kernel objects, to manipulate them, and to construct the more complex objects it provides to user mode. Objects are explained in more detail in Chapter 3, and processes and threads are described in Chapter 5.
Kernel Processor Control Region and Control Block (KPCR and KPRCB)
The kernel uses a data structure called the processor control region, or KPCR, to store processor-specific data. The KPCR contains basic information such as the processor’s interrupt dispatch table (IDT), task-state segment (TSS), and global descriptor table (GDT). It also includes the interrupt controller state, which it shares with other modules, such as the ACPI driver and the HAL. To provide easy access to the KPCR, the kernel stores a pointer to it in the fs register on 32-bit Windows and in the gs register on an x64 Windows system. On IA64 systems, the KPCR is always located at 0xe0000000ffff0000.
The KPCR also contains an embedded data structure called the kernel processor control block (KPRCB). Unlike the KPCR, which is documented for third-party drivers and other internal Windows kernel components, the KPRCB is a private structure used only by the kernel code in Ntoskrnl.exe. It contains scheduling information such as the current, next, and idle threads scheduled for execution on the processor; the dispatcher database for the processor (which includes the ready queues for each priority level); the DPC queue; CPU vendor and identifier information (model, stepping, speed, feature bits); CPU and NUMA topology (node information, cores per package, logical processors per core, and so on); cache sizes; time accounting information (such as the DPC and interrupt time); and more. The KPRCB also contains all the statistics for the processor, such as I/O statistics, cache manager statistics (see Chapter 11, “Cache Manager,” in Part 2 for a description of these), DPC statistics, and memory manager statistics. (See Chapter 10 in Part 2 for more information.) Finally, the KPRCB is sometimes used to store cache-aligned, per-processor structures to optimize memory access, especially on NUMA systems. For example, the nonpaged and paged-pool system look-aside lists are stored in the KPRCB.
EXPERIMENT: Viewing the KPCR and KPRCB
You can view the contents of the KPCR and KPRCB by using the !pcr and !prcb kernel debugger commands. If you don’t include flags, the debugger will display information for CPU 0 by default; otherwise, you can specify a CPU by adding its number after the command (for example, !pcr 2). The following example shows what the output of the !pcr and !prcb commands looks like. If the system had pending DPCs, those would also be shown.lkd> !pcr
KPCR for Processor 0 at 81d09800:
Major 1 Minor 1
NtTib.ExceptionList: 9b31ca3c
NtTib.StackBase: 00000000
NtTib.StackLimit: 00000000
NtTib.SubSystemTib: 80150000
NtTib.Version: 1c47209e
NtTib.UserPointer: 00000001
NtTib.SelfTib: 7ffde000
SelfPcr: 81d09800
Prcb: 81d09920
Irql: 00000002
IRR: 00000000
IDR: ffffffff
InterruptMode: 00000000
IDT: 82fb8400
GDT: 82fb8000
TSS: 80150000
CurrentThread: 86d317e8
NextThread: 00000000
IdleThread: 81d0d640
DpcQueue:
lkd> !prcb
PRCB for Processor 0 at 81d09920:
Current IRQL -- 0
Threads-- Current 86d317e8 Next 00000000
Idle 81d0d640
Number 0 SetMember 1
Interrupt Count -- 294ccce0
Times -- Dpc 0002a87f Interrupt 00010b87
Kernel 026270a1 User 00140e5e
You can use the dt command to directly dump the _KPCR and _KPRCB data structures because both debugger commands give you the address of the structure (shown in bold for clarity in the previous output). For example, if you wanted to determine the speed of the processor, you could look at the MHz field with the following command:lkd> dt nt!_KPRCB 81d09920 MHz
+0x3c4 MHz : 0xbb4
lkd> ? bb4
Evaluate expression: 2996 = 00000bb4
On this machine, the processor was running at about 3 GHz.