驱动开发-windows驱动设计目标

驱动程序和应用程序不一样的,由于其直接运行于windows r0级,故对于开发有更多和更严格的标准,一般会有以下一些常见的设计目标:

安全性、可移植性、可配置性、 可被中断、多处理器安全、可重用 IRP、 支持异步 I/O这些是基本目标。

1. 安全性:

驱动程序是足够安全的,它在系统运行的任何时候,都可以执行安装、卸载、禁用、启用等操作,而不引起蓝屏(BlueScreen)、系统运行缓慢等异常问题。

保障安全性方法包括但不限于:

A. 正确的处理编译器的任何警告和错误;和应用层不一样,任何警告都可能包含着错误,在WinDDK 7600中,甚至专门给出了对于警告和错误的工具链,检测并给出如何修改的建议,在新版本中集成到IDE中的,但感觉WinDDK还是很好用的

B. 良好的编码习惯和风格: 每一种标准风格的背后,都有一大堆的经验教训,例如下面的风格:

BOOLEAN
FunctionName (IN PVOID                   DeviceExtension, IN PMOUSE_INPUT_DATA       CurrentInput, OUT POUTPUT_PACKET         CurrentOutput,IN UCHAR                   StatusByte,IN PUCHAR                  DataByte,OUT PBOOLEAN               ContinueProcessing,OUT PMOUSE_STATE           MouseState,OUT PMOUSE_RESET_SUBSTATE  ResetSubState
);// 这种风格中,每一行都有特定的功能,并用IN和OUT表明参数是输入参数还是输出参数;作为对比我们看看
// 下面的风格BOOLEAN FunctionName (PVOID DeviceExtension, PMOUSE_INPUT_DATA CurrentInput, POUTPUT_PACKET CurrentOutput, UCHAR StatusByte, PUCHAR DataByte, PBOOLEAN ContinueProcessing, PMOUSE_STATE MouseState, PMOUSE_RESET_SUBSTATE  ResetSubState);// 对比发现上面风格非常清晰明确;

C. 注重对异常的检查和处理;根据实践的情况,和应用层是相反的,驱动层最好不要抛出异常,毕竟r9级别的异常可能带来非常多的问题,例如,如果我们使用物理地址拷贝数据时候,这时候越界很可能将毫不相关的进程写崩溃掉,此时系统很可能已经无法检测到的;

所有的异常最好是在当前函数中处理掉,故检测输入参数有效和确保输出参数有效非常有必要;

在没有必要的情况下,不要在内核中做一些骚操作,例如跑一个复杂的算法;

非常慎重对使用的内存进行处理;

大量的专业测试和问题调试;所有驱动的代码都需要经过HLK测试和认证,这可以避免一些异常;同时驱动尽可能留下日志,因为驱动有问题等同于系统有问题。

2. 可移植性

驱动程序应该支持所有 Windows 支持的硬件平台移植。 要实现跨平台可移植性,驱动程序开发中应该注意以下几点: 

使用C语言开发:内核模式驱动程序都应使用 C 编写,以便它们可以使用系统兼容的 C 编译器重新编译、重新链接并在不同的 Windows 平台上运行,而无需重写或替换任何代码。不能在内核模式驱动程序中使用许多 C++ 语言构造,因此使用C++要仔细评估。

驱动程序不应依赖于任何特定系统兼容的 C 编译器或 C 支持库的功能,代码应符合 ANSI C 标准,最好避免:

依赖于大小或布局因平台而异的数据类型。

调用维护状态的任何标准 C 运行时库函数。

调用操作系统为其提供替代支持例程的任何标准 C 运行时库函数。

使用 WDK 编程接口

每个Windows NT执行组件导出驱动程序和所有其他内核模式组件调用的一组内核模式驱动程序支持例程。 WDK 提供一组头文件,用于定义特定于系统的数据类型和常量,驱动程序需要保持从一个平台到另一个平台的可移植性。 所有内核模式驱动程序都包含一个主 WDK 内核模式头文件 Wdm.h 或 Ntddk.h。 在使用相应的编译器指令编译驱动程序时,主头文件不仅会引入系统提供的用于定义基本内核模式类型的宏,还会从任何特定于处理器体系结构的宏中拉取适当的选择。

如果驱动程序需要依赖于平台的定义,最好在 #ifdef 语句中隔离这些定义,以便针对相应的硬件平台编译和链接每个驱动程序。

在目前为止常见的架构是: x86、x64、IA64、Arm x86、Arm x64,我们最常用的还是x64架构;

3. 可中断/抢占

windows操作系统本身是可抢占的,它并非实时操作系统,可抢占意味着中断会按照一定的优先级来抢占,发生抢占时,低优先级的中断被挂起,待高优先级中断运行完成后,在恢复运行;

可中断设计的目标是最大限度地提高系统性能。 任何线程都可以被优先级较高的线程抢占,并且任何驱动程序的中断服务例程 (ISR) 都可以被以更高的中断请求级别运行的例程中断 (IRQL) 。

内核组件根据以下优先级条件之一确定代码序列的运行时间:

线程的内核定义的运行时优先级方案:

系统中的每个线程都有关联的优先级属性。 通常,大多数线程具有 可变 优先级属性:它们始终是抢占的,并计划与当前处于同一优先级的所有其他线程一起运行轮循机制。 某些线程具有 实时 优先级属性:这些时间关键型线程将运行到完成,除非它们被具有更高实时优先级属性的线程抢占。 Microsoft Windows 体系结构不提供固有的实时系统。

无论其优先级属性如何,在发生硬件中断和某些类型的软件中断时,系统中的任何线程都可以被抢占。

内核定义的 中断请求级别 (IRQL):

内核确定硬件和软件中断的优先级,以便某些内核模式代码在更高的 IRQL 下运行,从而使其具有高于系统中其他线程的计划优先级。 执行内核模式驱动程序代码的特定 IRQL 由其基础设备 的硬件优先级确定。

内核模式代码始终是可中断的:具有较高 IRQL 值的中断随时可能发生,从而导致具有更高系统分配 IRQL 的另一段内核模式代码立即在该处理器上运行。 但是,当一段代码在给定 IRQL 中运行时,内核会屏蔽处理器上 IRQL 值较小或相等的所有中断向量。

最低 IRQL 级别称为 PASSIVE_LEVEL。 在此级别,不会屏蔽任何中断向量。 线程通常以 IRQL=PASSIVE_LEVEL 运行。

软件中断中下一个更高的 IRQL 级别适用于软件中断。 这些级别包括APC_LEVEL、DISPATCH_LEVEL或内核调试WAKE_LEVEL。

硬件中断中设备中断的 IRQL 值仍然较高。 内核保留系统关键中断(例如来自系统时钟或总线错误)的最高 IRQL 值。

驱动程序中的每个例程都是可中断的。 这包括以高于 PASSIVE_LEVEL 的 IRQL 运行的任何例程。 仅在运行某个特定 IRQL 时未发生更高 IRQL 中断的情况下,在特定 IRQL 上运行的任何例程才保留对处理器的控制。

在 Windows 中,所有线程都具有线程上下文。 此上下文包含标识拥有线程的进程的信息以及其他特征,例如线程的访问权限。

通常,在请求驱动程序的当前 I/O 操作的线程上下文中,仅调用最高级别驱动程序。 中间级别或最低级别驱动程序永远不能假定它在请求其当前 I/O 操作的线程的上下文中执行。

因此,驱动程序例程通常在 任意线程上下文中执行 -- 调用标准驱动程序例程时,任何线程的上下文都是最新的。 出于性能原因(避免上下文切换),很少有驱动程序会设置自己的线程。

4. 多处理器

基于 Microsoft Windows NT 的操作系统设计为在单处理器和对称多处理器 (SMP) 平台上统一运行,内核模式驱动程序应设计为同样地运行。

在任何 Windows 多处理器平台中,都存在以下条件:

所有 CPU 都是相同的,所有或所有处理器都必须具有相同的协处理器。

所有 CPU 共享内存,并统一访问内存。

在 对称 平台中,每个 CPU 都可以访问内存、中断和访问 I/O 控制寄存器。 (相比之下,在 非对称 多处理器计算机中,一个 CPU 会接受一组从属 CPU 的所有中断。)

若要在 SMP 平台上安全运行,操作系统必须确保在一个处理器上执行的代码不会同时访问和修改另一个处理器正在访问和修改的数据。 

此外,在单处理器计算机中序列化的驱动程序的 I/O 操作可以在 SMP 计算机中重叠。 也就是说,处理传入 I/O 请求的驱动程序例程可以在一个处理器上执行,而与设备通信的另一个例程在另一个处理器上并发执行。 无论内核模式驱动程序是在单处理器还是对称多处理器计算机上执行,它们都必须同步对驱动程序例程之间共享的任何驱动程序定义数据或系统提供资源的访问,并同步对物理设备的访问。

Windows NT内核使用称为自旋锁的同步机制,驱动程序可以使用该机制保护共享数据 (或设备寄存器) ,避免在对称多处理器平台上并发运行的一个或多个例程同时访问。 内核强制实施两个有关使用旋转锁的策略:

在任何给定时刻,只有一个例程可以持有特定的旋转锁。 在访问共享数据之前,必须引用数据的每个例程必须首先尝试获取数据的旋转锁。 若要访问相同的数据,另一个例程必须获取旋转锁,但在当前持有者释放旋转锁之前,无法获取旋转锁。

内核将 IRQL 值分配给系统中的每个旋转锁。 内核模式例程仅当在旋转锁的分配 IRQL 上运行该例程时,才能获取特定的旋转锁。

这些策略阻止通常以较低 IRQL 运行但当前持有旋转锁的驱动程序例程被尝试获取相同旋转锁的较高优先级驱动程序例程抢占。 因此,可以避免死锁。

分配给旋转锁的 IRQL 通常是可以获取旋转锁的最高 IRQL 例程的 IRQL。

5. IRP可重用

IRP是驱动程序工作的核心,驱动程序的一切工作基本都是围绕IRP进行,应用层往往使用IRP来控制设备的正常工作,同时I/O 管理器、PNP管理器和电源管理器也会使用 I/O 请求数据包 (IRP) 与内核模式驱动程序通信,同时windows允许驱动程序之间相互通信(基于设备树的结构保证了驱动程序可以从任何一个叶子结点遍历整个内核中所有设备,在这里设备是一个抽象概念,不仅仅包含实际硬件设备)。

IRP创建之初就考虑可重用,本身IRP就是从应用层切换到内核层时候封装的上层请求,故IRP会在不同的驱动和应用程序之间共同使用,它们看起来像下面这样:

IRP可以由系统服务函数或者内核驱动创建,它被传递给驱动程序,驱动程序可以自行决定如何处理它,同时IRP也属于I/O管理器的重要部分,I/O 管理器通过IRP来管理应用程序和设备驱动程序之间的通信;

一个IRP通常会有下面几种处理方式: 

完成这个IRP;

创建新的IRP,并向下传递以完成这个IRP;

复用当前IRP,并向下传递IRP;此时我们可以在IRP上挂载一个IRP完成例程,这样当IRP在下层被完成时,我们的驱动也会得到通知;

6. 支持异步I/O请求

异步I/O对于系统性能的提升非常巨大,非常建议驱动程序提供异步 I/O 支持,以便 I/O 请求的发起方可以继续执行,而不是等待其 I/O 请求完成。 异步 I/O 支持可提高发出 I/O 请求的系统吞吐量和性能。

使用异步I/O主要是带来了系统的性能提升,和应用层不一样,驱动程序考虑系统性能的影响非常必要且重要,异步I/O意味着:

驱动程序不一定按照发送的相同顺序处理 I/O 请求,驱动程序可以在收到 I/O 请求时重新排序;

驱动程序可以将一个数据传输请求拆分为N个传输请求;

驱动程序也可以重叠 I/O 请求处理,尤其是在对称多处理器平台中,这样对性能提高非常有效;

内核模式驱动程序对单个 I/O 请求的处理不一定是序列化的,当驱动程序在开始处理下一个传入 I/O 请求之前,不一定处理每个 IRP 以完成;

驱动程序可以在设备对象的设备扩展中维护有关其当前 I/O 操作的状态信息;

和应用程序不一样的是,驱动程序只有“好”和“更好”两种状态,当代码在R0级运行时,任何细小的错误都会影响整个系统,所以如果仅仅是可以用,那么这个驱动程序很可能带来灾难。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/1247.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

高频前端面试题汇总之Vue篇

1. Vue的基本原理 当一个Vue实例创建时,Vue会遍历data中的属性,用 Object.defineProperty(vue3.0使用proxy )将它们转为 getter/setter,并且在内部追踪相关依赖,在属性被访问和修改时通知变化。 每个组件实…

Flutter 之 HTTP3/QUIC 和 Cronet 你了解过吗?

虽然 HTTP3/QUIC 和 cronet 跟 Flutter 没太大关系,只是最近在整理 Flutter 相关资料时发现还挺多人不了解,就放到一起聊聊。 本篇也是主要将现有资料做一些简化整合理解。 前言 其实为什么会有 HTTP3/QUIC ?核心原因还是现有协议已经无法满…

前端工程搭建:

搭建前端工程最主要的是技术选型,如果技术选型不够全面或不合适,会出现很多问题。技术选型通常会涉及基础框架、打包构建工具、网络库、组件库、CSS预编译语言、状态管理、IDE、代码质量检查工具、包管理工具、版本控制工具等 CSS预编译语言 搭建工程没有…

机器学习周记(第三十五周:语义分割)2024.4.15~2024.4.21

目录 摘要 ABSTRACT 1 语义分割基本概念 1.1 数据集格式 ​编辑 1.2 语义分割评价指标 1.3 语义分割标注工具 2 转置卷积 3 FCN网络结构基本原理 摘要 本周主要学习了语义分割的基本概念及其在计算机视觉领域中的应用。了解了语义分割的几种经典网络,如全卷…

linux系统密码重置的方法

在linux系统中忘记密码,重置(重启:shutdown -r now) 1、在启动 Linux 时,按键盘上的上下左右键来止 Linux 的正常启动。 2、按下e鍵进入安全模式 3、找到首行是linux16,末尾是UTF-8的段落,在后门…

Python中的设计模式与最佳实践

👽发现宝藏 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 Python中的设计模式与最佳实践 在软件开发中,设计模式是一种解决常见问题的经过…

APP开发_ js 控制手机横屏或竖屏

1 Android 控制手机横屏或者竖屏的方法 1.1 配置 AndroidManifest.xml 以横屏模式为例: 在 Android 开发中,如果想让应用或某个特定的 Activity 在运行时以横屏模式显示,可以通过修改 Activity 的 AndroidManifest.xml 文件中的配置来实现…

【Django】调用django的pbkdf2_sha256加密算法测试

基于django搭建的系统中,用到pbkdf2_sha256((Password-Based Key Derivation Function 2))加密算法,这里做些代码测试、总结。 PBKDF2简介 PBKDF2是一种基于密码的密钥派生函数,用于从用户提供的…

C语言读写文件和C++读写文件

文本文件读写: "r":只读模式打开文本文件,文件必须存在。"w":写入模式打开文本文件,如果文件不存在则创建新文件。"a":追加模式打开文本文件,如果文件不存在则创…

