The NT Kernel component determines when a particular sequence of code is run according to one of the following prioritizing criteria:
Every thread in the system has an associated priority attribute. In general, most threads in the system have variable priority attributes: they are always preemptible and are scheduled to run round-robin with all other threads currently at the same priority level. Some threads in the system have real-time priority attributes: these time-critical threads are preemptible by any thread with a higher real-time priority attribute, but otherwise run until they relinquish control.
Whatever its priority attribute, any thread in the system can be preempted when hardware and certain kinds of software interrupts occur.
The NT Kernel also prioritizes hardware and software interrupts so that some kernel-mode code runs at higher IRQLs, thereby making it, including most NT drivers, have a higher scheduling priority than all threads in the system.
The particular IRQL at which a piece of kernel-mode code executes determines its hardware priority. Such a piece of code is always interruptible: an interrupt with a higher IRQL value can occur at any time, thereby causing another piece of kernel-mode code with the system-assigned, higher IRQL to be run immediately on that processor. In other words, when a piece of code runs at a given IRQL, the Kernel masks off all interrupt vectors with a lesser or equal IRQL value on the processor.
In general, threads run at PASSIVE_LEVEL IRQL: no interrupt vectors are masked. Software interrupts are assigned relatively low IRQL values (APC_LEVEL, DISPATCH_LEVEL, or, for kernel debugging, WAKE_LEVEL). Device interrupts have higher IRQL values, and the Kernel reserves the highest IRQL values for system-critical interrupts such as the system clock or bus-error interrupts.
Some kernel-mode support routines also run at IRQL PASSIVE_LEVEL both because kernel-mode components can set up their own threads and because some kernel-mode support routines are implemented as pageable code.
Certain standard driver routines usually run at IRQL PASSIVE_LEVEL, as well. However, most NT driver routines run either at IRQL DISPATCH_LEVEL or, for a device driver, at device IRQL (also called DIRQL).
For performance reasons (avoiding context switches), very few NT drivers actually set up their own threads. Consequently, NT driver routines usually execute in the context of whatever thread happens to be current when the driver is called to get some work done: that is, in an arbitrary thread context.
In general, only a highest-level NT driver, such as a file system driver, is called in the context of a thread that is requesting the current I/O operation. An intermediate or lowest-level device driver can never assume that it is executing in the context of the thread that requested its current I/O operation.
While any kernel-mode routine that is run at higher than PASSIVE_LEVEL IRQL has a higher priority than all threads in the system, every routine in an NT driver is interruptible: any kernel-mode routine running at a particular IRQL retains control of the processor only if no interrupt with a higher IRQL value occurs while that routine is running.
Even an NT device driver’s interrupt service routine (ISR) can be interrupted by another routine (for example, by another driver’s ISR) that runs at higher IRQL. Unlike the drivers in some PC operating systems, an NT driver’s ISR is not the workhorse routine that does almost all I/O processing because an NT driver’s ISR does not necessarily retain control of the CPU it is currently running on until it returns.
Instead, an NT device driver must carry out most of its I/O operations at a lower IRQL than the DIRQL of its ISR. For good overall system performance, all kernel-mode routines that run at high IRQLs must relinquish control of the CPU very quickly. Such routines do only what must be done at high IRQL and generally queue a deferred procedure call (DPC) to complete any operations that can be done at a lower IRQL (DISPATCH_LEVEL).
For an introduction to the system-defined standard routines for NT drivers, see Chapter 2. For an overview of these routines, see Chapter 4.