Windows 驱动程序的发展演变
我们在学习开发驱动程序时有必要弄清楚Windows设备驱动程序的发展演变过程(为了简便起见,以下简称驱动程序),以便明白我们将要开发什么样的驱动程序。这就象你开发一个应用程序时必须弄清楚它是运行在WINDOWS平台下还是在DOS平台下,否则我们能写出什么样的应用程序就可想而知了。
驱动程序开发者的各项任务之中,有许多是为特定的硬件编写驱动程序。由于WINDOWS的发展,这样的工作在 Windows 9X 下要比在前一版Windows(windows3.x 、Windows Workgroup) 中容易得多。先了解一些历史演变,可能会对驱动程序的编写有所帮助。
实模式Windows(Real-Mode Windows)
从一开始,MS-DOS 和系统基本输入输出系统(BIOS) 就已经提供了许多硬件设备的驱动程序。BIOS 通过一些常用的软件中断,开放出驱动程序的服务 ,像INT 10h 是显示系靳中断,INT 13h是磁盘子系靳中断,INT 16h 是键盘中断等等。BIOS 也处理硬件中断,并承担对“可编程中断控制器”(Programmable Interrupt Controller ,PIC )的管理任务。MS-DOS 也通过软件中断(如 INT 21h 、INT 25h 、INT 26h )提供了系统服务 ,并提供一个机制(CONFIG.SYS 中的 device= 语句),让新的或强化后的驱动程序能?蛟谙到y启动时被加载进操作系统内核。
标准模式Windows(Standard-Mode Windows)
早期的 Windows 中,MS-DOS 和 BIOS 是最重要的。Windows运行在实模式状态中,这时的Windows充其量不过是一个强化后的MS-DOS图形用户界面而已。从系统角度看,Windows只不过是个大的图形应用程序。Intel 80286 的出现,使 Windows能?蛟诒;つJ街性诵胁⒒竦酶叽? 16MB 实际内存空间。依靠保护模式和实模式的转换,Windows 仍然继续使用MS-DOS 和 BIOS 提供的服务来完成所有的系统需求。这种运作模式被称为 Windows标准模式(Windows standard mode) 。在 80286 机器上切换实模式和保护模式,系统开销很大。Intel 于是提供了一个快又有效率的指令,让你从实模式切换到保护模式。但Intel 认为没有什么人还需要再从保护模式切换回实模式。结果,唯一能?蛉帽;つJ匠绦颍ㄈ? Windows standard mode )存取实模式软件(如 MS-DOS )的方法就是复位CPU(reset CPU) 。在人们开发出来的各种复位方法中,最普遍的一种就是触发键盘控制器,提供由 Ctrl-Alt-Delete 键所发出的外部信号。于是引发所谓的三键失效(triple fault,即三键热启动),这是 CPU 先天无法处理的一种“失效“。事实上无论哪一种作法,代价都很昂贵,因为它们至少都得经过 BIOS 的引导程序 。事实上,在某些 286 机器,模式的切换要花掉好几毫秒。显然 Windows 需要一种方法,避免每次一有事件发生,像是键盘被按下或鼠标移动等等,就得切换到实模式。解?Q方法就是写一个保护模式驱动程序,可以在保护模式中处理 I/O 中断。这些驱动程序直到今天我们都还在使用,你在 SYSTEM 子目录中看到的扩展名为 .DRV 的文件都是!包括 MOUSE.DRV 、COMM.DRV 等等。我把它们称为 ring3 DLL 驱动程序,因为它们实质上都是 16 位 Windows 动态链接库(DLLs ),在 ring3层 (Intel CPU 最不受保护的层,一般应用程序运行在ring3层,核心态的驱动程序动行在ring0层)执行。它们的任务是在不离开 CPU保护模式的前提下,和 Windows KERNEL 、USER 、GDI 模块之间形成接口。
增强模式Windows(Enhanced-Mode Windows )
Intel 80386 CPU 使 Windows的第三种操作模式(所谓的 enhanced mode)成为可能。在此模式中 Windows 采用分页(paging) 和虚拟86(V86) 特性,创造出??拟机器(VirtualMachines ,VMs )。对一个应用程序而言,VM 就像一独立的的个人电脑,独自拥有自己的键盘、鼠标、显示器等等硬件。而实际上,经过所谓的??拟化(virtualization ),数个 VMs 共享相同硬件。对最终用户而言,最大的好处是他现在能?蛟诖翱谧刺?中(而非全屏幕)运行MS-DOS程序 。"??拟化"是 VxDs 的工作。VxD 的名称来自于 "virtual x device",意思是此驱动程序用来??拟化某个(x )设备。例如:VKD用来??拟化键盘,使Windows 和任何一个MS-DOS程序都自认为独立拥有属于自己的键盘。VMD 用来??拟化鼠标。某些 VxDs 并不是为了??拟化某些硬件,而是为了提供各种底层系统服务。页面交换(PAGESWAP) 和 页面文件(PAGEFILE)就属于这种非设备VxD ,它们共同管理交换文件(swap file ),使 增强模式Windows (enhanced-modeWindows) 得以将磁盘空间分配成为??拟内存的一部份。尽管基础技术令人耳目一新,但增强模式Windows (enhanced-mode Windows )还是继续在磁盘和文件 I/O 方面使用 MS-DOS 和 BIOS 。需要交换(swap )一个文件时,它还是把 CPU 切换到 V86 模式,让 MS-DOS 和 BIOS 来处理 I/O 操作。在保护模式、真实模式、V86 模式之间的所有切换动作都使得 Windows 慢下来。更多
的延时则来自于MS-DOS 和 BIOS 不可重入这一问题(即不能两个程序同时使用相同的服务)。Windows 必须强迫所有应用程序在同一个队列等待实模式服务。
Windows95
Windows 95 将终结这一份对历史的回忆。Windows 95 使用数种不同的驱动程序模型,大部份是使用 32 位 ring0层的虚拟设备驱动程序(VxDs) ,而非 rin3层的 DLLs 。所有的设备驱动程序都有一个具有管理功能的核心虚拟机VMM(虚拟机管理器)管理。
Windows对中断的处理与MS-DOS大不一样。当中断发生时,处理器转换为ring0级保护模式。Windows系统并不像MS-DOS那样通过中断描述符表IDT(Interrupt Descriptor Table)直接指向中断处理过程,而是由IDT入口指向VMM中的程序。该程序将判断是否为中断调用,如果是,则把中断控制权交给虚拟可编程中断控制器VPICD(Virtual Programmable Interrupt Controller Device),VPICD实际上是一个重要的VxD。VPICD再将其交给另一个注册了该中断的VxD(如Audcard.vxd)来处理。VxD程序是通过调用VPICD服务VPICD_Virtualize_IRQ来注册中断的。
Windows 95 对于设备 (device) 的处理,一般的模型是:由一个 VxD 掌管所有中断并执行所有数据传输,应用程序则使用函数调用 (function calls) 的方式对 VxDs 发出服务请求。这种VxD 为主的设备规划模型的一个好例子就是:Windows 95 的串行通信(serial communications) 。从前 Windows的串行通讯是使用一个 ring3 驱动程序(COMM.DRV ),?群?硬件中断处理程序以及驱动一个通用异步收发蕊片(universal asynchronous receiver-transmitter (UART )蕊片)所需的全部逻辑功能代码。在未让此驱动程序知道的情?r下,两个 VxDs (VCD 和COMBUFF )拦截了硬件中断和软件 IN/OUT 指令,为的是??拟化每一个 port ,并且改善因多任务而引起的问题。Windows 95 也有一个 ring3 组件名为 COMM.DRV ,但这个组件已经成为新的VxD (VCOMM )的一个简单的外层程序,只用来提供 16 位程序和 VCOMM之间的接口。VCOMM 则处于底层,联结一般应用程序、VxD clients 、 VxD 端口驱动程序和实际的硬件。端口驱动程序现在负责处理所有中断,并执行真正与硬件起作用的 IN/OUT 指令。
Windows 95 文件系统是另一个好例子。过去,对文件系统服务的请求(requests ),源自于16 位保护模式程序所发出的 INT 21h 。有一个 VxD 用来处理这些 INT 21h ,并将它们切换到 V86 模式,以便让MS-DOS 处理。MS-DOS 发出 INT 13h中断 ,以请求使用 BIOS 的磁盘 I/O 功能;发出 INT 2Fh ,允许网络的 "redirector modules"(重新定向模块)将此请求通过网络传输出去。Windows 95 提供给应用程序的,仍是向上兼容的接口,INT 21h 仍旧是导至文件系统的动作,但是底层基础却大不一样。
在 Windows 95 之中,一个名为“可安砚文件系统“(Installable File System ,IFS )的管理器会处理所有 INT 21h ,甚至是来自于 V86 模式的。然后它把控制权交斤一个文件系统驱动程序(File System Driver ,FSD )。有一个 FSD 名为 VFAT ,是针对 MS-DOS
文件系统(所谓 File Allocation Table ,FAT )而设计;另一个 FSD 名为 CDFSD ,可以解析 CD-ROM 格式;此外还有其他 FSDs ,知道如何经由各种网络彼此通讯。针对本机(local 端)FSD (如VFAT )的磁盘操作,会经过被I/O管理器(Input/Output Supervisor ,IOS)监视管理的一堆VxDs处理。甚至 V86 模式的 INT 13h 中断调用最终也是由 IOS 处理。换句??真,实模式和保护模式所发出的对文件系靳的请求(request ),不论是针对本地(local )或远程(remote )磁盘,有可能完全(或几乎完全)由 VxDs 来处理。Windows 95 这种以 VxD 为中心的驱动程序模型,好处之一是,系统程序员不一定要是 MS-DOS 和 BIOS 的专家,就可以写驱动程序。那些准备提供系统扩展组件的程序员,也同享这个好处;原本你必须了解DOS保护模式接口(DPMI)以及 Windows 核心模块的许多神秘特性或未公开特性,现在只需了解 Win32 的 DeviceIoControl API 函数,以及那些支持所谓 "alertable waits”(即时唤醒,大意是那些可以在VXD中调用的Windows 32位 API函数,但数量极其有限,)的 Win32 API 即可。这两个接口可以让你把 VxD 当做 32 位应用程序的扩展组件。尽管Windows系统驱动程序设计的任务主要是在系统底层上扩展 Windows 的功能,但Windows 95 还是保留了令人印象深刻的向上兼容能力(对上层程序,如dos程序来说,它们的调用接口没变,但底层实际操作却大不一样了)。DPMI 还是存在(有些16 位程序还是需要它),你还是可以运行实模式的网络驱动程序或文件系统驱动程序--如果这是你的必要选择。事实上,你往往可以把 Windows 3.1 的一整组硬件设备、网络驱动程序、16 位应用程序及其必要的 VxDs 整个搬到 Windows 95 ,不至于遭遇什么大问题。 Windows98&2k&NT
1996年的Windows Hardware Engineering Conference(WinHEC)会议上,Microsoft宣布了一种新的Windows设备驱动程序模型――Win32 Driver Model(WDM)。这种新的设备驱动程序模型将成为Windows 2000(即Windows NT 5.0)的核心。
这个消息令从事Windows设备驱动程序(VxD)开发的程序员感到沮丧(虽然大家早已预料到Windows系列与Windows NT系列最终将走到一起)。WDM将vxd的开发人员带到了一个新的起点上,什么都是新的:新的模式,新的观点。如果你曾看过DDK的汇编代码的话,你一定可以体会这个消息对VxD开发者是个沉重的打击,而对于Windows NT设备驱动程序(Kernel Mode Driver)的开发者来说,却是另一番心情――因为WDM基本等于Kernel Mode Driver+Plug and Play。 VxD将让位于WDM,现在令我们欣慰的是Microsoft宣布Windows 98(Windows 98支持VxD,推荐使用WDM方式驱动,但有些设备,如打印机等还不能用它,微软预先设想的是Windows98和Windows 2k x86版在WDM驱动上可以二进制码兼容,但实际上没有完全实现)可能会坚持到200X年(天知道,估计也就是三两年)。在这期间,掌握VxD技术的你还是可以主动要求老板给你加薪的。即使到了WDM一统天下之时,也不用灰心,因为无论是VxD还是WDM,都要求开发人员对计算机硬件有着全面而细致的了解。通过VxD的锻炼,你至少熟悉了计算机的硬件资源并对保护模式有了比较深刻的认识,这些东西都是将来从事WDM开发的硬功夫。 好了,该说说Windows NT了。在Windows NT中,80386保护模式的“保护”比Windows 95中更坚固,这个“镀金的笼子”更加结实,更加难以打破。在Windows 95中,至少应用程序I/O操作是不受限制的,而在Windows NT中,我们的应用程序连这点权限都被剥夺了。在NT中几乎不太可能进入真正的ring0层。
在Windows NT中,存在三种Device Driver: 1.“Virtual device Driver” (VDD)。通过VDD,16位应用程序,如DOS 和Win16应用程序可以访问特定的I/O端口(注意,不是直接访问,而是要通过VDD来实现访问)。 2.“GDI Driver”,提供显示和打印所需的GDI函数。 3.“Kernel Mode Driver”,实现对特定硬件的操作,比如说CreateFile, CloseHandle (对于文件对象而言), ReadFile, WriteFile, DeviceIoControl 等操作。“Kernel Mode Driver”还是Windows NT中唯一可以对硬件中断和DMA进行操作的Driver。SCSI 小端口驱动和 网卡NDIS 驱动都是Kernel Mode Driver的一种特殊形式。
Visual studio11与Windows8带来格外不同的新体验
1.启动Vs11 ![]()
2.看见满目的驱动开发模板 ![]()
3.选择一个驱动模式,有内核模式与用户模式两种的驱动 ![]()
4.创建一个驱动程序,KMDF DriverMVP ![]()
5.我们选择的是内核模式的驱动程序,下面是创建成功后的界面,分别是驱动程序本身,与驱动安装包 ![]()
6.按下F5,选择驱动编译,
![]()
7.编译成功我们开始调试 ![]()
8.选择继续调试 选择驱动模式设置
![]()
![]()
9.选择穿越防火墙,继续调试 ![]()
10.主要代码框架分析如下,device描述代码 [cpp] view plaincopy
- /*++
-
- Module Name:
-
- device.c - Device handling events for example driver.
-
- Abstract:
-
- This file contains the device entry points and callbacks.
-
- Environment:
-
- Kernel-mode Driver Framework
-
- --*/
-
- #include "driver.h"
- #include "device.tmh"
-
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text (PAGE, KMDFDriverMVPCreateDevice)
- #endif
-
-
- NTSTATUS
- KMDFDriverMVPCreateDevice(
- _Inout_ PWDFDEVICE_INIT DeviceInit
- )
- /*++
-
- Routine Description:
-
- Worker routine called to create a device and its software resources.
-
- Arguments:
-
- DeviceInit - Pointer to an opaque init structure. Memory for this
- structure will be freed by the framework when the WdfDeviceCreate
- succeeds. So don't access the structure after that point.
-
- Return Value:
-
- NTSTATUS
-
- --*/
- {
- WDF_OBJECT_ATTRIBUTES deviceAttributes;
- PDEVICE_CONTEXT deviceContext;
- WDFDEVICE device;
- NTSTATUS status;
-
- PAGED_CODE();
-
- WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&deviceAttributes, DEVICE_CONTEXT);
-
- status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
-
- if (NT_SUCCESS(status)) {
- //
- // Get the device context and initialize it. WdfObjectGet_DEVICE_CONTEXT is an
- // inline function generated by WDF_DECLARE_CONTEXT_TYPE macro in the
- // device.h header file. This function will do the type checking and return
- // the device context. If you pass a wrong object handle
- // it will return NULL and assert if run under framework verifier mode.
- //
- deviceContext = WdfObjectGet_DEVICE_CONTEXT(device);
- deviceContext->PrivateDeviceData = 0;
-
- //
- // Create a device interface so that applications can find and talk
- // to us.
- //
- status = WdfDeviceCreateDeviceInterface(
- device,
- &GUID_DEVINTERFACE_KMDFDriverMVP,
- NULL // ReferenceString
- );
-
- if (NT_SUCCESS(status)) {
- //
- // Initialize the I/O Package and any Queues
- //
- status = KMDFDriverMVPQueueInitialize(device);
- }
- }
-
- return status;
- }
11.驱动描述代码 [cpp] view plaincopy
- /*++
-
- Module Name:
-
- driver.c
-
- Abstract:
-
- This file contains the driver entry points and callbacks.
-
- Environment:
-
- Kernel-mode Driver Framework
-
- --*/
-
- #include "driver.h"
- #include "driver.tmh"
-
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text (INIT, DriverEntry)
- #pragma alloc_text (PAGE, KMDFDriverMVPEvtDeviceAdd)
- #pragma alloc_text (PAGE, KMDFDriverMVPEvtDriverContextCleanup)
- #endif
-
-
- NTSTATUS
- DriverEntry(
- _In_ PDRIVER_OBJECT DriverObject,
- _In_ PUNICODE_STRING RegistryPath
- )
- /*++
-
- Routine Description:
- DriverEntry initializes the driver and is the first routine called by the
- system after the driver is loaded. DriverEntry specifies the other entry
- points in the function driver, such as EvtDevice and DriverUnload.
-
- Parameters Description:
-
- DriverObject - represents the instance of the function driver that is loaded
- into memory. DriverEntry must initialize members of DriverObject before it
- returns to the caller. DriverObject is allocated by the system before the
- driver is loaded, and it is released by the system after the system unloads
- the function driver from memory.
-
- RegistryPath - represents the driver specific path in the Registry.
- The function driver can use the path to store driver related data between
- reboots. The path does not store hardware instance specific data.
-
- Return Value:
-
- STATUS_SUCCESS if successful,
- STATUS_UNSUCCESSFUL otherwise.
-
- --*/
- {
- WDF_DRIVER_CONFIG config;
- NTSTATUS status;
- WDF_OBJECT_ATTRIBUTES attributes;
-
- //
- // Initialize WPP Tracing
- //
- WPP_INIT_TRACING( DriverObject, RegistryPath );
-
- TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
-
- //
- // Register a cleanup callback so that we can call WPP_CLEANUP when
- // the framework driver object is deleted during driver unload.
- //
- WDF_OBJECT_ATTRIBUTES_INIT(&attributes);
- attributes.EvtCleanupCallback = KMDFDriverMVPEvtDriverContextCleanup;
-
- WDF_DRIVER_CONFIG_INIT(&config,
- KMDFDriverMVPEvtDeviceAdd
- );
-
- status = WdfDriverCreate(DriverObject,
- RegistryPath,
- &attributes,
- &config,
- WDF_NO_HANDLE
- );
-
- if (!NT_SUCCESS(status)) {
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_DRIVER, "WdfDriverCreate failed %!STATUS!", status);
- WPP_CLEANUP(DriverObject);
- return status;
- }
-
- TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
-
- return status;
- }
-
- NTSTATUS
- KMDFDriverMVPEvtDeviceAdd(
- _In_ WDFDRIVER Driver,
- _Inout_ PWDFDEVICE_INIT DeviceInit
- )
- /*++
- Routine Description:
-
- EvtDeviceAdd is called by the framework in response to AddDevice
- call from the PnP manager. We create and initialize a device object to
- represent a new instance of the device.
-
- Arguments:
-
- Driver - Handle to a framework driver object created in DriverEntry
-
- DeviceInit - Pointer to a framework-allocated WDFDEVICE_INIT structure.
-
- Return Value:
-
- NTSTATUS
-
- --*/
- {
- NTSTATUS status;
-
- UNREFERENCED_PARAMETER(Driver);
-
- PAGED_CODE();
-
- TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
-
- status = KMDFDriverMVPCreateDevice(DeviceInit);
-
- TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Exit");
-
- return status;
- }
-
- VOID
- KMDFDriverMVPEvtDriverContextCleanup(
- _In_ WDFOBJECT DriverObject
- )
- /*++
- Routine Description:
-
- Free all the resources allocated in DriverEntry.
-
- Arguments:
-
- DriverObject - handle to a WDF Driver object.
-
- Return Value:
-
- VOID.
-
- --*/
- {
- UNREFERENCED_PARAMETER(DriverObject);
-
- PAGED_CODE ();
-
- TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");
-
- //
- // Stop WPP Tracing
- //
- WPP_CLEANUP( WdfDriverWdmGetDriverObject(DriverObject) );
-
- }
12.队列描述代码
[cpp] view plaincopy
- /*++
-
- Module Name:
-
- queue.c
-
- Abstract:
-
- This file contains the queue entry points and callbacks.
-
- Environment:
-
- Kernel-mode Driver Framework
-
- --*/
-
- #include "driver.h"
- #include "queue.tmh"
-
- #ifdef ALLOC_PRAGMA
- #pragma alloc_text (PAGE, KMDFDriverMVPQueueInitialize)
- #endif
-
- NTSTATUS
- KMDFDriverMVPQueueInitialize(
- _In_ WDFDEVICE Device
- )
- /*++
-
- Routine Description:
-
-
- The I/O dispatch callbacks for the frameworks device object
- are configured in this function.
-
- A single default I/O Queue is configured for parallel request
- processing, and a driver context memory allocation is created
- to hold our structure QUEUE_CONTEXT.
-
- Arguments:
-
- Device - Handle to a framework device object.
-
- Return Value:
-
- VOID
-
- --*/
- {
- WDFQUEUE queue;
- NTSTATUS status;
- WDF_IO_QUEUE_CONFIG queueConfig;
-
- PAGED_CODE();
-
- //
- // Configure a default queue so that requests that are not
- // configure-fowarded using WdfDeviceConfigureRequestDispatching to goto
- // other queues get dispatched here.
- //
- WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(
- &queueConfig,
- WdfIoQueueDispatchParallel
- );
-
- queueConfig.EvtIoDeviceControl = KMDFDriverMVPEvtIoDeviceControl;
-
- status = WdfIoQueueCreate(
- Device,
- &queueConfig,
- WDF_NO_OBJECT_ATTRIBUTES,
- &queue
- );
-
- if( !NT_SUCCESS(status) ) {
- TraceEvents(TRACE_LEVEL_ERROR, TRACE_QUEUE, "WdfIoQueueCreate failed %!STATUS!", status);
- return status;
- }
-
- return status;
- }
-
- VOID
- KMDFDriverMVPEvtIoDeviceControl(
- _In_ WDFQUEUE Queue,
- _In_ WDFREQUEST Request,
- _In_ size_t OutputBufferLength,
- _In_ size_t InputBufferLength,
- _In_ ULONG IoControlCode
- )
- /*++
-
- Routine Description:
-
- This event is invoked when the framework receives IRP_MJ_DEVICE_CONTROL request.
-
- Arguments:
-
- Queue - Handle to the framework queue object that is associated with the
- I/O request.
-
- Request - Handle to a framework request object.
-
- OutputBufferLength - Size of the output buffer in bytes
-
- InputBufferLength - Size of the input buffer in bytes
-
- IoControlCode - I/O control code.
-
- Return Value:
-
- VOID
-
- --*/
- {
- TraceEvents(TRACE_LEVEL_INFORMATION,
- TRACE_QUEUE,
- "!FUNC! Queue 0x%p, Request 0x%p OutputBufferLength %d InputBufferLength %d IoControlCode %d",
- Queue, Request, (int) OutputBufferLength, (int) InputBufferLength, IoControlCode);
-
- WdfRequestComplete(Request, STATUS_SUCCESS);
-
- return;
- }
|