驱动开发4.基本编程技术一
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
核心模式编程环境
I/O管理器(Io前缀) 包含许多驱动程序可 以使用的服务函数,后面将有很多对这些 函数的描述。 例如IoCallDriver 调用把 IRP交给下层驱动程序处理。 进程结构模块(Ps前缀) 创建并管理内核 模式线程。普通的WDM驱动程序应使用一 个独立的线程来循环检测无中断生成能力 的设备。如PsCreateSystemThread 创建 一个内核模式线程
内核模式的结构化异常特点
结构化异常机制可以使内核模式代码在访 问一个非法的用户模式地址后避免系统崩 溃 不能捕捉其它处理器异常,例如被零除或 试图访问非法的内核模式地址。 这种机制在内核模式中不象在用户模式中 那样具有通用性
如何实现?
内核模式程序通过在内存堆栈上建立异常 帧来实现结构化异常(这个堆栈就是程序 用于参数传递、子程序调用,和分配自动 变量所使用的内存堆栈。) 每个异常帧都指定一个过滤函数 ,当异常 发生时,操作系统通过扫描堆栈异常帧来 寻找相应的异常处理程序
核心模式编程环境
运行时间库部件(Rtl前缀) 包含工具例程,例如串管理 例程,内核模式驱动程序可以用这些例程来替代常规的 ANSI标准例程。大部分例程可以从其名字上直接看出它 的功能。 如RtlAnsiStringToUnicodeSize Win32子系统存在于用户模式中,所以用户模式中的应 用程序可以容易地调用其例程。为了方便,Windows NT 在内核模式中实现了一些有Zw前缀名的函数,这些函数 可以使驱动程序调用Win32子系统例程。Windows 2000 DDK中仅暴露很少一部分这样的函数给驱动程序使用, 包括访问文件和注册表的函数。 如ZwCreateFile ZwQueryValueKey
核心模式状态代码和用户模式状态 代码
关于和应用程序接口部分的状态代码。 以错误状态完成一个IRP,通常会导致一个Win32 函数调用错误返回。应用程序可以调用 GetLastError得到出错的原因。 如果在驱动程序中IRP失败时的客户位 (customer)置1,GetLastError函数将把错误代 码直接返回,不作任何修改。反之,客户位 (customer)置0(Microsoft定义的标准错误代 码都是这种情况),GetLastError函数返回一个 在WINERROR.H头文件中定义的标准代码。
核心模式编程环境
Windows NT内核(Ke前缀) 所有多线程和多处理 器的低级同步活动都发生在内核中。这种Ke函 数使用的最多,如KeInitializeDeviceQueue 在操作系统的最底层是硬件抽象层(HAL,Hal前 缀)。操作系统把所有关于计算机硬件如何连接 的信息都存放在HAL中。HAL了解如何在特定平 台上实现中断操作,如何实现自旋锁,如何寻 址I/O或内存映射设备,等等。 WDM驱动程序不 直接与硬件对话,它通过调用HAL中的函数来达 到目的。所以WDM驱动程序能够实现平台无关和 总线无关。
错误处理
下面将描述三种错误处理形式:状态代码、结构 化异常处理,和bug check 一般,内核模式支持例程通过返回状态代码来报 告意外错误。对于正常情况,它们将返回布尔值 或者数值而不是正式的状态代码。 结构化异常处理为异常事件发生后的清除工作提 供了一个标准化方法,它可以避免因为异常事件 而导致系统崩溃,异常事件是指诸如被零除或参 考无效指针等的意外错误。 Bug check实际上就是致命错误的内部名称,对于 这种错误,唯一的解决办法就是重启动系统。
核心模式编程环境
内存管理器(Mm前缀) 控制页表,页表定义 了虚拟内存到物理内存之间的映射。如 MmMapIoSpace 把物理地址映射为虚拟地 址 执行支持 (Ex前缀) 提供堆管理和同步服 务。 如ExAllocatePoolWithTag 从内存池 中分配内存,ExAcquireFastMutex获取一 个快速互斥对象
Try-Finally块
__try{
<guarded body>
} __finally {
<termination handler>
}
Try-Finally块
语法上,try-finally按下面方式工作。首 先,计算机执行被保护体。由于某种原因控制 离开被保护体,计算机执行终止处理程序 。 如下图:
错误和警告
平时编程如何对待错误和警告? 错误和警告之间的差别有时候是很明显的。 内核如何处理? 在METHOD_BUFFERED的控制操作中,如果你以 STATUS_BUFFER_OVERFLOW完成一个IRP――这是一 个警告,I/O管理器会把数据拷贝回用户缓冲区。 但是如果你以STATUS_BUFFER_TOO_SMALL完成一个 IRP――这是一个错误,I/O管理器不会把数据拷 贝回用户缓冲区。
常用的核心模式状态代码和用户模 式状态代码的对应关系
Kernel-Mode Status Code STATUS_SUCCESS STATUS_INVALID_PARAMETER STATUS_NO_SUCH_FILE STATUS_ACCESS_DENIED STATUS_INVALID_DEVICE_REQU EST ERROR_BUFFER_TOO_SMALL STATUS_DATA_ERROR User-Mode Error Code NO_ERROR (0) ERROR_INVALID_PARAMETER ERROR_FILE_NOT_FOUND ERROR_ACCESS_DENIED ERROR_INVALID_FUNCTION ERROR_INSUFFICIENT_BUFFER ERROR_CRC
侧效(Side effects)
#define min(x,y) (((x)<(y)) ? (x) : (y)) int a = 2, b = 42, c; c = min(a++, b); 最后 c结果是什么? 还有哪些表达式有侧效?
侧效(Side effects)
通常,不知道DDK什么时候使用宏,什么时 候使用真正的外部函数。有时候,一个特 殊的服务函数在某些平台上是宏而在其它 平台上却是外部函数。此外,Microsoft也 可能在将来改变想法。所以,当写WDM驱动 程序时应坚守下面原则: 决不在内核模式服务函数的参数中使用 带有侧效的表达式。
状态代码
内核模式支持例程(以及我们写的驱动代码) 通过向其调用者返回一个NTSTATUS状态代码 来表明调用是否成功。NTSTATUS是一个由多 个子域组成的32位整数,如下图。
状态代码
高两位(Severity)指出状态的严重性——成功、 信息、警告、错误 客户位(Customer)是一个标志,完成的IRP将携带 一个表明完成状态的状态代码,如果这个状态代 码中的Customer标志被设置,那么这个状态代码 将被不修改地传回应用程序(应用程序通过调用 GetLastError函数获得)。通常,状态代码在返给 应用程序前要翻译成Win32错误代码 facility代码指出该状态是由哪个系统部件导致 的,一般用于减少开发组之间的代码关联。剩下 的16位代码指出实际的状态。
运行时间库
运行时间库:系统提供两套运行时间库,一个是 为用户模式程序,一个是为内核模式。 DDK文档指出内核模式驱动程序仅应调用DDK中公 开的函数。所以我们应该使用Rtl开头的内核模式 运行时间库,例如不要在驱动程序中调用wcscmp 函数,而应该调用RtlCompareUnicodeString。 但是,还是有很多人使用用户模式运行时间库, 因为他们知道很多的内部细节,他们知道哪些情 况下可以使用哪些函数。
核心模式编程环境
对象管理器(Ob前缀) 集中控制Windows NT中的各种数据对象。WDM驱动程序仅需 要对象管理器维护对象的参考计数,以防 止对象被意外删除。 如 ObReferenceObject 增加一个指定对象的 参考计数 安全参考监视器(Se前缀) 使文件系统驱 动程序执行安全检测。I/O请求到达WDM驱 动程序前已经做完了安全检测。
状态代码检测方法
我们应该总是检测例程的返回状态。 如果状态码高位为0,那么不管其它位是否 设置,该状态代码仍旧代表成功。所以, 绝对不要用状态代码与0比较来判断操作是 否成功,应该使用NT_SUCCESS宏: NTSTATUS status = SomeFunction(...); if(!NT_SUCCESS(status)){ <handle error>}
内核模式的使用
在许多情况下,WDM驱动程序例程接收到的 参数都是经过其它代码严格检验的,一般 不会成为导致异常的原因。 但我们仍要遵循这样基本原则:对用户模 式虚拟内存直接引用的代码段应该用结构 化异常保护起来。 这样的引用通常发生在调用 MmProbeAndLockPages、ProbeForRead,和 ProbeForWrite函数时。
几种异常
核心模式和用户模式的SEH处理机制差不 多。 用户模式try finally try except ?主要 区别? C++异常处理catch throw? C++异常处理是针对语言的,SEH是能够应 用于任何语言的操作系统特性,在Visual c++中的C++异常也是通过一个try except 实现的
结构化异常处理
结构化异常(SEH)? 结构化异常.doc 结构化异常处理与编译器的代码生成器紧密集成,它允许 你在自己的代码段周围加上保护语句,如果被保护代码段 中的任何语句出现异常,系统将自动调用异常处理程序。 为什么要引入? MS在windows中引入SEH的动机是为了便于操作系统的开 发,使得操作系统更加强壮。 SEH使得程序员可以集中精力处理眼前的工作,将可能发 生的错误处理放在后面处理。 SEH使得程序更加容易理解,代码更加容易维护,如果使 用得当,具有最小的系统开销。
错误处理
To err is human; to recover is part of software engineering. 程序中总会发生异常情况,其中一些源自 程序中的Bug,或者在我们的代码中或者在 调用我们代码的用户模式应用程序中。 另一些涉及到系统装载或硬件的瞬间状 态。无论什么原因,代码必须能对不寻常 的情况作出恰当的反应。
基本编程技术
sigang@mti.xidian.edu.cn
内容
核心模式编程环境 错误处理 内存管理 字符串处理 访问注册表 访问文件 浮点运算 有关调试
普通的Windows程序编程环境
用到了哪些函数? 由谁来提供这些函数的服务? 内核模式编程环境呢?
核心模式编Fra Baidu bibliotek环境
右图显示 了Windows NT操作系 统的某些 组成部 分。每个 部分都输 出一些服 务函数
一个简单的例子:
LONG counter = 0; __try { ++counter; } __finally { --counter; }KdPrint(("%d\n", counter));
一个稍复杂的修改 ,结果是什么?
VOID RandomFunction(PLONG pcounter) { __try { ++*pcounter; return; } __finally { --*pcounter; } }
状态代码
不仅要检测调用例程的返回状态,还要向调用你 的例程返回状态代码。 在前面,讲述的两个驱动程序例程,DriverEntry 和AddDevice,它们都定义了NTSTATUS返回代码。 所以,如果这些例程成功,则应返回 STATUS_SUCCESS。如果在某个地方出错,应返回 一个适当的错误状态代码。 有时函数返回的状态代码就是出错函数返给你的 状态代码。 前面AddDevice函数,加入了错误检测
结论
不管控制以何种原因离开被保护体,包括 通过return语句或goto语句,终止处理程 序都将执行。
加深对try-finally语句的理解:
static LONG counter = 0; __try { ++counter; BadActor(); } __finally { --counter; } …