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

news2024/12/26 21:52:42

驱动程序和应用程序不一样的,由于其直接运行于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.coloradmin.cn/o/1611649.html

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

相关文章

C++语言·类和对象(下)

1. 初始化列表 我们回忆上节写的MyQueue类,其中有两个栈类和一个int类型,栈类因为其特殊性,要开空间,所以我们必须手搓Stack类的构造函数。但是正常来说MyQueue自动生成的构造函数会调用自定义类型的默认构造函数,也就…

C语言 | Leetcode C语言题解之第41题缺失的第一个正数

题目&#xff1a; 题解&#xff1a; int firstMissingPositive(int* nums, int numsSize) {for (int i 0; i < numsSize; i) {while (nums[i] > 0 && nums[i] < numsSize &&nums[nums[i] - 1] ! nums[i]) {int t nums[nums[i] - 1];nums[nums[i] -…

SQLite运行时可加载扩展(三十五)

返回&#xff1a;SQLite—系列文章目录 上一篇:SQLite轻量级会话扩展&#xff08;三十四&#xff09; 下一篇&#xff1a;SQLite—系列文章目录 1. 概述 SQLite 能够在运行时加载扩展&#xff08;包括新的应用程序定义的 SQL 函数、整理序列、虚拟表和 VFS&#xff09…

NineData正式将SQL开发正式升级为数据库DevOps

NineData SQL 开发早期主要提供 SQL 窗口&#xff08;IDE&#xff09;功能&#xff0c;产品经过将近两年时间的打磨&#xff0c;新增了大量的企业级功能&#xff0c;时至今日已经服务了上万开发者&#xff0c;覆盖了数据库设计、开发、测试、变更等生命周期的功能。 为了让企业…

C++相关概念和易错语法(7)(初始化列表、隐式类型转换、友元)

1.初始化列表 初始化列表是集成在构造函数里面的&#xff0c;对象在创建的时候一定会调用构造函数&#xff08;就算不显式定义&#xff0c;也会自动生成并调用&#xff09;。初始化列表就是这些对象的成员变量在创建的时候初始化的地方。 下面是使用的例子&#xff0c;可以先…

接口测试相关

接口测试&#xff0c;接口 接口是数据交互的入口和出口 接口是一套规范和标准 统一设计标准 前后端相对独立 扩展型灵活 接口文档。 接口测试 接口测试环境&#xff0c;运行程序&#xff0c;自己搭建环境 接口测试插件 谷歌postman 火狐 restclient java测试工具为j…

Docker - 入门基础

原文地址&#xff0c;使用效果更佳&#xff01; Docker - 入门基础 | CoderMast编程桅杆https://www.codermast.com/dev-tools/docker/docker-basic.html Docker架构 Docker 使用的是客户端-服务端&#xff08;C/S&#xff09;架构模式&#xff0c;使用远程 API 来管理和创建…

滚动条详解:跨平台iOS、Android、小程序滚动条隐藏及自定义样式综合指南

滚动条是用户界面中的图形化组件&#xff0c;用于指示和控制内容区域的可滚动范围。当元素内容超出其视窗边界时&#xff0c;滚动条提供可视化线索&#xff0c;并允许用户通过鼠标滚轮、触屏滑动或直接拖动滑块来浏览未显示部分&#xff0c;实现内容的上下或左右滚动。它在保持…

NASA数据集——TANSO-FTS 运行前 11 年收集的测量数据中得出二氧化碳(CO2)干空气摩尔分数(XCO2)的估计值

ACOS GOSAT/TANSO-FTS Level 2 bias-corrected XCO2 and other select fields from the full-physics retrieval aggregated as daily files V7.3 (ACOS_L2_Lite_FP) at GES DISC 简介 ACOS Lite 文件包含经过偏差校正的 XCO2 以及其他选定字段的每日汇总文件。ACOS 2 级标准…

Unity ECS

一&#xff1a;前言 ECS与OOP不同&#xff0c;ECS是组合编程&#xff0c;而OOP的理念是继承 E表示Entity&#xff0c;每个Entity都是一个有唯一id的实体。C表示Component&#xff0c;内部只有属性&#xff0c;例如位置、速度、生命值等。S表示System&#xff0c;驱动实体的行为…

js 函数节流和函数防抖及区别详解

文章目录 1. 前言2. 函数节流3. 函数防抖4. 总结 1. 前言 浏览器中总是有一些操作非常耗费性能。所以就有了函数节流和函数防抖来提高浏览器性能。 函数节流&#xff1a;频繁触发一个事件时候&#xff0c;每隔一段时间&#xff0c;函数只会执行一次。 函数防抖&#xff1a;当触…

js 遍历数据结构,使不符合条件的全部删除

js 遍历数据结构&#xff0c;使不符合条件的全部删除 let newSourceJSON.parse(JSON.stringify(state.treeData))state.expandedKeys[]checkedKeys.map((item:any)>{loop(newSource,{jsonPath:item.split(&)[1]},state.expandedKeys)})function removeUnwantedNodes(tre…

react 项目路由配置(react-router-dom 版本 v6.3、v6.4)

根据 react-router-dom 的版本&#xff0c;有不同的方式 一、react-router-dom v6.3 用到的主要 api: BrowserRouteruseRoutesOutlet 下面是详细步骤&#xff1a; 1、index.js BrowserRouter 用来实现 单页的客户端路由使用 BrowserRouter 包裹 App放在 顶级 位置&#x…

香港服务器_免备案服务器有哪些正规的?企业、建站方向

香港服务器&#xff0c;是最受欢迎的外贸、企业建站服务器&#xff0c;在个人建站领域&#xff0c;香港服务器、香港虚拟主机都是首选的网站服务器托管方案&#xff0c;不仅其具备免备案的特点&#xff0c;而且国内外地区访问速度都很快。那么&#xff0c;现今2024年个人和企业…

使用Cesium ion将 Sketchfab 3D 模型添加到您的GIS应用中

您现在可以将 Sketchfab 中的 3D 模型导入 Cesium ion 中以创建 3D 块&#xff0c;从而更轻松地为地理空间体验创建上下文和内容。 Sketchfab 是 Epic Games 的一部分&#xff0c;也是使用最广泛的 3D 资产市场之一。自 2012 年推出以来&#xff0c;已有超过 1000 万用户使用 …

《设计模式之美》- 总结

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

Spring Boot + Thymeleaf 实现的任务发布网站

角色&#xff1a; 管理员雇主雇员 功能 雇主&#xff1a;登录、注册、发布任务、选择中标雇员、评价雇员雇员&#xff1a;登录、注册、查看任务列表、投标任务、收藏任务、完成任务管理员、登录、任务管理、雇主管理、雇员管理 部分功能截图 部署 导入数据库…

【剪映专业版】13快速为视频配好音:清晰、无噪声、对齐

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 使用场景&#xff1a;视频无声音或者视频有声音但是需要更改声音 时间指示器在哪里&#xff0c;就从哪里开始 红色按钮&#xff1a;开始录音 声音波纹&#xff1a;蓝色最佳&#xff0c;黄色或红色声音太大&#xff0c;…

【Django】学习笔记

文章目录 [toc]MVC与MTVMVC设计模式MTV设计模式 Django下载Django工程创建与运行创建工程运行工程 子应用创建与注册安装创建子应用注册安装子应用 数据模型ORM框架模型迁移 Admin站点修改语言和时区设置管理员账号密码模型注册显示对象名称模型显示中文App显示中文 视图函数与…