读书频道 > 系统 > windows > Windows内核原理与实现
6.2.1 驱动程序初始化
2013-05-18 16:22:29     我来说两句 
收藏    我要投稿   

本文所属图书 > Windows内核原理与实现

本书从操作系统原理的角度,详细解析了Windows如何实现现代操作系统的各个关键部件,包括进程、线程、物理内存和虚拟内存的管理,Windows中的同步和并发性支持,以及Windows的I/O模型。在介绍这些关键部件时,本...  立即去当当网订购

正如2.6.2节所介绍,I/O 系统的初始化是在内核的阶段1 初始化过程中完成的。阶段1 初始化的主函数是Phase1InitializationDiscard,它调用IoInitSystem 函数来初始化I/O 系统,而且oInitSystem 函数的执行过程占据系统引导进度条的 25%~75%,请参考base\ntos\init\initos.c 文件中Phase1InitializationDiscard函数的代码。

IoInitSystem 函数的代码位于base\ntos\io\iomgr\ioinit.c 文件的 140~860 行,它首先初始化I/O 系统中用到的各种全局数据结构,然后执行以下初始化工作:

•  调用IopCreateObjectTypes 函数,创建7 种类型对象:Adapter、DeviceHandler、Controller、Device 、Driver、IoCompletion 和File ,分别存放在相应的全局变量中,这些全局类型变量如表2.4所列。

•  调用IopCreateRootDirectories 函数,在对象管理器的根目录下创建3 个目录对象:\Driver 、\FileSystem 和\FileSystem\Filters 。

•  调用IopInitializePlugPlayServices 函数,执行即插即用管理器的阶段0 初始化。

•  调用PoInitDriverServices 函数,执行电源管理器的阶段0 初始化。

•  调用HalInitPnpDriver,执行HAL的即插即用总线驱动程序初始化。

•  调用WMIInitialize,执行WMI的阶段0 初始化。

•  再次调用IopInitializePlugPlayServices ,执行即插即用管理器的阶段1 初始化。

•  调用IopInitializeBootDrivers ,初始化“引导-启动”类型的驱动程序。

•  调用PpLastGoodDoBootProcessing,执行“最近一次的正确配置(LKG)”的处理。

•  调用PsLocateSystemDll ,初始化系统 DLL (ntdll.dll),并映射到 System 进程中。
•  调用IopInitializeSystemDrivers ,加载“系统-启动”类型的驱动程序,并对它们进行初始化。

•  调用IopCallDriverRein itializationRoutines ,处理需要重新初始化的驱动程序。

•  调用IopReassignSystemRoot ,将系统根目录(\SystemRoot)替换成NT 路径名,这是一个符号链接(symbolic link )名称。

•  调用IopProtectSystemPartition ,保护系统分区。

•  调用IoAssignDriveLetters ,为磁盘分区和CD-ROM驱动器分配DOS驱动器字母。

•  再次调用WMIInitialize,执行WMI的阶段1 初始化。

•  再次调用PoInitDriverServices,执行电源管理器的阶段1 初始化。

WRK并没有包含以上所有被调用的函数,但是,IoInitSystem 函数所做的初始化事项却是简单明了。在以上步骤中,“引导- 启动”的驱动程序和“系统- 启动”的驱动程序分别被加载和初始化。下面我们讨论驱动程序的启动类型以及它们的初始化过程。Windows 中的每一个驱动程序(和Windows 服务)在安装时都被指定了一个启动类型,存放在注册表中。启动类型是一个整数值,其定义如下:
#define SERVICE_BOOT_START  0x00000000
#define SERVICE_SYSTEM_START  0x00000001
#define SERVICE_AUTO_START  0x00000002
#define SERVICE_DEMAND_START  0x00000003
#define SERVICE_DISABLED    0x00000004

