现在的位置: 首页 > 综合 > 正文

驱动程序时应当避免的事项列表

2013年09月20日 ⁄ 综合 ⁄ 共 2966字 ⁄ 字号 评论关闭

下面是开发人员在使用 Windows NT 设备驱动程序时应当避免的事项列表: 

1. 一定不要在没有标注 I/O 请求数据包 (IRP) 挂起 (IoMarkIrpPending) 的情况下通过调度例程返回 STATUS_PENDING。 
2. 一定不要通过中断服务例程 (ISR) 调用 KeSynchronizeExecution。 它会使系统死锁。 
3. 一定不要将 DeviceObject->Flags 设置为 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它会扰乱系统并最终导致致命错误。 而且,一定不要在 DeviceObject->Flags 中设置 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因为这些值只在定义 IOCTL 时使用。 
4. 一定不要通过页面缓冲池分配调度程序对象。 如果这样做,将会偶尔导致系统故障检测 (Bugcheck)。 
5. 当运行于 IRQL >= DISPATCH_LEVEL 时,一定不要通过页面缓冲池分配内存,或访问页面缓冲池中的内存。 这是一个致命错误。 
6. 一定不要在 IRQL >= DISPATCH_LEVEL 上等候核心调度程序对象出现非零间隔。 这是一个致命错误。 
7. 在 IRQL >= DISPATCH_LEVEL 上执行时,一定不要调用任何导致调用线程发生直接或间接等待的函数。 这是一个致命错误。 
8. 一定不要把中断请求级别 (IRQL) 降低到低于您的顶级例程被调用的级别。 
9. 如果没有调用过 KeRaiseIrql(),则一定不要调用 KeLowerIrql()。 

10. 一定不要使处理器 (KeStallExecutionProcessor) 停止运转的时间超过 50 微秒。 
11. 一定不要使旋转锁 (Spin Lock) 保持锁定状态的时间超过您的需要。 要使系统获得更好的总体性能,请不要使任何系统范围内有效的旋转锁的锁定时间超过 25 微秒。 
12. 当 IRQL 大于 DISPATCH_LEVEL 时,一定不要调用 KeAcquireSpinLock 和 KeReleaseSpinLock,或 KeAcquireSpinLockAtDpcLevel 和 KeReleaseSpinLockFromDpcLevel。 
13. 一定不要通过调用 KeReleaseSpinLockFromDpcLevel 来释放 KeAcquireSpinLock 所获取的旋转锁,因为这会使原始 IRQL 无法被还原。 
14. 一定不要在 ISR 或 SynchCritSection 例程中调用 KeAcquireSpinLock 和 KeReleaseSpinLock 或者其它任何使用可执行旋转锁的例程。 
15. 当您在例程中而不是在 DriverEntry 中创建设备对象时,一定不要忘记清除 DO_DEVICE_INITIALIZING 标记。 
16. 一定不要同时在不同处理器的多个线程中将延时过程调用 (DPC) 对象添加到队列中(使用 KeInsertQueueDpc)。 这会导致致命错误。 
17. 一定不要通过 CutomerTimerDPC 例程释放周期定时器。 您可以通过 DPC 例程释放非周期定时器。 
18. 一定不要将相同的 DPC 指针传递给 KeSetTimer,或者 KeSetTimerEx (CustomTimerDpc) 和 KeInsertQueueDpc (CustomDpc),因为这将导致竞争。 
19. 旋转锁锁定时,一定不要调用 IoStartNextPacket。 这将使系统死锁。 
20. 旋转锁锁定时,一定不要调用 IoCompleteRequest。 这将使系统死锁。 
21. 如果您的驱动程序设置了完成例程,那么一定不要在没有把完成例程设置为 NULL 的情况下调用 IoCompleteRequest。 
22. 调用 IoCompleteRequest 之前,一定不要忘记设置 IRP 中的 I/O 状态区。 
23. 在将 IRP 添加到队列中或将它发送到另一个驱动程序 (IoCallDriver) 之后,一定不要调用 IoMarkPending。 在驱动程序调用 IoMarkPending 之前,IRP 可能已经完成,由此可能发生故障检测。 对于包含完成例程的驱动程序,如果设置了 Irp->PendingReturned,则完成例程必须调用 IoMarkPending。 
24. 一定不要在已经对某个 IRP 调用 IoCompleteRequest 之后再去访问该 IRP。 
25. 一定不要对不属于您的驱动程序的 IRP 调用 IoCancelIrp,除非您知道该 IRP 还没有完成。 
26. 在您的调度例程返回到调用者之前,一定不要对您的调度例程正在处理的 IRP 调 

55. 一定不要在任意的线程上下文 中使用 IoBuildSynchronousFsdRequest/IoBuildDeviceIoControlRequest 来分配 IRP,因为该 IRP 依然与该线程保持关联 (Irp->ThreadListEntry),直到它被释放。 
56. 如果已经使用 IoAllocateIrp 在 ChargeQuota 参数被设置为 TRUE 的情况下分配了某个 IRP,那么一定不要对该 IRP 调用 IoInitializeIrp。 如果在 ChargeQuota 设置为 TRUE 的情况下分配 IRP,则 I/O 管理器将把它为该 IRP 分配内存时所用的缓冲池的相关信息保存在该 IRP 的内部标记中。 

如果对这样的 IRP 调用 IoInitializeIrp,那么,当该函数盲目地清零整个 IRP 时,分配池信息将会丢失。 当您释放 IRP 时,这将导致内存被破坏。 同时,一定不要重用来自 IO 管理器的 IRP。 如果要重用 IRP,应当使用 IoAllocateIrp 分配您自己的 IRP。 
57. 如果在调用线程的堆栈中分配 了对象,就一定不要在 KeWaitForSingleObject/KeWaitForMultipleObjects 中将 WaitMode 指定为 UserMode。 这样做的结果是,如果被等候的对象是在函数堆栈中创建的,那么您必须将 WaitMode 指定为 KernelMode 才能防止线程被页面调度出去。 
58. 在没有对关键节中的代码加以保护的情况下,一定不要在用户模式线程的上下文中获取诸如 ERESOURCES 和 FastMutex(Unsafe) 这类资源。 

因为获取这些资源不会使 IRQL 提高到 APC_LEVEL,所以,如果线程在已获取资源后被挂起(通过将 APC 加入队列实现),它可能导致死锁,并使系统安全性降低。 因此,应当通过显式地将 IRQL 提高到 APC_LEVEL,或者调用 KeEnterCriticalRegion 来进入关键段,然后可以获取这些资源。 

抱歉!评论已关闭.