Deferred Procedure Calls (DPCs)

Deferred Procedure Calls (DPCs) are a mechanism in the Windows kernel that allows routines to be executed at a later time, typically after an interrupt service routine (ISR) has completed its immediate tasks.

Purpose of DPCs

DPCs are essential for managing hardware interrupts efficiently. When a hardware device generates an interrupt, the system executes an Interrupt Service Routine (ISR). ISRs are designed to be very short and fast, as they run at a high interrupt request level (IRQL) and must quickly acknowledge the interrupt and minimize the time spent disabling other interrupts. However, some tasks triggered by an interrupt might be more complex and time-consuming. These tasks can be deferred and executed by a DPC at a lower IRQL, allowing the system to return to a more responsive state quickly.

How DPCs Work

The process involves the following steps:

  1. A hardware device generates an interrupt.
  2. The kernel's ISR runs to acknowledge the interrupt and perform immediate, critical tasks.
  3. The ISR then queues a DPC routine to be executed later.
  4. The kernel scheduler, upon returning to a suitable IRQL (typically `DISPATCH_LEVEL`), checks for pending DPCs.
  5. When the system is ready, it executes the queued DPC routine.

Key Concepts

DPC Routines

A typical DPC routine has the following signature:


VOID
MyDpcRoutine(
    PKDPC Dpc,
    PVOID DeferredContext,
    PVOID SystemArgument1,
    PVOID SystemArgument2
    );
        

Queuing a DPC

Drivers typically queue a DPC from their ISR or from another routine running at a high IRQL. The primary function for this is IoRequestDpc().


// Assuming 'pDpc' is a pointer to a initialized KDPC structure
// and 'pContext' is a pointer to driver-specific data

BOOLEAN
IoRequestDpc(
    IN PVOID              DeviceObject,
    IN PIRP               Irp OPTIONAL,
    IN PKDPC              Dpc,
    IN PVOID              Context OPTIONAL,
    IN PVOID              Arg1 OPTIONAL,
    IN PVOID              Arg2 OPTIONAL
    );
        

If the DPC is already on the DPC queue, IoRequestDpc returns FALSE. If the DPC was successfully queued, it returns TRUE.

Important Considerations:

Note on Timers:

Timer objects are implemented using DPCs. When a timer expires, a DPC is automatically queued to execute the timer's callback routine.

DPC Object Initialization

Before a DPC can be queued, its associated _DPC object must be initialized. This is typically done in the driver's AddDevice routine or another initialization phase.


VOID
KeInitializeDpc(
    IN PKDPC Dpc,
    IN PKDEFERRED_ROUTINE DeferredRoutine,
    IN PVOID Context
    );
        

Advantages of DPCs

See Also