Windows NT 驱动程序开发人员提示 -- 应注意避免的事项

时间:2023-02-23 14:28:33

下面是开发人员在使用 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 。 
44.  与上例相同的条件下,同样不能调用 KeReleaseMutex 。 
45.  一定不要通过零售的 Windows NT 驱动程序调用 KeBugCheckEx 或 KeBugCheck 来停止系统的运行,除非您遇到的是破坏系统内存并最终导致系统进入故障检测的重要错误。 应当始终巧妙地处理错误条件。 
46.  一定不要假定 IoTimer 例程将会准确地在一秒边界处被调用,因为任何特定 IoTimer 例程的调用间隔最终取决于系统时钟。 
47.  一定不要从核心模式的设备驱动程序调用 Win32 应用程序编程接口 (API)。 
48.  一定不要使用会导致堆栈溢出的递归函数,因为调用线程的核心模式堆栈不能动态增长。 
49.  在处理多个中断的 ISR 例程中,一定不要使用中断对象指针 (PKINTERRUPT) 来标识中断,因为您在 ISR 中所获得的中断对象地址不会始终与您通过 IoConnectInterrupt 所获得的地址相同。 要想识别当前发生中断的设备,应当仅使用您在 IoConnectInterrupt 中所指定的 ServiceContext 值。 
50.  如果没有清零 CustomTimerDpc (KeCancelTimer),一定不要卸载驱动程序。 如果在卸载驱动程序后启动 DPC,它可能调用不存在的代码,并导致系统进入故障检测查。 
51.  如果 IRP 中设置了某个驱动程序的 I/O CompletionRoutine,那么一定要等到所有这些 IRP 完成之后,才能卸载该驱动程序。 如果卸载驱动程序后,IRP 被更低级的驱动程序完成,那么系统会试图执行不存在的代码,并导致系统崩溃。 
52.  一定要等到驱动程序准备好要处理某个设备中断时,才能启用该设备中断。 应当只在完成驱动程序初始化之后才启用它,执行 ISR 和 DPC 时,系统才能安全的访问设备对象的若干私有成员。 
53.  在旋转锁锁定时,一定不要调用驱动程序以外的代码,因为这会引起死锁。 
54.  如果您的驱动程序通过 IoBuildAsynchronousFsdRequest/IoAllocateIrp 创建了一个 IRP,那么,一定不要从您的 I/O CompletionRoutine 为这个 IRP 返回 STATUS_MORE_PROCESSING_REQUIRED 以外的任何状态,因为该 IRP 没有为与完成有关的 I/O 管理器的处理后工作做好准备。 这样的 IRP 应当被驱动程序显式地释放 (IoFreeIrp)。 如果本来没有打算重用 IRP,可以在返回状态 STATUS_MORE_PROCESSING_REQUIRED 之前,在 CompletionRoutine 中将它释放。 
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 来进入关键段,然后可以获取这些资源。