除了禁止启动的驱动程序,系统中安装的驱动程序有4 种可能的启动方式:“引导-启动”、“系统-启动”、“自动-启动”和“按需-启动”,其值分别对应于 0~3 ,如上述宏所定义。这些注册表值定义在HKLM\System\CurrentControlSet\Services\<DriverName >键的Start 值中,这里DriverName 代表了驱动程序的名称。

下面逐一介绍这4 种启动类型的驱动程序的加载和初始化过程。首先,“引导-启动”类型的驱动程序是由系统加载器,即ntldr 程序,加载到系统空间中的,因此,内核无须加载它们,直接初始化即可。如上面所介绍,这些驱动程序的初始化过程是在IopInitializeBootDrivers 函数中完成的。IopInitializeBootDrivers 为每个驱动程序调用IopInitializeBuiltinDriver 函数以完成实际的初始化工作。IopInitializeBuiltinDriver 函数的代码位于ioinit.c 文件的2 430~2 740行,其原型如下:
NTSTATUS
IopInitializeBuiltinDriver(
    IN PUNICODE_STRING DriverName,
    IN PUNICODE_STRING RegistryPath,
    IN PDRIVER_INITIALIZE DriverInitializeRoutine,
    IN PKLDR_DATA_TABLE_ENTRY DriverEntry,
    IN BOOLEAN IsFilter,
    OUT PDRIVER_OBJECT *Result
    );

这里DriverName参数指定了要初始化的驱动程序的名称,RegistryPath参数指定了该驱动程序对应的注册表键的路径,DriverInitializeRoutine参数指定了该驱动程序的初始化例程,DriverEntry 参数指定了该驱动程序在加载参数块中的相应表项(这是由 ntldr 准备好并传递给内核ntoskrnl.exe 模块的),IsFilter 参数指定了是否为过滤驱动程序(参见6.5.1节的介绍),Result 参数是一个返回参数,若初始化成功,则指向一个驱动程序对象。
 
IopInitializeBuiltinDriver 函数的基本流程如下:根据参数中指定的驱动程序名称,创建一个驱动程序对象,其类型是IoInitSystem 函数已经注册的IoDriverObjectType;然后初始化该驱动程序对象,设置它的DriverInit 域为参数中指定的初始化例程;将驱动程序对象插入到当前进程(即System 进程)的句柄表中;搜索系统的已加载模块列表(全局变量PsLoadedModuleList ),找到该驱动程序所在的模块项,并初始化驱动程序对象中与映像模块有关的域;将驱动程序的名称复制到驱动程序对象的名称缓冲区中,该缓冲区是从换页内存池中分配的;记录下驱动程序注册表键的名称;调用驱动程序的初始化函数,即DriverInitializeRoutine参数中指定的例程;最后,调用 IopReadyDeviceObjects 函数,将该驱动程序创建的设备对象设置成已被初始化,从而可被其他驱动程序或客户访问。

值得一提的是,所加载的驱动程序的初始化例程实际上是驱动程序二进制映像文件中的入口函数(通常该函数的名称为DriverEntry )。I/O 管理器在调用该例程时,会将驱动程序对象和注册表路径传递进去,因此,初始化例程可以访问驱动程序对象中的信息,尤其是其中的 HardwareDatabase 域,它包含了当前系统中关于硬件的描述。驱动程序在得到了它的注册表路径以后,可以利用此路径来保存有关的信息。如果驱动程序要保存该路径,则必须自己复制一份路径字符串,因为 IopInitializeBuiltinDriver函数传递给它的字符串是临时缓冲区。“引导-启动”的驱动程序不是过滤驱动程序,所以IsFilter 参数为 FALSE。

