4. UMDF IRP处理流程:IRP到达驱动栈、框架拦截、WDFREQUEST创建、回调分发
UMDF驱动模型的核心优势在于其框架拦截(Framework Interception)机制。当IRP(I/O请求包)从应用层或内核层下发时,并不会直接到达驱动开发者编写的回调函数,而是先被UMDF框架层截获,经过一系列标准化处理后,转换为框架内部的WDFREQUEST对象,再通过预定义的回调分发机制通知驱动。理解这一流程是排查设备栈遍历故障的基础。
核心要点: UMDF驱动不直接处理IRP,而是处理WDFREQUEST。框架负责IRP到WDFREQUEST的转换、队列管理和同步控制。
4.1 IRP到达驱动栈的完整路径
一个典型的IRP从创建到被UMDF驱动处理,需要经过以下层次:
- 应用层发起I/O请求(如ReadFile、WriteFile、DeviceIoControl)。
- 内核I/O管理器创建IRP,并将其发送到设备对象栈。
- 内核模式驱动栈(如ACPI、PCI驱动)处理硬件相关操作,然后将IRP转发至UMDF的反射器(Reflector)。
- 反射器(WudfRd.sys)将内核IRP封装,通过ALPC(高级本地过程调用)传递给用户模式的UMDF驱动宿主进程(WudfHost.exe)。
- UMDF框架在宿主进程中接收该请求,创建WDFREQUEST对象,并开始回调分发。
4.2 框架拦截与WDFREQUEST创建
当IRP通过ALPC到达UMDF框架后,框架执行以下关键步骤:
| 步骤 | 框架动作 | 说明 |
|---|---|---|
| 1 | 接收ALPC消息 | 框架从ALPC端口读取内核传递过来的IRP封装数据。 |
| 2 | 创建WDFREQUEST对象 | 框架在堆上分配WDFREQUEST结构体,包含IRP的MajorFunction、参数、I/O缓冲区信息。 |
| 3 | 关联设备对象 | 将WDFREQUEST与目标WDFDEVICE对象绑定,确定请求的目标设备栈。 |
| 4 | 设置请求上下文 | 框架自动填充请求类型、超时、优先级等元数据。 |
| 5 | 入队或直接分发 | 根据队列配置,将WDFREQUEST放入I/O队列或立即调用回调。 |
调试提示: 在WinDbg中使用
!wdfrequest 扩展命令可以查看当前WDFREQUEST对象的详细信息,包括其关联的IRP MajorFunction和内部状态。
4.3 回调分发机制
WDFREQUEST创建完成后,框架根据请求类型和驱动注册的回调函数进行分发。UMDF驱动通过事件回调函数(Event Callback)来处理请求,而不是直接处理IRP。
4.3.1 主要回调函数类型
| 回调函数 | 对应的IRP主功能码 | 典型用途 |
|---|---|---|
IQueueCallbackRead |
IRP_MJ_READ | 处理从设备读取数据的请求 |
IQueueCallbackWrite |
IRP_MJ_WRITE | 处理向设备写入数据的请求 |
IQueueCallbackDeviceIoControl |
IRP_MJ_DEVICE_CONTROL | 处理自定义控制码(IOCTL) |
IQueueCallbackDefaultIoHandler |
其他未匹配的IRP | 处理框架未明确拦截的请求 |
4.3.2 分发流程
- 框架匹配队列: 根据WDFREQUEST的MajorFunction,框架查找驱动注册的I/O队列(WDFQUEUE)。
- 调用回调: 框架调用队列对应的回调函数,传入WDFREQUEST和WDFQUEUE句柄。
- 驱动处理: 驱动在回调中执行实际逻辑(如访问硬件、操作缓冲区)。
- 请求完成: 驱动调用
WdfRequestComplete通知框架请求已完成,框架将结果返回给内核。
4.4 关键代码示例:注册回调与处理请求
以下代码展示了在UMDF驱动中如何配置I/O队列并注册回调函数:
// 创建默认I/O队列并注册回调
HRESULT CMyDevice::CreateQueueAndCallbacks()
{
HRESULT hr = S_OK;
WDF_IO_QUEUE_CONFIG queueConfig;
WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
&queueConfig,
WdfIoQueueDispatchParallel // 并行分发模式
);
// 注册读、写、IOCTL回调
queueConfig.EvtIoRead = OnRead;
queueConfig.EvtIoWrite = OnWrite;
queueConfig.EvtIoDeviceControl = OnDeviceIoControl;
// 创建队列
hr = m_FxDevice->CreateIoQueue(
&queueConfig,
WDF_NO_OBJECT_ATTRIBUTES,
&m_DefaultQueue
);
return hr;
}
// 读请求处理回调
void CMyDevice::OnRead(
IWDFIoQueue* pQueue,
IWDFIoRequest* pRequest
)
{
HRESULT hr = S_OK;
IWDFMemory* pInputMemory = nullptr;
SIZE_T bytesToRead = 0;
// 获取请求的输入缓冲区
pRequest->GetInputMemory(&pInputMemory);
bytesToRead = pRequest->GetInputBufferSize();
// 执行设备读取操作(伪代码)
hr = ReadFromHardware(pInputMemory, bytesToRead);
// 完成请求
pRequest->Complete(hr);
}
常见故障点: 如果驱动未注册
EvtIoDefault 回调,而框架收到无法匹配的IRP(如IRP_MJ_CREATE),框架会默认完成该请求并返回失败状态。这可能导致应用层CreateFile调用失败。排查时应检查所有可能的IRP主功能码是否都有对应的回调处理。
4.5 流程总结与故障排查要点
| 阶段 | 关键对象/组件 | 排查要点 |
|---|---|---|
| IRP到达 | IRP、反射器、ALPC | 检查反射器是否加载(!wdfldr),ALPC端口是否正常 |
| 框架拦截 | UMDF框架、WDFREQUEST | 使用 !wdfrequest 查看请求状态,确认是否成功创建 |
| 回调分发 | WDFQUEUE、回调函数 | 确认队列配置正确,回调函数指针非空,分发模式匹配 |
| 请求完成 | WdfRequestComplete | 检查是否每个请求都调用了Complete,避免请求泄漏 |
核心原则: UMDF驱动开发者应始终从WDFREQUEST的角度思考I/O处理,而不是IRP。框架的拦截机制虽然增加了抽象层,但也提供了队列管理、同步、电源管理等强大功能,正确理解这一流程是高效调试的基础。