Interrupt Handling
This document provides an overview of interrupt handling mechanisms within the Windows operating system, focusing on the concepts and structures relevant to hardware device driver development.
Introduction
Interrupts are essential signals generated by hardware devices to alert the processor of an event requiring immediate attention. Effective interrupt handling is crucial for system responsiveness, efficient device operation, and overall system stability. Windows provides a robust framework for managing interrupts, enabling drivers to respond promptly and safely to hardware events.
Interrupt Request (IRQ)
An Interrupt Request (IRQ) is a hardware line that a device uses to signal an interrupt to the processor. In modern systems, IRQs are managed by an interrupt controller (like the Advanced Programmable Interrupt Controller - APIC) which prioritizes and routes interrupts to the appropriate processor core. Understanding IRQ assignments and potential conflicts is a fundamental aspect of hardware configuration.
Interrupt Service Routines (ISR)
An Interrupt Service Routine (ISR) is a piece of code, typically part of a device driver, that is executed when a specific interrupt occurs. The ISR's primary role is to acknowledge the interrupt, identify the source, perform minimal processing (often queuing further work), and then return control to the operating system. ISRs are executed at a high IRQL (Interrupt Request Level) to ensure timely response.
Handling Interrupts
Device drivers register an ISR with the operating system to handle interrupts for their associated hardware. This registration typically involves specifying the device, the interrupt vector, and the ISR routine. The system then invokes the registered ISR when an interrupt occurs on that vector.
The Interrupt Object
In Windows, interrupt handling is managed through an KINTERRUPT object. Drivers obtain a pointer to this object when they register their ISR. The KINTERRUPT object contains information about the interrupt, such as its IRQL, affinity, and whether it's shared or vector-specific. Drivers use this object to manage interrupt behavior.
Interrupt Context
Code executing within an ISR runs in interrupt context. This means it has significant restrictions: it cannot perform operations that would block (like waiting for I/O completion), it cannot access user-mode memory, and it should execute as quickly as possible. Complex processing is typically deferred to a deferred procedure call (DPC) which runs at a lower IRQL.
A typical interrupt handling flow:
- Hardware asserts an IRQ line.
- The interrupt controller routes the interrupt to the processor.
- The operating system's interrupt dispatcher identifies the interrupt vector.
- The system calls the registered ISR for that vector.
- The ISR acknowledges the hardware, determines the event, and potentially queues a DPC.
- The ISR returns, and the system may decide to execute a DPC.
- The DPC performs the remaining processing at a lower
IRQL.
Example of registering an interrupt (simplified pseudocode):
NTSTATUS DriverEntry(
PDRIVER_OBJECT DriverObject,
PUNICODE_STRING RegistryPath
) {
// ... initialization ...
// Create an interrupt object
PVOID interruptObject;
NTSTATUS status = IoConnectInterrupt(
&interruptObject, // Address of KINTERRUPT object pointer
MyInterruptServiceRoutine, // Pointer to ISR
DeviceContext, // Context passed to ISR
Vector, // Interrupt vector
Level, // IRQL
Level, // Affinity
Affinity, // Processor affinity
InterruptMode, // Latched or Triggered
Shared // Shared or exclusive
);
if (!NT_SUCCESS(status)) {
// Handle error
return status;
}
// Store interruptObject in device context
DeviceContext->InterruptObject = interruptObject;
// ... more initialization ...
return STATUS_SUCCESS;
}
KSERVICE_ROUTINE MyInterruptServiceRoutine(
PKINTERRUPT Interrupt,
PVOID ServiceContext
) {
PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)ServiceContext;
// Acknowledge the interrupt (hardware specific)
// ...
// Queue a DPC for deferred processing
IoRequestDpc(deviceContext->InterruptObject, deviceContext, NULL);
// Return TRUE if this interrupt was for this device, FALSE otherwise
return TRUE;
}
KDPC_SERVICE_ROUTINE MyDeferredProcedureCall(
PKDPC Dpc,
PVOID DeferredContext,
PVOID SystemArgument1,
PVOID SystemArgument2
) {
PDEVICE_CONTEXT deviceContext = (PDEVICE_CONTEXT)DeferredContext;
// Perform deferred processing here
// This code runs at DISPATCH_LEVEL, not the higher ISR IRQL
// ...
// Re-enable interrupts if necessary (depending on hardware)
// ...
}