接下来我们来看“系统- 启动”类型的驱动程序的初始化,这是在IopInitializeSystemDrivers 函数中完成的。该函数通过CmGetSystemDriverList 函数(其代码位于base\ntos\config\cmsysini.c 文件中),获得在注册表中“Start ”值被指定为SERVICE_SYSTEM_START 的驱动程序的列表,然后对尚未加载到系统中的驱动程序逐个调用IopLoadDriver函数,将其加载到系统地址空间,并执行初始化。IopLoadDriver函数的原型如下:
NTSTATUS
IopLoadDriver(
    IN  HANDLE      KeyHandle,
    IN  BOOLEAN     CheckForSafeBoot,
    IN  BOOLEAN     IsFilter,
    OUT NTSTATUS   *DriverEntryStatus
    );

IopLoadDriver函数的代码位于base\ntos\io\iomgr\internal.c 文件的3 688~4 380行。其中KeyHandle 参数指向一个驱动程序的注册表键;CheckForSafeBoot 参数指示是否要检查安全模式,如果该参数为 TRUE ,那么,当系统以安全模式引导时,只有该驱动程序属于安全模式允许的驱动程序列表,它才被加载。IsFilter 参数指定了是否为过滤驱动程序;DriverEntryStatus是返回参数。

IopLoadDriver是一个通用函数,它不仅用于引导时加载“系统-启动”类型的驱动程序,而且也被用于系统正常运行过程中动态地加载驱动程序。本节后面我们还会看到该函数被调用的情形。当IopInitializeSystemDrivers 调用该函数时,CheckForSafeBoot 参数为TRUE ,而IsFilter 参数为FALSE。

IopLoadDriver 函数的流程大体如下:首先根据参数中指定的注册表键,构造出驱动程序的全路径名;然后,调用 MmLoadSystemImage函数,将驱动程序加载到系统地址空间。后面的步骤与 IopInitializeBuiltinDriver 相仿:创建一个驱动程序对象,初始化该对象的域,尤其是,它的DriverInit 成员指向驱动程序的入口函数;然后将该对象插入到当前进程的句柄表中。完成了对象成员初始化以后,再调用驱动程序的初始化函数,即DriverInit成员,并将驱动程序对象和注册表路径传递进去。

然而,IopLoadDriver 函数实际的代码比以上这些步骤要复杂一些。它需要检查安全模式引导选项,还需要检查此驱动程序是否已经在系统的已加载模块列表中。它还需要考虑该驱动程序已经被初始化了的情形,所以要试着打开该驱动程序。另外,它也会考虑,如果该驱动程序并非是老式的驱动程序(Legacy Driver )但是又没有增加任何设备对象,则认为该驱动程序的初始化不成功,于是调用它的Unload 函数将其立即卸载。

有一点值得一提,IopLoadDriver和IopInitializeBuiltinDriver 函数都必须在System 进程中运行,所以,新创建的驱动程序对象将被加入到该进程的句柄表中。这对于“引导-启动”和“系统- 启动”类型的驱动程序不成问题,因为它们的上游调用者IopInitializeBootDrivers 和IopInitializeSystemDrivers 都是在IoInitSystem 函数中被调用的。

然而,对于“自动-启动”类型的驱动程序,情形就不同了。

“自动-启动”类型的驱动程序,即注册表中“Start ”值为 SERVICE_AUTO_START (2)的驱动程序,是在系统引导过程的后期,由 SCM(服务控制管理器,Service ControlManager,services.exe 进程)加载的,参见 2.6.3节。在内核中,这是通过 NtLoadDriver函数来完成的。NtLoadDriver 是一个系统服务,其原型如下:
NTSTATUS
NtLoadDriver (
    __in PUNICODE_STRING DriverServiceName
    );

参数DriverServiceName指定了待加载的驱动程序在注册表中的名称。NtLoadDriver的功能并不复杂,其代码位于 base\ntos\io\iomgr\loadunld.c 文件的26~172行。如果用户模式代码调用NtLoadDriver,那么,它需要检查当前进程是否有加载驱动程序的特权。然后,若当前进程是System 进程,则直接调用 IopLoadUnloadDriver 函数完成驱动程序初始化工作;否则,将 IopLoadUnloadDriver 放到一个工作项目(WorkItem)中,由 System 进程中的系统线程来调用IopLoadUnloadDriver 函数。