Windows NT 驱动程序开发人员提示 -- 应注意避免的事项的更多相关文章

  1. Windows NT驱动程序的基本结构和实例

    Windows驱动程序分为两类:一类是不支持即插即用功能的NT式驱动程序:另一类是支持即插即用功能的WDM驱动程序. NT式驱动的基本结构: 1)驱动加载过程与驱动入口函数DriverEntry: 驱 ...

  2. 注冊成为Windows Phone开发人员而且解锁Windows Phone 8.1手机

    注冊成为Windows Phone开发人员而且解锁Windows Phone 8.1手机 上篇文章介绍了怎样使用Qt Creator和Visual Studio构建Windows Phone 8.1应 ...

  3. Windows Azure 网站开发Stacks支持

    编辑人员注释:本文章由 Windows Azure 网站团队的项目经理 Daria Grigoriu 和 Windows Azure 网站开发人员体验合作伙伴共同撰写. Windows Azure 网 ...

  4. 理解和使用NT驱动程序的执行上下文

    理解Windows NT驱动程序最重要的概念之一就是驱动程序运行时所处的“执行上下文”.理解并小心地应用这个概念可以帮助你构建更快.更高效的驱动程序. NT标准内核模式驱动程序编程的一个重要观念是某个 ...

  5. IE调试网页之四:F12 开发人员工具控制台错误消息 (Windows)(转载)

    F12 工具控制台可以报告在运行时期间发生的错误和信息消息.本文将介绍错误消息,并提供有关如何修正这些错误的建议. 简介 F12 工具控制台错误消息提供了一些代码(例如,SEC7111 或 HTML1 ...

  6. Windows 10 开发人员预览版中的新增功能(转自 IT之家)

    Windows 10 开发人员预览版中的新增功能 在Win10预览版中安装工具与SDK后,即可着手创建Windows通用应用或先浏览目前的环境与此前相比都发生了什么变化. 应用建模 文件资源管理器: ...

  7. Windows 硬件开发人员怎样选择代码签名证书类型

    在建立 Windows 开发人员中心硬件仪表板帐户之前,你需要获取代码签名证书以保护数字信息的安全.此证书是用于建立你的公司对你所提交代码的所有权的接受标准.它让你可以用数字形式签署 PE 二进制文件 ...

  8. PX4/Pixhawk---高速成为开发人员(Windows)

    1 高速成为开发人员新手教程(翻译)---官方 1.1 编译环境之版本号控制系统 (1)安装MSysGIT 安装完毕后,配置GIT. 安装注意     安装过程中除了以下一步外,其它的步骤都採用默认安 ...

  9. 面向开发人员的Windows错误报告(WER)

    Windows错误报告是更新的Windows XP上Dr.Watson的替代品.它监视故障并收集可以发送到要分析的服务器(如果用户允许)的有用信息.这项功能帮助微软修复了很多错误——由于收到的报告,微 ...

随机推荐

  1. Response.StatusCode的HTTP状态代码列表

    1xx - 信息提示这些状态代码表示临时的响应.客户端在收到常规响应之前,应准备接收一个或多个 1xx 响应. · 100 - Continue 初始的请求已经接受,客户应当继续发送请求的其余部分.( ...

  2. git-tag

    标签可以针对某一时间点的版本做标记,常用于版本发布. 列出标签 $ git tag # 在控制台打印出当前仓库的所有标签 $ git tag -l 'v0.1.*' # 搜索符合模式的标签 打标签 g ...

  3. 递归遍历树结构顺序显示并且添加到list集合

    /// <summary> /// 给流程排序 by 于連偉 2015/06/15 /// </summary> /// <param name="lstNex ...

  4. CSS边框与边界

    (上右下左依次体现)à边框的同一属性名称可以同时使用多个属性值 简化方案:border: 长度 形态 颜色 例如 border:1px solid black; 16.2 CSS中边界的使用 padd ...

  5. LintCode-落单的数 III

    给出2*n + 2个的数字.除当中两个数字之外其它每一个数字均出现两次,找到这两个数字. 您在真实的面试中是否遇到过这个题? Yes 例子 给出 [1,2,2,3,4,4,5,3].返回 1和5 挑战 ...

  6. Prometheus安装和配置node&lowbar;exporter监控主机

    Node_exporter是可以在* Nix和Linux系统上运行的计算机度量标准的导出器. Node_exporter 主要用于暴露 metrics 给 Prometheus,其中 metrics ...

  7. 题解-Codeforces671D Roads in Yusland

    Problem Codeforces-671D 题意概要:给定一棵 \(n\) 点有根树与 \(m\) 条链,链有费用,保证链端点之间为祖先关系,问至少花费多少费用才能覆盖整棵树(\(n-1\) 条边 ...

  8. Knut重排算法

    /// <summary> /// 这是Knut重排算法的实现 /// </summary> /// <param name="number"> ...

  9. Codeforces777E&period; Hanoi Factory 2017-05-04 18&colon;10 42人阅读 评论&lpar;0&rpar; 收藏

    E. Hanoi Factory time limit per test 1 second memory limit per test 256 megabytes input standard inp ...

  10. 跨服务器查询信息的sql

    --跨服务器查询信息的sql: select * from openrowset( 'SQLOLEDB', '192.168.1.104'; 'sa'; '123.com',[AutoMonitorD ...