Windows 驱动程序中不同函数运行在不同的中断请求级别 (IRQL)详细的分类
IRQL 级别说明
PASSIVE_LEVEL (0)
- 最低优先级,可以被任何中断打断
- 可以访问分页内存,可以等待
常用函数:
// 文件操作
ZwCreateFile()
ZwReadFile()
ZwWriteFile()
ZwClose()
ZwQueryInformationFile()
ZwSetInformationFile()// 注册表操作
ZwOpenKey()
ZwQueryValueKey()
ZwSetValueKey()
ZwClose()// 内存管理
ZwAllocateVirtualMemory()
ZwFreeVirtualMemory()
ZwMapViewOfSection()
ZwUnmapViewOfSection()// 线程和进程
PsCreateSystemThread()
PsTerminateSystemThread()
ZwWaitForSingleObject()
ZwWaitForMultipleObjects()// 同步对象
KeWaitForSingleObject()
KeWaitForMultipleObjects()
KeDelayExecutionThread()// 驱动程序框架
DriverEntry()
DriverUnload()
AddDevice()
APC_LEVEL (1)
- 异步过程调用级别
- 不能等待内核对象
常用函数:
// 异步过程调用
KeInsertQueueApc()
KeFlushQueuedDpcs()// 某些内存操作
ExAllocatePoolWithTag() // 可能运行在APC_LEVEL
ExFreePoolWithTag()
DISPATCH_LEVEL (2)
- 调度级别,高优先级
- 不能访问分页内存,不能等待
常用函数:
// DPC 相关
KeInsertQueueDpc()
KeRemoveQueueDpc()
KeFlushQueuedDpcs()// 计时器
KeSetTimer()
KeCancelTimer()
KeSetTimerEx()// 自旋锁
KeAcquireSpinLock()
KeReleaseSpinLock()
KeAcquireSpinLockAtDpcLevel()
KeReleaseSpinLockFromDpcLevel()// 中断相关
KeAcquireInterruptSpinLock()
KeReleaseInterruptSpinLock()
KeSynchronizeExecution()// 内存操作(非分页)
RtlCopyMemory()
RtlZeroMemory()
RtlMoveMemory()// 链表操作
InsertTailList()
RemoveEntryList()
IsListEmpty()// 原子操作
InterlockedIncrement()
InterlockedDecrement()
InterlockedExchange()
InterlockedCompareExchange()
DIRQL (Device IRQL, 3-31)
- 设备中断级别
- 最高优先级,不能被低级别中断打断
常用函数:
// 中断服务例程
InterruptServiceRoutine()// 硬件访问
READ_PORT_UCHAR()
WRITE_PORT_UCHAR()
READ_REGISTER_ULONG()
WRITE_REGISTER_ULONG()// 原子操作
InterlockedIncrement()
InterlockedDecrement()
InterlockedExchange()// 简单的内存操作
RtlCopyMemory()
RtlZeroMemory()
实际代码示例
// PASSIVE_LEVEL 示例
NTSTATUS DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{// 当前 IRQL = PASSIVE_LEVELKIRQL currentIrql = KeGetCurrentIrql();DbgPrint("DriverEntry IRQL: %d\n", currentIrql);// 可以调用文件操作HANDLE fileHandle;NTSTATUS status = ZwCreateFile(&fileHandle, ...);// 可以等待LARGE_INTEGER timeout;timeout.QuadPart = -10000000; // 1秒KeDelayExecutionThread(KernelMode, FALSE, &timeout);return STATUS_SUCCESS;
}// DISPATCH_LEVEL 示例
VOID MyDpcRoutine(PKDPC Dpc,PVOID DeferredContext,PVOID SystemArgument1,PVOID SystemArgument2
)
{// 当前 IRQL = DISPATCH_LEVELKIRQL currentIrql = KeGetCurrentIrql();DbgPrint("DPC IRQL: %d\n", currentIrql);// 只能调用非分页函数KIRQL oldIrql;KeAcquireSpinLock(&MySpinLock, &oldIrql);// 处理数据ProcessDataAtDispatchLevel();KeReleaseSpinLock(&MySpinLock, oldIrql);
}// DIRQL 示例
BOOLEAN MyInterruptServiceRoutine(PKINTERRUPT Interrupt,PVOID ServiceContext
)
{// 当前 IRQL = DIRQL (高于 DISPATCH_LEVEL)// 只能进行最基本的操作ULONG data = READ_PORT_ULONG(DevicePort);// 快速处理,可能排队DPCKeInsertQueueDpc(&MyDpc, NULL, NULL);return TRUE; // 中断已处理
}
IRQL 检查和调试
// 检查当前 IRQL
KIRQL GetCurrentIRQL(VOID)
{return KeGetCurrentIrql();
}// 安全的 IRQL 检查
BOOLEAN IsIrqlSafeFor(OPERATION_TYPE operation)
{KIRQL currentIrql = KeGetCurrentIrql();switch (operation) {case FILE_OPERATION:return (currentIrql <= PASSIVE_LEVEL);case MEMORY_ALLOCATION:return (currentIrql <= DISPATCH_LEVEL);case SPINLOCK_OPERATION:return (currentIrql <= DISPATCH_LEVEL);case PORT_ACCESS:return TRUE; // 所有级别都可以default:return FALSE;}
}// 临时提升 IRQL
VOID RaiseIrqlExample(VOID)
{KIRQL oldIrql;// 提升到 DISPATCH_LEVELKeRaiseIrql(DISPATCH_LEVEL, &oldIrql);// 在高 IRQL 下执行操作// 注意:不能访问分页内存或等待// 恢复原来的 IRQLKeLowerIrql(oldIrql);
}
常见的 IRQL 错误
// 错误示例1:在高 IRQL 下调用分页函数
VOID BadDpcRoutine(PKDPC Dpc, PVOID Context, PVOID Arg1, PVOID Arg2)
{// 当前 IRQL = DISPATCH_LEVEL// 错误!不能在 DISPATCH_LEVEL 调用可能分页的函数NTSTATUS status = ZwCreateFile(&handle, ...); // 会导致 BSOD
}// 错误示例2:在高 IRQL 下等待
VOID BadWaitAtHighIrql(VOID)
{KIRQL oldIrql;KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);// 错误!不能在 DISPATCH_LEVEL 等待KeWaitForSingleObject(&MyEvent, Executive, KernelMode, FALSE, NULL);KeLowerIrql(oldIrql);
}
最佳实践
- 始终检查当前 IRQL
- 使用 PAGED_CODE() 宏标记可分页代码
- 在高 IRQL 下快速完成操作
- 使用 DPC 将工作推迟到较低 IRQL
- 正确管理自旋锁和 IRQL 级别
这些知识对于编写稳定的 Windows 驱动程序非常重要!