现在来看IopLoadUnloadDriver 函数,它位于base\ntos\io\iomgr\internal.c 文件的 4 583~ 4 681 行。该函数既可以用于加载驱动程序,也可以用于卸载驱动程序,取决于参数(LOAD_PACKET数据结构)中指定的DriverObject是否已被赋了非零值。它直接调用IopLoadDriver函数来完成驱动程序的加载和初始化,这里不再赘述。

最后是“按需- 启动”类型的驱动程序,这是在系统运行过程中,有些用户程序或者系统模块根据需要而加载的驱动程序。譬如前面三章中介绍的工具软件在第一次运行时,需要加载一个驱动程序,以便完成某些必需在内核中完成的工作。这些工具软件通过SCM来加载和初始化驱动程序。在这种情况下,驱动程序的加载经由NtLoadDriver 系统服务来完成。此外,内核中的模块也可以根据需要加载并初始化“按需- 启动”类型的驱动程序,譬如,在前面三类驱动程序的初始化过程中就存在这样的需求。在这种情况下,内核代码要么直接调用IopLoadDriver 函数来完成驱动程序的加载,要么通过一个工作项目由系统辅助线程来完成加载工作。

通过上面的介绍,我们看到,这 4 类驱动程序都有一个映像文件,而且,驱动程序的初始化例程直接对应于映像文件的入口函数。实际上,Windows 也允许创建无映像文件的驱动程序对象。譬如,Windows 创建的第一个驱动程序对象“\Driver\PnpManager ”正是这样一个驱动程序。这一类驱动程序是通过IoCreateDriver 函数来创建的,该函数位于base\ntos\io\iomgr\iosubs.c 文件中,以下是其函数原型:
NTSTATUS
IoCreateDriver(
    IN PUNICODE_STRING DriverName    OPTIONAL,
    IN PDRIVER_INITIALIZE InitializationFunction
    );

该函数非常直截了当,可选参数 DriverName 指定了驱动程序的名称,InitializationFunction 参数指向一个初始化函数。IoCreateDriver 函数首先构造出驱动程序的名称,如果 DriverName没指定名称,则 IoCreateDriver函数利用当前时间信息生成一个驱动程序名称。然后,调用ObCreateObject 函数创建一个驱动程序对象,并初始化该对象,再调用ObInsertObject函数,将该对象插入到当前进程的句柄表中。最后,调用参数InitializationFunction 指定的初始化函数,并将驱动程序对象传递给它。

至此,我们已经了解了驱动程序的加载和初始化过程。一旦一个驱动程序被加载并且其初始化例程被执行,则该驱动程序就已经融入内核:它的代码已进入到系统地址空间,成为内核模式下可执行代码的一部分;它已经获得了初始的执行权,以后如何再次获得执行权,取决于初始例程中的代码逻辑。本章6.5.3节将介绍驱动程序的代码结构,我们将可以看到驱动程序中的例程如何获得控制。

