分类: WINDOWS
2014-03-25 14:32:56
转载自:
1、一定不要在没有标注 I/O 请求数据包 (IRP) 挂起 (IoMarkIrpPending) 的情况下通过调度例程返回 STATUS_PENDING。
2、一定不要通过中断服务例程 (ISR) 调用 KeSynchronizeExecution。 它会使系统死锁。
通常在分发例程或StartIo例程里,需要访问一些资源,而这些资源在中断服务例程( ISR )中也需要使用,也就是说ISR和这些例程共享某些资源,因此必须考虑同步问题。
ISR在运行时系统提供一个自旋锁,来保护ISR的运行(在DIRQL上 ) 。
为了解决与ISR的同步问题,系统提供了KeSynchronizeExecution函数,使用这个函数可以获取该自旋锁并且提升到与ISR相同的中断级别上,在此基础上来运行KeSynchronizeExecution( )中指定的SynchRoutine。这样SynchRoutine就运行在与ISR相同的IRQL上,且拥有与中断关联的锁,因此,设备中断将被临时阻塞,SynchRoutine就可以自由地访问与ISR共享的资源。
SyschronizeRoutine 的作用就是一个临界区,在keSyschronizeExecution的调用后就调用了,如果这个时候isr也要调用,那么isr就要等待这个同步操作的完成。
3、一定不要将 DeviceObject->Flags 设置为 DO_BUFFERED_IO 和 DO_DIRECT_IO。 它会扰乱系统并最终导致致命错误。 而且,一定不要在 DeviceObject->Flags 中设置 METHOD_BUFFERED、METHOD_NEITHER、METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT,因为这些值只在定义 IOCTL 时使用。
在我的USB2COM 中的USB2COM_PnPAddDevice中有:
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 标记。
在USB2COM驱动程序中就是在全程中创建设备对象,却没有清除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、VS阅读驱动代码的方法
文件->新建->从现有代码创建->
选择代码所在文件夹,如我选择文件夹 vDisp,那么出现的工程文件如下: