驱动开发-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级运行时,任何细小的错误都会影响整个系统,所以如果仅仅是可以用,那么这个驱动程序很可能带来灾难。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Numpy】对于 Numpy 中 Axis 的理解

文章目录 前言理解轴的两个角度在维度变化方向上计算降维 示例剖析写在最后 前言 Numpy 是 Python 中一个常用科学计算库,常用来表示向量、矩阵以及多维度数组。在 Numpy 中多对某一个维度(轴)进行相应的操作,这一点经常出错。今…

再论图像变化和频率的关系。

我之前是做了一些探讨,但是没说清楚,现在再看这个问题。 我先提出这个问题。 以以为点列为例,先写成傅里叶级数的形式,不过这里不是三角函数形式,而是指数形式,是一样的。 对f(n)求导,就可以观…

【大语言模型LLM】-使用大语言模型搭建点餐机器人

关于作者 行业:人工智能训练师/LLM 学者/LLM微调乙方PM发展:大模型微调/增强检索RAG分享国内大模型前沿动态,共同成长,欢迎关注交流… 大语言模型LLM基础-系列文章 【大语言模型LLM】-大语言模型如何编写Prompt?【大语言模型LL…

C语言—字符指针,指针数组和数组指针详解

字符指针 在指针的类型中我们知道有一种指针类型为字符指针 char* ; int main() {char ch w;char *pc &ch;*pc w;return 0; }还有一种使用方式如下: int main() {const char* pstr "hello world.";//这里是把一个字符串放到pstr指针变量里了吗…

chrome浏览器查看css样式

样式的查看 1.匹配器为灰色文本: 表示非当前选择器 2.样式有划线标识:CSS属性无效或未知 / 属性值无效 / 被其他属性覆盖的属性 3.属性以浅色文本显示且有感叹号提示:属性虽然有效,但由于CSS逻辑而没有任何影响 转自:…

笔试狂刷系列--Day1

大家好,我是LvZi,今天开启新的章节笔试狂刷系列 一.两个数组的交集 1. 题⽬链接: 两个数组的交集 思路分析: 查找两个数组的公共元素,一开始可能想到使用Set,先遍历第一个数组,存储nums1中所有的元素,接着遍历nums2中的所有元素,判断是否在Set之中,但是发现在遍历第二个数组…

神经网络中的神经元和激活函数介绍

文章目录 1、什么是人工神经网络 2、什么是神经元 3、什么是激活函数 线性激活函数 Sigmoid激活函数 双曲正切激活函数 修正线性单元(ReLU)激活函数 Leaky ReLU激活函数 Softmax激活函数 1、什么是人工神经网络 神经网络能够利用多层神经元学习复杂的模…

使用docker打包当前服务器的neo4j环境

Docker 是一个开源的应用容器引擎,它允许开发者将应用程序及其依赖打包到一个可移植的容器中,这样应用程序就可以在任何支持Docker的平台上运行,而无需担心环境差异。 当运行一个Docker容器时,它会加载一个镜像并运行它。Docker在容器内部创建一个隔离的环境,这个环境被称…

Redis学习-Redis的九种数据结构

String (字符串) 虽然redis是用C语言编写,但是redis中的string是redis自己实现的字符串结构,叫Simple Dynamic String简称(SDS),因为redis做为中间件会接受不同语言编写的程序传过来的字符串&a…

Oracle Hint 语法详解

什么是Hint Hint 是 Oracle 提供的一种 SQL 语法,它允许用户在 SQL 语句中插入相关的语法,从而影响 SQL 的执行方式。 因为 Hint 的特殊作用,所以对于开发人员不应该在代码中使用它,Hint 更像是 Oracle 提供给 DBA 用来分析诊断问…

Python中pyside2出现的pyside2 qt platform plugin could be in错误及其解决方法

系统平台:Win10 64bit python版本: python 3.8 使用pip install pyside2安装 pyside2 这是找不到QT平台的插件,这是环境变量QT_QPA_PLATFORM_PLUGIN_PATH出现错误 具体解决方法: 我们可以在每一段程序开始之前设定环境变量&…

pytorch与深度学习

ChatGPT PyTorch是一个由Facebook AI Research Team开发的开源深度学习库,它提供了一个灵活的环境和丰富的API,用于快速且方便地构建、训练和部署深度学习模型。PyTorch在科学界和工业界都收到了广泛的使用,其中包括了学术研究、小型项目和大…

第50篇:算法的硬件实现<一>

Q:本期我们来开始介绍如何使用算法状态机(ASM)图在硬件开发板上实现算法。 A:算法状态机 (Algorithmic State Machine,ASM) 图是描述数字系统控制单元的工作流程图,主要用来描述控制单元的时序操作特性&am…

功能测试用例设计思路

我们为什么要写好一份测试用例呢?测试同学应该都知道测试用例的重要性,测试用例就是我们测试的依据,也是测试过程中不能缺少的测试文档。 一、用例编写规范目的: 1、提高测试用例的可读性,可执行性、合理性。 2、测试…

Spring(下)

接上篇,从第八个问题讲起 八.Spring工厂创建复杂对象 1.什么是复杂对象 简单对象就是可以直接new出来的,也就是直接调用构造方法创建 所以复杂对象就是不能直接通过调用构造方法创建。就比如JDBC中的Connection 2.三种方法 (1&#xff…

4月阿里offer被毁,我该怎么进字节?

在校招求职的浪潮中,有些故事总是让人唏嘘不已。比如最近在社交平台上广泛讨论的一个话题:“4月阿里offer被毁,我该怎么进字节?”这不仅反映了当下职场的变动性,也映射了求职者在面对突如其来的变故时的无助与挣扎。 …

基于springboot实现精准扶贫管理系统项目【项目源码+论文说明】计算机毕业设计

基于SpringBoot实现精准扶贫管理系统演示 摘要 随着信息技术在管理上越来越深入而广泛的应用,管理信息系统的实施在技术上已逐步成熟。本文介绍了精准扶贫管理系统的开发全过程。通过分析精准扶贫管理系统管理的不足,创建了一个计算机管理精准扶贫管理系…

docker-003镜像制作

步骤 使用docker commit提交容器使之成为镜像以ubuntu安装vim后的容器为例 1 ubuntu安装vim 启动容器 docker run -it --nameubuntu-vim ubuntu /bin/bash安装vim apt-get update apt-get install vim2 提交容器作为镜像 查看容器 docker ps -a提交容器作为镜像 命令格式&…

随机森林计算指标重要性—从决策树到随机森林Python实现

文章目录 前言一、节点二、决策树2.1 案例分析——优良的水稻2.2 案例分析——家庭财富水平 三、随机森林三、Python代码实现3.1 关键问题3.1.1 节点的表示3.1.2 决策树的表示** 根节点划分左右子树的依据 **3.1.3 随机森林的构造与重要性的表示 3.2 节点类3.2 决策树类3.2.1 初…

阿里云OSS对象存储的使用和快速入门

一、介绍 云存储是一种通过互联网连接的服务,允许用户将数据存储在远程的云服务器上,而不是存储在本地设备上。这些数据可以包括文件、数据库、备份等各种类型的信息。云存储服务提供商通常会管理和维护这些存储设施,并提供用户可以访问、管…