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 调用 IoCancelIrp。  
27. 一定不要从中间驱动程序调用 IoMakeAssociatedIrp 来为较低的驱动程序创建 IRP。 在中间驱动程序中所获得的 IRP 可能是已被关联的 IRP,而您不能将其它 IRP 关联到已经被关联的 IRP。  
28. 一定不要对使用缓冲 I/O 而设置的 IRP 调用 IoMakeAssociatedIrp。  
29. 一定不要简单地将指向设备 I/O 寄存器的虚拟指针解除引用并访问这些指针。 始终使用正确的硬件抽象层 (HAL) 函数来访问设备。  
30. 如果 IRP 或设备对象可能在 DISPATCH 级别被修改,那么一定不要通过 ISR 来访问 它。 在对称多处理器系统中,这会造成数据损坏。  
31. 正在高级 IRQL 中运行时,如果数据可能被低级 IROL 代码写入,那么一定不要修改该数据。 应当使用 KeSynchronizeExecution 例程。  
32. 在获取系统范围的取消旋转锁 (IoAcquireCancelSpinLock) 之前,一定不要在您的 DispatchCleanup 例程中获取驱动程序自己的旋转锁(如果有的话)。 要避免可能出现的死锁,一定要在驱动程序中遵循一致的锁定获取层次结构。  
33. 一定不要在取消例程中调用 IoAcquireCancelSpinLock,因为该例程被调用时已经获取了系统级的取消旋转锁。  
34. 在从取消例程返回之前,一定不要忘记调用 IoReleaseCancelSpinLock。  
35. 一定不要使用基于 IRQL 的同步,因为它只对单处理器系统有效。 提高单处理器上的 IRQL 将不会掩蔽在其它处理器上的中断。  
36. 一定不要对重叠的内存地址范围使用 RtlCopyMemory。 应当使用 RtlMoveMemory。  
37. 一定不要假定页面大小是常量,即使是用于给定的 CPU。 为了保持可移植性,应当使用 PAGE_SIZE 以及在头文件中所定义的其它页面相关常量。  
38. 一定不要从引导/系统初始化阶段加载的驱动程序的 DriverEntry 例程中访问除 Registry/Machine/Hardware 和 Registry/Machine/System 以外的任何注册表项。  
39. 一定不要为了加载驱动程序而在驱动程序的注册表项 (Registry/Machine/System/CurrentControlSet/Services) 下创建 Enum 项。 系统将动态地创建该项。  
40. 如果没有先在注册表中申请必需的与总线相关的 I/O 端口、内存范围、中断或直接内存访问 (DMA) 通道/端口等硬件资源,一定不要初始化物理设备。  
41. 一定不要在您的 DriverEntry 例程调用 IoRegisterDriverReinitialization,除非重初始化例程返回了 STATUS_SUCCESS。  
42. IRQL 为 PASSIVE_LEVEL 时,一定不要从被页面调度的线程或驱动程序例程中在 Wait 参数被设置为 TRUE 的情况下调用 KeSetEvent。 如果碰巧在调用 KeSetEvent 和 KeWait..Object(s) 之间您的例程被页面调度出去,这类调用就会导致致命的页面错误。  
43. 与上例相同的条件下,同样不能调用 KeReleaseSemaphore 。  

你可能感兴趣的:(Windows NT 驱动程序开发人员提示 -- 应注意避免的事项)