在零级线程处理和同步中,WDDM 允许以可重入的方式对显示微型端口驱动程序进行零级 DxgkDdi*Xxx 调用。 也就是说,多个线程可以通过调用零级 DDI 同时进入驱动程序。
驱动程序应预期系统中的任何线程会传入,并应相应地保护该线程的数据。
尽管驱动程序中可以同时运行两个或多个线程,但不能有两个线程属于单个进程。
级别零是默认的线程和同步级别,包括以下函数:
- DxgkDdiCheckMultiPlaneOverlaySupport3 (或 DxgkDdiCheckMultiPlaneOverlaySupport2/DxgkDdiCheckMultiPlaneOverlaySupport)
- DxgkDdiCloseAllocation
- DxgkDdiCollectDbgInfo。 此函数应收集各种故障的调试信息,并且可以随时在高 IRQL (调用,也就是说, DxgkDdiCollectDbgInfo 运行所在的 IRQL 通常) 未定义。 在所有情况下, DxgkDdiCollectDbgInfo 都必须验证所需调试信息和正确同步的可用性。 但是,如果 pCollectDbgInfo 参数指向的 DXGKARG_COLLECTDBGINFO 结构的 Reason 成员设置为 VIDEO_TDR_TIMEOUT_DETECTED 或 VIDEO_ENGINE_TIMEOUT_DETECTED,则驱动程序必须确保 DxgkDdiCollectDbgInfo 可分页、在 IRQL = PASSIVE_LEVEL 运行并支持零级同步。
- DxgkDdiControlEtwLogging
- DxgkDdiCreateAllocation
- DxgkDdiCreateContext
- DxgkDdiCreateDevice
- DxgkDdiDescribeAllocation
- DxgkDdiDestroyAllocation
- DxgkDdiDestroyContext
- DxgkDdiDestroyDevice
- DxgkDdiDpcRoutine
- DxgkDdiEnumVidPnCofuncModality
- DxgkDdiGetScanLine
- DxgkDdiGetStandardAllocationDriverData
- DxgkDdiInterruptRoutine
- DxgkDdiIsSupportedVidPn
- DxgkDdiMiracastCreateContext
- DxgkDdiMiracastDestroyContext
- DxgkDdiMiracastIoControl
- DxgkDdiMiracastQueryCaps
- DxgkDdiOpenAllocation
- DxgkDdiPresent
- DxgkDdiQueryAdapterInfo
- DxgkDdiQueryCurrentFence
- DxgkDdiRecommendFunctionalVidPn
- DxgkDdiRecommendVidPnTopology
- DxgkDdiRender
- DxgkDdiRenderKm
- DxgkDdiResetDevice
1. 零级同步的核心特性
(1) 可重入性(Reentrancy)
- 多个线程可同时调用零级 DDI 函数,但 同一进程的两个线程不能同时进入驱动程序(跨进程允许并发)。
- 驱动程序必须 自行保护共享数据(如全局状态、硬件寄存器等),避免竞争条件。
(2) 默认同步级别
- 零级是 默认级别,适用于大多数常规 DDI 调用。
- 相比 Level 1/2/3,零级同步限制最少,但要求驱动程序 自行管理线程安全。
(3) IRQL 限制
- 大多数零级函数可以在 任意 IRQL(中断请求级别)运行,但某些特殊情况(如 DxgkDdiCollectDbgInfo 处理超时)必须在 PASSIVE_LEVEL(低 IRQL)执行。
2. 零级包含的主要函数
以下是常见的零级 DDI 函数:
函数 | 描述 |
---|---|
DxgkDdiCheckMultiPlaneOverlaySupport[2/3] |
检查硬件是否支持多平面覆盖(MPO) |
DxgkDdiCloseAllocation |
关闭分配的资源(如显存) |
DxgkDdiCollectDbgInfo |
调试信息收集(关键函数,见下文) |
3. 特殊函数:DxgkDdiCollectDbgInfo
(1) 一般情况
- 可以在 任意 IRQL 调用(包括 DISPATCH_LEVEL 或更高)。
- 必须验证数据可用性(如内存是否可分页)。
- 必须同步访问共享资源(如日志缓冲区)。
(2) 超时检测(TDR/Engine Timeout)
当 DXGKARG_COLLECTDBGINFO.Reason 为以下值时:
- VIDEO_TDR_TIMEOUT_DETECTED(GPU 超时)
- VIDEO_ENGINE_TIMEOUT_DETECTED(引擎超时)
驱动必须:
- 在 PASSIVE_LEVEL(低 IRQL)运行(可分页代码)。
- 支持零级同步(允许多线程进入)。
- 避免死锁(不能持有自旋锁等不可睡眠的锁)。
4. 线程安全实现建议
由于零级允许可重入调用,驱动程序必须:
使用适当的同步机制:
- 自旋锁(Spin Lock):适用于高 IRQL(如 DISPATCH_LEVEL)。
- 互斥体(Mutex):适用于 PASSIVE_LEVEL(可分页代码)。
- 原子操作(Atomic Operations):适用于简单状态更新(如引用计数)。
避免全局状态污染:
- 使用 线程局部存储(TLS) 或 上下文结构体 隔离不同线程的数据。
5. 典型应用场景
多线程资源管理:
- 线程A调用 DxgkDdiCloseAllocation 释放资源,同时线程B调用 DxgkDdiCheckMultiPlaneOverlaySupport3 检查 MPO 支持。
调试信息收集:
- 系统在 GPU 超时(TDR)时调用 DxgkDdiCollectDbgInfo,同时其他线程可能仍在提交命令。
6. 总结
特性 | 零级(Level 0) | 一级(Level 1) | 二级(Level 2) | 三级(Level 3) |
---|---|---|---|---|
可重入性 | ✅ 允许(跨进程) | ❌ 类内禁止 | ❌ 单线程 | ❌ 单线程 |
默认级别 | ✅ 是 | ❌ 否 | ❌ 否 | ❌ 否 |
IRQL 限制 | 大多数任意 IRQL | 通常 PASSIVE_LEVEL |
硬件空闲时 | 硬件空闲时 |
同步要求 | 驱动自行管理 | 类内互斥 | WDDM 保证单线程 | WDDM 保证单线程 |
零级同步适用于大多数常规操作,但驱动程序必须自行处理线程安全!