| NT环境下的设备驱动
一 .Windows NT的结构
Windows NT 的系统结构决定了NT下访问设备的特殊性。NT是建立在Mach和Vax思想上的一种客户/服务器模型操作系统,由一个特权执行体以及一系列被称为保护子系统的非特权服务器组成。整个操作系统被分为用户态模式和核心态模式。所谓特权,是指处理器的操作方式,大多数的处理器都有一种甚至若干种特权方式。在特权方式方式下,所有机器指令都可执行并且系统内存可存取。在非特权方式方式下,某些机器指令不能执行并且系统内存不可存取。在Windows NT下,核心态就是指处于特权处理器方式下,而用户态总处于非特权处理器方式。 核心态模式又被称为NT执行体,包括了系统服务和硬件描述层(HAL),它们运行于CPU的特权层Ring0(在驱动程序内部,在不同部分还分为不同权限层)。系统服务包含了一个操作系统的所有应有服务,文件系统,进程控制,内存管理,设备管理等等。NT中除了微内核外,另外还包含了一些独特的部分,如对象管理器,配置管理器,执行体支持,本地过程调用,安全监视器,所有这些都建立在HAL之上。IO管理器实现对设备的管理,包含了文件系统,中间介质和设备驱动。通过HAL,NT可以防止内核和NT执行体的其它部分受硬件平台不同的影响。另外NT还将网络管理器加入了核心态模式。用户态模式包括了一些保护子系统,如OS/2子系统,POSIX子系统,安全子系统,当然还有最常用的Win32子系统,NT都称之为服务器。它们运行于CPU的用户层Ring3,建立在服务器上的各类应用程序被称为客户。二 .NT中的设备驱动 在NT核心态包含了两种基本类型的驱动:用户模式驱动和核心态驱动。用户态驱动包含了Win32多媒体驱动,支持MS-DOS应用程序的VDD(虚拟设备驱动)和其它保护子系统的驱动.用户态驱动是一种特殊的子系统,详细的可以参见DDK中的多媒体驱动和虚拟DOS驱动文档。核心态驱动有针对逻辑的,虚拟的以及物理的设备.我们称之为NT驱动,它们为NT执行体的一部分,所谓的新技术(new technology)和基于微内核的操作系统,就是能支持一个或多个保护子系统,在这篇文章里,NT都只代表Windows NT执行体。NT包含了一些各自独立地定义了自己的功能的组件。对于设备驱动开发者来说,主要感兴趣的内核,I/O管理器,执行体支持,进程结构。另外可能还有对象管理器和安全监视器。对NT文件系统感兴趣的还包含Cache管理器。象NT一样,NT驱动由一些按照需求设计的离散的功能模块组成。所有NT驱动都有系统定义的标准驱动例程和一些由设计者决定的内在例程。 有三种类型的NT驱动,每种类型都有略微不同的结构和完全不同的功能。
Windows NT 网络驱动也可归类于这三种驱动类型的一种,例如,一个NT服务器或重定向器是一种特殊的文件系统,传输驱动是一种中间介质的NT驱动,而物理网卡(有时也称为介质存取控制器MAC)驱动是一个NT设备驱动,然而NT提供特殊的接口支持网络驱动,例如针对物理网卡的NDIS 3.0(Network Device Interface Specification)。NT驱动是基于下列目标设计的:可移植性,硬件和软件可配置性,总是抢占和可中断的,在多处理平台上的安全性,基于对象,可重用的IO请求包,支持异步传输。由于NT运行于核心模式,NT驱动只能使用系统提供的RtlXxx运行库,文件系统的驱动还可以使用FsRtlXxx运行库。在NT核心态不支持浮点运算。大部分NT元件都用C语言编写,只有一小部分HAL和核心基于效率的原因用汇编语言写成,NT驱动同样也是使用C写.这样可以在不同平台上保证原码级兼容。驱动开发者不能使用系统不支持的C库,所以最好使用ANSI C标准。NT驱动开发者必需避免使用定义取决于系统的数据类型。 至于可配置性,NT配置管理器提供了一个数据库,叫做注册库,它包含了硬件,外围设备和给定机器的驱动信息。NT驱动可以使用注册库信息获得硬件配置信息。还有也可以通过NT的HAL原件,这是一个动态连接库,它响应系统中所有硬件层,包括NT驱动。HAL隐藏了平台硬件的特殊性,如caches,I/O总线,中断,等等.NT设备驱动只要调用HAL提供的例程,就可以获得一些硬件信息。NT 的抢先多任务不仅表现在用户界面上,在设备驱动级也是抢占和可中断的.在系统中每一个线程都被赋予优先属性,大部分的优先属性都是可变的,除非是实时优先,这种优先只有自己放弃控制.无论优先属性如何,当硬件或某种软件中断产生时,系统中的一些线程将被抢占.核心态对于给定平台的指派中断都定义了中断请求层IRQL,NT核心区分硬件和软件中断优先,一些核心态代码运行于较高的IRQL,例如NT驱动.一般的,若不带中断向量,线程运行于PASSIVE_LEVEL_IRQL,软件中断被分配于相对较低的IRQL(APC_LEVEL,DISPATCH_LEVEL,或核心调试,WAKE_LEVEL).设备中断具有较高的IRQL值,核心为系统临界中断保留最高级的IRQL,例如系统时钟或总线中断。一些核心态例程运行于PASSIVE_LEVEL_IRQL,这是因为核心态组件能设定自己的线程,或按照分页代码使用。大部分NT驱动例程运行于DISPATCH_LEVEL,设备驱动运行于设备IRQL(也叫DIRQL)。 如果是在多处理平台上,必须保证以下条件:所有处理器都是平等的,如果有协处理器的话,协处理器也得平等。所有处理器共享内存,并且统一存取内存。如果是在对称平台,每个处理器都能存取内存,获取中断,获取I/O控制注册。在非对称平台,主处理器获得辅助处理器的所有中断。Windows NT是设计成不用修改就既可以在单一处理器也可以在对称多处理器上运行的,所以NT的驱动也必须是一样的。为了在对称多处理器上安全地运行,操作系统就必须保证运行在一个处理器上的代码不能同时修改或存取正被另一个处理器使用着的数据。例如,NT设备驱动的对应于一个处理器的ISR必须独占临界区,驱动定义数据,设备注册数据。NT核心提供了一个spin lock的机制。用做保护数据。Windows NT 是基于对象的系统,在执行体内部,不同的组成部分定义为一个或多个对象类型。每一个NT元素导出各自的核心态模式工作列程,通过操作各自的对象类型调用列程。NT驱动和它们的设备也是基于对象的。对于系统中的其它元素,包括用户态代码设备表现为I/O管理器中的一个文件对象。在I/O系统中,每一个驱动的逻辑的,虚拟的,或者物理的设备都表现为设备对象。在I/O管理器中,每一个NT驱动的载入影像都表现为驱动对象,I/O管理器为文件对象,设备对象,驱动对象定义对象类型,同样adapter对象对应为系统DMA控制器和总线DMA控制卡,controller对象对应为相应设备的物理控制器。控制操作格式: PrefixOperationObject 前缀 操作符 对象where 前缀 识别NT元素导出列程,定义对象类型。一般前缀为两个文字。 Operation 操作符描述对象的作用 Object 对象定义对象类型
例如,当NT驱动初始化时将调用IoCreateDevice一次或数次,为物理,逻辑或虚拟设备创建设备对象。 I/O管理器的主要工作是接收I/O请求(通常来自于用户模式的应用程序),创建I/O请求包,将IRP传递给合适的NT驱动。并且跟踪它们直到完成。并且为每个I/O操作的原始请求者返回状态。I/O管理器使用IRP和NT驱动通讯,并且允许NT驱动互相之间通讯。要注意的是一些IRP将传递给不止一个NT驱动。例如,在磁盘中打开文件这个请求将将手先传到文件系统驱动,经过中间介质的镜像驱动最终传到物理磁盘驱动。因此每一个IRP有一个固定的部分,还有一个或多个I/O的位置栈。在固定的部分,I/O管理器保持原始请求信息,例如调用者参数,关于哪一个文件打开的设备对象地址,等等。另外在固定的部分还包含I/O状态块,其中包含了请求操作的驱动信息。在高级驱动的I/O本地栈中,I/O管理器设置特殊的参数。 I/O管理器提供异步I/O,这样IRP的请求者能够继续执行,而不是等待IRP完成。NT驱动没有必要按照它们传递给I/O管理器的顺序处理I/O请求。I/O管理器或高级驱动在接收时可以重新排列I/O请求或将大数据的传输请求分离为小的传输请求。
NT保护子系统,例如WIN32子系统,通过I/O系统服务传递I/O请求到相应的核心态子系统。如上图所示。通过NT的I/O管理器提供的文件对象,子系统可以存取NT驱动设备或储存设备。在NT系统中,所有驱动对象以符号链接表示。
注意在设计NT驱动时必须牢记以下几点: NT驱动是分层的,不只一个的NT驱动可以处理单一的IRP.NT驱动使用IRP中的IO状态栈进行IO操作,处理请求成功还是失败的通讯.而IO管理器将这一结果传递给用户态请求者。NT驱动不需要也没必要设计成提供支持特殊应用.保护子系统或其特殊的子系统,用户态驱动已经提供了这些支持.但有一个例外:建立在专门应用设备上的MS-DOS应用程序可以请求NT驱动控制这一设备和相近的WIN32用户模式虚拟设备驱动.更多有关VDD的信息请参见DDK中的Virtual DOS Drivers文档.
上面的图表示了驱动是如何利用IO支持例程处理IRP读写请求.同时也详细表示了低级设备IRP的本地IO栈的细节.例如物理磁盘驱动。
NT设备使用以下基本请求: IRP_MJ_CREATE – 打开目标设备对象,指明使用的IO操作. IRP_MJ_READ – 从设备传输数据 IRP_MJ_WRITE – 传输数据到设备 IRP_MJ_DEVICE_CONTROL – 设置(或重设)设备,根据系统定义设备特殊的IO控制代码. IRP_MJ_CLOSE – 关闭目标设备对象. NT的IO管理器提供在系统已存在的驱动链中增加新驱动的支持.在驱动中设置线程将影响系统性能.NT驱动通常在驱动的设备对象部分(也称为设备扩展)保持其IO操作状态. 在设计NT驱动时牢记以下几点: 一个新的NT驱动必须处理一些系统支持的驱动中设置相同的IRP_MJ_XXX,如果驱动未在入口点定义IRP_MJ_XXX,否则IO管理器将对给定的针对目标设备的IO请求返回STATUS_INVALID_DEVICE_REQUEST。通过IRP_MJ_DEVICE_CONTROL设备驱动同样必须处理所需的IO控制代码。一个新的插入已存在的驱动链的NT中间驱动必须识别所替换的IRP_MJ_XXX。这些驱动只是简单地传递请求的IRP到下一个低级驱动,自己并不处理。一个低级的设备驱动只能存取其发送的IRP中自己的IO位置栈。一个高级驱动能存取自己和下一个低级驱动的IO本地栈。每一个NT驱动仅仅通过IRP的IO状态栈和高级驱动(通常是经过IO管理器的用户态应用)进行通讯。IO管理器在每一个驱动完成IRP后将IO本地栈清零。NT驱动的一部分使用IRP_MJ_INTERNAL_DEVICE_CONTROL定义了特殊的IO控制代码,用于将请求从高级部分传递给低级部分。
IO管理器定义驱动对象类型,并且使用驱动对象注册和跟踪载入的NT驱动镜像的信息。注意在驱动对象指定的入口点和主要的函数代码(IRP_MJ_XXX)保持通讯。 每一个IRP的IO管理器例程首先到达驱动指定的入口点,设备驱动的入口例程调用IoStartPacket将每个IRP排队,通过StartIo循环启动IO请求操作。高级NT驱动通常没有StartIo循环。 当NT驱动被载入时,DriverEntry被指向驱动的对象调用。DriverEntry在输入驱动对象中设置多个Dispatch入口点,IO管理器可以处理恰当的Dispatch循环。同时DriverEntry设置驱动的StartIo和Unload入口点。
三.核心态中的一些基本概念 1.I/O请求包(IRP) NT下所有的I/O都是包驱动的。 IRP是I/O管理器在响应一个I/O请求时从非分页系统内存中分配的一块可变大小的数据结构内存。 IRP包括两部分:IRP标头和I/O堆栈。 a.IRP标头包含I/O请求的各种信息,其中有些驱动可以直接访问,另一些是IO管理器特有属性。以下是允许驱动访问的部分: IO_STATUS_BLOCK IoStatus包含IO操作的最后状态,Status域被设置成STATUS_XXX,Information域设置成0或函数代码特定值 PVOID AssociatedIrp.SystemBuffer指向有缓冲IO驱动的系统缓冲区的指针 PMDL MdlAdress 指向直接IO设备的用户空间缓冲区描述符列表的指针 PVOID UserBuffer IO缓冲区的用户空间地址 BOOLEAN Cancel IRP是否被取消 b.IO堆栈主要存放IO请求的函数指针和参数: UCHAR MajorFunction 指定操作的IRP_MJ_XXX函数 UCHAR MinorFunction有文件系统和SCSI驱动程序 union Parameters MajorFunction代码的联合类型 struct Read IRP_MJ_READ的参数 ULONG Length ULONG Key LARGE_INTEGER ByteOffset struct Write IRP_MJ_WRITE的参数 ULONG Length ULONG Key LARGE_INTEGER ByteOffset struct DeviceControl IRP_DEVICE_CONTOL和IRP_MJ_INTERNAL_DEVICE_CONTOL的参数 ULONG OutoutBufferLength ULONG InputBufferLength ULONG IocontrolCode struct Others DEVICE_OBJECT DeviceObject此IO请求的目标设备 PFILE_OBJECT FileObject此IO请求的文件对象 对于低级驱动,对应的IRP只有一个IO堆栈,高级驱动可能有若干IO堆栈。 处理IRP的函数有如下一些: IoStartPacket发送IRP到Start IO例程,Dispatch调用 IoCompleteRequest指示所有处理已完成,DpcForIsr调用 IoStartNextPacket发送下一个IRP到Start IO例程,DpcForIsr调用 处理IRP堆栈的函数有如下一些: IoGetCurrentIrpStackLocation得到调用者堆栈单元的指针,Dispatch调用 IoMarkIrpPending标记调用者的堆栈单元需要进一步处理,Dispatch调用
2.中断请求级(IRQL) 为了将不同CPU体系中不同的处理硬件优先级方法统一起来,NT使用了抽象的CPU优先级方案。即中断请求级。 IRQL是一个数,定义了CPU当前活动的重要性。 HIGHEST_LEVEL机器检查和总线错误(硬件) POWER_LEVEL电源失效中断(硬件) IPI_LEVEL多处理器系统处理器之间的门铃(硬件) CLOCK2_LEVEL内部时钟2(硬件) CLOCK1_LEVEL 内部时钟1(硬件) PROFILE_LEVEL轮廓文件定时器(硬件) DIRQLs IO设备中断的平台相关的级数(硬件) DISPATCH_LEVEL线程调度器和延迟过程调用执行(软件) APC_LEVEL异步过程调用执行(软件) PASSIVE_LEVEL一般的线程执行级(软件) 当一个中断到达CPU时,处理器比较请求中断的IRQL和CPU当前的IRQL,如果等于或小于CPU当前IRQL,请求被堆栈挂起,直到CPU的IRQL低于请求时才进行。
DPC就是对将要在较低一级IRQL上运行的代码进行延迟调用,改善系统事件响应。 DPC使用软件中断,推迟对时间要求不高的代码的执行,直到较高的IRQL活动结束。 当运行于较高IRQL的一段代码要在较低的IRQL上继续工作时,将被加入DPC队列,请求DPC中断。而此DPC中断不会被马上执行,只有在CPU的IRQL低于原来IRQL时才进行。
当IO管理器寻找除DriverEntry例程以外的其它函数时,它使用于设备相关的Driver对象。这个对象是一个目录,含有指向各驱动函数的指针。 IO管理器在载入一个驱动时建立Driver对象,DriverEntry将指向各驱动函数的指针装入Driver对象。当IRP发送到特定设备时,IO管理器使用Driver对象找到正确的Dispatch例程。Driver对象还含有指向设备对象链表的指针。
5.设备对象DeviceObject和Device Extension 设备对象记录设备的特征和状态信息。驱动程序可访问以下域: PVOID DeviceExtension指向Device Extension结构的指针 PDRIVER_OBJECT DriverObject指向这一设备的Driver对象的指针 ULONG Flags指定这一设备的缓冲策略 DO_BUFFERED_IO(IO管理器在每个IO操作开始,从非分页池分配缓冲区,并将地址传递给驱动程序,在传输大于一页数据时会降低速度,并占用太大非分页池) DO_DIRECT_IO(提供对内存物理页的直接访问) PDEVICE_OBJECT NextDevice指向驱动中下一个设备 UCHAR StackSize IRP所需IO堆栈的最小数目 ULONG AlighmentRequirement缓冲区要求的内存对齐 处理设备对象的一些函数有: IoCreateDevice创建Device对象(DriverEntry) IoCreateSymbolicLink使Device对象Win32可见(DriverEntry) IoDeleteSymbolicLink从Win32名字空间删除Device对象(Unload) IoDeleteDevice从系统删除Device对象(Unload) Device Extension是IO管理器分配的Device对象的非分页池。必须将全局和静态变量放入Device Extension中。
保证使用相同控制寄存器管理多个物理设备的适配卡正常工作。保证某一时刻对设备的唯一占有。 Controller对象中唯一可见的域是PVOID ControllerExtension。
协调硬件对系统DMA的争夺。 Adapter对象对用户完全不可见。
Interrupt对象指定中断服务例程的位置。 Interrupt对象对用户完全不可见。 Interrupt对象的调用函数有: HalGetInterruptVector将于总线有关的中断向量转化为系统范围的值(DriverEntry) IoConnectInterrupt将ISR和系统中断向量联系起来(DriverEntry) KeySynchronizeExecution同步在不同IRQL运行的驱动程序例程 IoDisconnectInterrupt删除Interrupt对象(Unload)
9.同步中断请求 当在不同IRQL上的代码试图同时访问相同数据结构,将出现中断同步问题。可以用中断阻塞和DPC来处理此问题。 中断阻塞可以临时提高CPU的IRQL,完成处理后再将IRQL恢复。 使用DPC将避免对数据结构的修改冲突。
10.主要例程一览 DriverEntry:IO管理器装入驱动时调用,执行初始化,建立例程指针,宣告资源,使设备名对系统可见。 Reinitialize:除DriverEntry以外的初始化例程。 Unload:解除资源,删除驱动对象。 ShutDown:将硬件恢复到已知状态。 StartIO:当调用IoStartPacket时,将启动StartIO例程。 ISR:中断服务例程。 DPC:处理设备操作后的清理工作,将完成的IO请求送回IO管理器,开始下一个设备操作。 ControllerControl:在一个外设卡支持多个设备时使用。 AdapterControl:拥有DMA使用权时调用。 SynchCritSection:由于ISR在DIRQL,而其它部分在DISPATCH_LEVEL或较低级执行,这些低级IRQL代码只能通过SynchCritSection接触ISR的资源。 Timer:跟踪时间的驱动例程。另外还有CustomTimeDpc。 IoCompletion:低级驱动在完成高级驱动的请求时,以此例程调用形式通知高级驱动。 CancelIo:若驱动在很长时间将等待请求,必须将此例程挂接到请求,,当请求被取消时负责清理操作。
11.资源的检测 设备驱动必须知道要管理的设备的控制寄存器,DMA能力,中断的IRQL,以及所需特定的内存。 在NT启动时,引导组件NTDETECT(RISC上是硬件ARC)搜集系统硬件信息,将其写入到Registry的\HARDWARE\DESCRIPTION下。 对于ISA设备,自动检测不能获得信息,只能使用其它方法找到硬件。 对于PCI和EISA设备就要好的多,可以用IoQueryDeviceDecription函数在注册中查找,它对匹配的请求信息调用ConfigCallBack扫描硬件信息,在IoQueryDeviceConfigurationData下的CM_PARTIAL_RESOURCE_DECRIPTOR包含了硬件的实际信息。包括口地址,中断向量,DMA,内存和设备特定信息。 当将数据取出后,必须将它们转换成系统范围内的等价值。 HalTranslateBusAddress可以将内存和寄存器从总线相关值转换成系统范围内的等价值。 HalGetInterruptVector将总线相关的中断信息转换成系统分配的向量,DIRQL和相似掩码值。 HalGetAdapter找到对特定设备进行DMA操作的Adadpter对象。 若是没有自动检测信息的ISA设备,可以将信息在用户态放进注册表,供设备驱动查询。 其它可以得到硬件信息的函数有HalGetBusData,它允许对特定总线的特定槽位进行查询。但只对PCI和EISA总线有效。 如果以上都无法使用,只好通过探询控制寄存器来查找硬件。
12. 资源的申请和释放 在注册库的\HARDWARE\RESOURCEMAP下维护了所有当前硬件的数据。 在驱动的资源中有两个.Raw和.Translated的值,前者包含了设备资源总线特定信息,后者为资源转换后的系统范围值。 在申明资源前必须构造一个要分派的资源列表,这是一个CM_RESOURCE_LIST类型的结构,通过将其传递给IoReportResourceUsage,请求CM_RESOURCE_LIST中项目的拥有权,并检查任何冲突。 对于动态分配资源的设备,可以使用HalAssignSlotResources来获得资源信息。 资源的释放可以构造一空的资源列表,然后调用IoReportResourceUsage。
13.映射设备内存 若设备使用专用的内存地址范围,必须调用HalTranslationBusAddress将总线相关的物理地址转换成系统值,然后用MmMapIoSpace将设备内存映射到相同虚拟地址空间。 在卸出驱动时调用MmUnmapIoSpace。
14.IO操作,扩展IO操作和IOCTL 常用IO操作有如下一些: IRP_MJ_CREATE CreateFile IRP_MJ_CLEANUP CloseHandle IRP_MJ_CLOSE CloseHandle IRP_MJ_READ ReadFile IRP_MJ_WRITE WriteFile IRP_MJ_DEVICE_CONTROL DeviceIoControl IRP_MJ_INTERNAL_DEVICE_CONTROL没有Win32调用 IRP_MJ_QUERY_INFORMATION GetFileSize IRP_MJ_SET_INFORMATION SetFileSize IRP_MJ_FLUSH_BUFFER FlushFileBuffer FlushConsoleInputBuffer PurgeComm IRP_MJ_SHUTDOWN InitialSystemShutdown IO管理器不允许用户增加新的IRP功能,只提供了IRP_MJ_DEVICE_CONTROL和IRP_MJ_INTERNAL_DEVICE_CONTROL两个特定操作,前者既可以在用户态调用也可在其它驱动通过构造IRP调用,后者只在驱动中被调用。 IOCTL是一个特定结构,其包括四部分: DeviceType指定IoCreateDevice的fILE_DEVICE_XXX值0x0000到0x7fff为MS保留0x8000的0xffff可用 ControlCode驱动定义的IOCTL代码0x000的0x7ff为MS保留0x800到0xfff可用 TransferType METHOD_BUFFER使用非分页池缓冲区处理数据,将地址放在IRP的AssociatedIrp.SystemBuffer上 METHOD_IN_DIRECT输入使用直接IO,输出使用缓冲,将地址放在IRP的MdlAddress上(内存描述表) METHOD_OUT_DIRECT 输出使用直接IO,输入使用缓冲,同上 METHOD_NEITHER将用户空间地址放到IRP的UserBuffer RequiredAccess在CreateFile时必须请求的访问权 FILE_ANY_ACCESS FILE_READ_DATA FILE_WRITE_DATA FILE_READ_DATA|FILE_WRITE_DATA
15.同步例程 当一个低IRQL例程在使用共享资源时,可能到达一个中断,可以通过在一个SynchCritSection中放入接触这些共享资源的代码来解决这个同步问题。KeSynchCritSection可以将IRQL提升到Interrupt对象的DIRQL,获得该对象的Interrupt旋转锁,在SynchCritSection运行时保证代码不被Interrupt对象关联设备中断。例程结束后,KeSynchCritSection释放旋转锁,将IRQL降为其原来级别。
16.系统线程 系统线程是运行于内核模式中的线程,没有用户模式的上下文环境,不能访问用户地址空间。一般在以下情况下可以考虑使用线程: 设备速度很慢,并且不经常访问。 设备要花很长时间进行状态转换,并且驱动必须等待该转换的发生。 设备为了完成单个操作,必须进行多个状态的转换,并且必须轮询该转换一段时间。 需要执行只在PASSIVE_LEVEL_IRQL上的操作。 通常在DriverEntry中调用PsCreateSystemThread建立线程,在卸出时调用PsTeminateSystemThread。 线程的同步有时间同步和一般同步。
17.缓存IO和直接IO 缓存IO是IO管理器分配的一个非分页池,它足够大地存放调用者的输入输出数据。IO管理器将其地址放在IRP的AssoicatedIrp.SystemBuffer中。 当驱动申明直接IO时,IO管理器根据需要分配缓冲区,并将其锁定在物理内存中,为其创建内存描述表(MDL),将MDL的指针放在IRP的MdlAddress中。
18.IO空间寄存器和内存映射寄存器 IO空间寄存器是将设备寄存器映射到称为IO空间的一组地址。这些IO空间地址(即端口)不是CPU看到的内存空间的一部分,只能通过特定机器指令访问: READ_PORT_XXX WRITE_PORT_XXX READ_PORT_BUFFER_XXX WRITE_PORT_BUFFER_XXX 如果将设备寄存器映射到物理内存地址,就可以使用内存操作的装入和存储指令: READ_REGISTER_XXX WRITE_ REGISTER _XXX READ_ REGISTER _BUFFER_XXX WRITE_ REGISTER _BUFFER_XXX
四.PSPNT中的NT设备驱动 我们以杭州照排机为例子,讲述NT核心态的组成结构以及于用户态的作用关系。
分配核心态非分页内存,作为核心态内存链。 分配核心态非分页内存,作为核心态内存链。 中断工作在 DIRQLs线程调度器和延迟过程调用执行在 DISPATCH_LEVEL异步过程调用执行在 APC_LEVEL一般的线程工作在 PASSIVE_LEVEL将 IRP要求的服务例程指向各个函数。在这里有 IRP_MJ_CREATE,IRP_MJ_CLOSE,IRP_MJ_READ,IRP_MJ_WRITE以及IRP_MJ_DEVICE_CONTROL。IRP_MJ_DEVICE_CONTROL包含用户自定义的IO操作。
将用户层的数据转移到核心态数据链储存起来,并且检查此数据链是否有空的部分,若有就向用户态发送送数据事件。 当中断到来时,首先检查中断,然后将核心态数据发送到数据总线,即物理设备地址空间。并通知 DPC向用户态发送送数据事件。(不能在DIRQLs设置事件)最后清理中断源。处理清扫工作,关闭事件句柄,解除中断联系,释放核心态内存,释放资源,端口和内存映射,删除 Win32符号联接,删除设备。附注:在自定义的初始化设备例程中还要设置卡的中断源和一些初始化工作,如设置寄存器,清内存链,清事件等等。在 ISR中最后记住清中断。NT核心态驱动程序源代码 (9.54K) |