在对象管理器的名字空间中,驱动程序对象位于\FileSystem 和\Driver 目录下,表 6.1显示了在Virtual PC环境中一个典型的Windows Server 2003 SP1 系统的“\Driver ”目录中的驱动程序对象,表6.2 显示了同一系统的“\FileSystem ”目录中的驱动程序对象。通过这两张表,我们可以看到一个典型的Windows 系统包含哪些驱动程序。不难看出,Windows中涉及I/O 的绝大多数功能都是通过驱动程序来完成的。
表6.1   Virtual PC 环境下Windows Server 2003 SP1 系统的“\Driver ”目录中的驱动程序
驱动程序  说明  驱动程序  说明
1-driver-
vmsrvc
虚拟机附加服务驱动程序  NdisTapi  NDIS TAPI 驱动程序
ACPI  ACPI驱动程序  Ndisuio  NDIS用户模式 I/O 驱动程序
ACPI_HAL  HAL中有关ACPI的虚拟驱动 NdisWan  NDIS WAN 驱动程序
AFD  WinSock 辅助功能驱动程序  NDProxy  NDIS代理
Atapi  标准的IDE/ESDI 硬盘控制器  NetBT
基于TCP/IP 的NetBIOS 驱
动程序
audstub  声音存根驱动程序  Null  NULL 驱动程序
Beep  BEEP 驱动程序  Parport  并口驱动程序
Cdrom  CD-ROM驱动程序  PartMgr  磁盘分区管理器
crcdisk  磁盘块检验驱动程序  Parvdm  VDM并口驱动程序
DC21x4  DC21x4网络适配器驱动程序  PCI  PCI 总线驱动程序
Disk  即插即用磁盘驱动程序  PnpManager  即插即用管理器
Dmio  NT磁盘管理器 I/O 驱动程序  PptpMiniport 对等网络隧道协议
dmload  NT磁盘管理器启动驱动程序  Ptilink
直接并行链接驱动程序
(Direct Parallel Link Driver)
Fdc  软盘控制器驱动程序  RasAcd  RAS 自动连接驱动程序
Fips  FIPS 驱动程序  Rasl2tp  RAS L2TP 小端口驱动程序
Flpydisk  软盘驱动程序  RasPppoe  RAS PPPoE 小端口驱动程序
Ftdisk  简单卷管理驱动程序  Raspti  PTI 直接并行小端口驱动程序
gameenum  游戏端口列举器(Enumerator )RDPCDD  RDP 小端口驱动程序
续表
驱动程序  说明  驱动程序  说明
Gpc  通用包分类器  rdpdr  RDP 设备重定向器驱动程序
i8042prt  i8042 端口驱动程序  serenum  串口列举器驱动程序
IntelIde  Intel PCI IDE 驱动程序  Serial  串口驱动程序
IPSec  IPSec 驱动程序  swenum  即插即用软件设备列举器
isapnp  PNP ISA 总线驱动程序  Tcpip  TCP/IP 协议驱动程序
Kbdclass  键盘类驱动程序  TermDD  终端服务驱动程序
KSecDD  内核安全支持提供者接口  Update  Microsoft 更新驱动程序
mnmdd  帧缓冲区模拟器  VgaSave  VGA视频驱动程序
Mouclass  鼠标类驱动程序  VolSnap  卷影像拷贝驱动程序
MountMgr  挂载点管理器  vpc-s3  虚拟机S3小端口驱动程序
mssmbios  系统管理BIOS驱动程序  Wanarp  远程访问路由ARP 驱动程序
msvmmouf  虚拟机附加的鼠标过滤驱动  Win32k  Win32 子系统驱动程序
NDIS  NDIS系统驱动程序  WMIxWDM WMI驱动程序
表6.2  Virtual PC 环境下 Windows Server 2003 SP1系统的“\FileSystem”目录中的驱动程序
驱动程序  说明  驱动程序  说明
Cdfs  CD-ROM文件系统驱动程序  Mup  多UNC提供者驱动程序
DfsDriver  分布式文件系统过滤驱动程序  NetBIOS  NetBIOS 接口驱动程序
FltMgr  文件系统过滤管理器  Npfs
命名管道(named pipe )驱动
程序
Fs_Rec  文件系统识别器驱动程序  Ntfs  NTFS驱动程序
MRxSmb  NT SMB 驱动程序  RAW
内置的RAW 文件系统驱动
程序
MrxVPC  虚拟机文件夹共享驱动程序  Rdbss
重定向的驱动器缓冲子系统
驱动程序
Msfs  邮件槽(mailslot)驱动程序  Srv  服务器驱动程序

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:6.2 I/O 管理器
下一篇:6.2.2 驱动程序对象和设备对象
相关文章
图文推荐
3.4.4 进程生命期管
3.4.2 Windows应用商
3.4.1 Windows应用商
3.4 进程生命期管理
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站