从Intel80386开始,出于安全性和稳定性的考虑,该系列的CPU可以运行于ring0~ring3从高到低四个不同的权限级,对数据也提供相应的四个保护级别。运行于较低级别的代码不能随意调用高级别的代码和访问较高级别的数据,而且也只有运行在ring0层的代码可以直接对物理硬件进行访问。由于WindowsNT是一个支持多平台的操作系统,为了与其他平台兼容,它只利用了CPU的两个运行级别。一个被称为内核模式,对应80x86的ring0层,是操作系统的核心部分,设备驱动程序就是运行在该模式下;另一个被称为用户模式,对应80x86的ring3层,操作系统的用户接口部分(就是我们通常所说的win32 API)以及所有的用户应用程序都运行在该级别。操作系统对运行在内核模式下的代码是不设防的,所以不管是建设还是破坏内核模式下的编程都是值得去研究的。
如下图所示为Windows操作系统驱动程序模型:
Windows驱动程序既可以运行在用户态也可以运行在核心模态。
用户态与核心太驱动程序的区别
♦ 用户态的驱动程序运行在非特权处理机模式(nonprivileged processor mode)上,其他一些被保护的子系统代码也运行在该模式上。用户态的驱动程序不能获得系统数据的存取权,除非调用Win32 API或者系统服务。
♦ 核心态驱动程序作为操作系统的一个组成部分被执行——支持一个或多个受保护的子系统的操作系统底层组件。
用户态和核心态驱动程序有不同的结构,不同的入口点和不同的系统接口。一个设备是需要一个用户态驱动程序还是需要一个核心态驱动程序依赖于该设备的类型和操作系统对它提供的支持。
用户模式与内核模式是如何交互的呢
当用户模式程序需要读取设备数据时,它就调用Win32 API函数,如ReadFile。Win32子系统模块(如KERNEL32.DLL)通过调用平台相关的系统服务接口实现该API,而平台相关的系统服务将调用内核模式支持例程。在ReadFile调用中,调用首先到达系统DLL(NTDLL.DLL)中的一个入口点,NtReadFile函数。然后这个用户模式的NtReadFile函数接着调用系统服务接口,最后由系统服务接口调用内核模式中的服务例程,该例程同样名为NtReadFile。
系统中还有许多与NtReadFile相似的服务例程,它们同样运行在内核模式中,为应用程序请求提供服务,并以某种方式与设备交互。它们首先检查传递给它们的参数以保护系统安全或防止用户模式程序非法存取数据,然后创建一个称为“I/O请求包(IRP)”的数据结构,并把这个数据结构送到某个驱动程序的入口点。在刚才的ReadFile调用中,NtReadFile将创建一个主功能代码为IRP_MJ_READ(DDK头文件中的一个常量)的IRP。实际的处理细节可能会有不同,但对于NtReadFile例程,可能的结果是,用户模式调用者得到一个返回值,表明该IRP代表的操作还没有完成。用户模式程序也许会继续其它工作然后等待操作完成,或者立即进入等待状态。不论哪种方式,设备驱动程序对该IRP的处理都与应用程序无关。
驱动程序完成一个I/O操作后,通过调用一个特殊的内核模式服务例程来完成该IRP。完成操作是处理IRP的最后动作,它使等待的应用程序恢复运行。