2024-4-狼道

2024-4-狼道 2024-4-9 宋犀堃(堃通坤,多用于人名) fatux: 做人当如狗,和蔼可亲;做事当如狼,专注果决。 狼道 智慧生存的强者法则 走向卓越的成功之道 狼道,是追求卓越的野心&am…

C++_特殊类的设计和单例模式

文章目录 学习目标:1.请设计一个类,不能被拷贝2. 请设计一个类,只能在堆上创建对象3. 请设计一个类,只能在栈上创建对象4. 请设计一个类,不能被继承5. 请设计一个类,只能创建一个对象(单例模式) 特殊类的设…

达梦数据库的DMRMAN工具-管理备份(备份集查看)

达梦数据库的DMRMAN工具-管理备份(备份集查看) 管理备份一个重要的目的是删除不再需要的备份。DMRMAN 工具提供 SHOW、 CHECK、REMOVE、 LOAD 等命令分别用来查看、校验、删除和导出备份集。下文将对这些命令进行详细介绍。若命令中指定了 dm.ini&#…

如何在原生项目中集成flutter

两个前提条件: 从flutter v1.17版本开始,flutter module仅支持AndroidX的应用在release模式下flutter仅支持一下架构:x84_64、armeabi-v7a、arm6f4-v8a,不支持mips和x86;所以引入flutter前需要在app/build.gradle下配置flutter支持的架构 a…

vue3-setup与vue2的data共存

文章目录 前言一、vue3的setup响应式状态生命周期钩子示例注意事项 二、与vue2 的data 共存setup 与 data 的区别setup 与 data 的共存注意事项示例 前言 vue3 setup 学习 一、vue3的setup Vue 3 的 setup 函数是 Composition API 的核心,它提供了一种新的方式来使…

《设计模式之美》- 总结

《设计模式之美》- 总结 第一章 概述 1.1 为什么学习代码设计 编写高质量的代码应对复杂代码的开发程序员的基本功职业发展的必备技能 1.2 如何评价代码的质量 1.2.1 可维护性 可维护性代码的特性:代码简洁、可读性好、可扩展性好代码分层结构清晰、模块化程度…

maven问题汇总

​ 1、报错 failed to transfer from http://0.0.0.0/ during a previous attempt. com.byd.xxx:xxx-parent:pom:1.1.0-SNAPSHOT failed to transfer from http://0.0.0.0/ during a previous attempt. This failure was cached in the local repository and resolution is no…

【Pytorch】PytorchCPU版或GPU报错异常处理(10X~4090D)

Pytorch为CPU版或GPU使用报错异常处理 文章目录 Pytorch为CPU版或GPU使用报错异常处理0.检查阶段1. 在conda虚拟环境中安装了torch2.卸载cpuonly3.从tsinghua清华源安装不完善误为cpu版本4.用tsinghua清华源安装成cpu错误版本5.conda中torch/vision/cudatoolkit版本与本机cuda版…

探索Java设计模式:组合模式

探索Java设计模式:深入理解与实践组合模式 组合模式(Composite Pattern)是一种结构型设计模式,它将对象组织成树形结构,以表示“部分-整体”的层次关系,并允许客户端以一致的方式处理单个对象和对象集合。…

Python 计算给定公式的真值表

在Python中,计算给定逻辑公式的真值表可以通过编写一个简单的函数来实现,该函数遍历所有可能的输入变量组合,并计算表达式的值。以下是一个使用Python计算逻辑表达式真值表的示例: def calculate_truth_table(variables, express…

LeetCode - 283.移动零

题目链接&#xff1a; LeetCode - 283.移动零 题目分析&#xff1a; ​​​​​ 题解代码&#xff1a; #include<iostream> #include<vector> using namespace std;class Solution { public:void moveZeroes(vector<int>& nums) {for (int cur 0, des…