读书频道 > 系统 > windows > Windows内核原理与实现
6.2.2 驱动程序对象和设备对象
2013-05-18 16:29:05     我来说两句 
收藏    我要投稿   

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

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

当I/O 管理器加载一个设备驱动程序时,它会创建一个驱动程序对象,该对象在对象管理器目录中的路径为:\Driver\< DriverName > 或\FileSystem\< DriverName > 。如果是文件系统类型的驱动程序,则该对象被放置在“\FileSystem ”目录下,否则放在“\Driver ”目录下。因此,驱动程序可粗略地分为文件系统驱动程序和非文件系统驱动程序,后面6.5.1节将全面地讨论驱动程序的分类。

与驱动程序相关的另一种对象是设备对象。每个设备对象代表了系统中的一个设备,包括逻辑设备和物理设备。正常情况下,有两种途径可创建设备对象:即插即用管理器在检测到设备时,通过调用驱动程序的AddDevice例程来创建设备对象;或者,非即插即用驱动程序在它们的初始化例程中创建设备对象。每个设备对象都必定有一个为它负责的驱动程序。对设备对象的各种操作实际上是由为它负责的驱动程序中的例程来完成的。一个驱动程序可以支持多个设备,所以,驱动程序对象中有一个链表记录了它所负责的所有设备对象。

在上一小节中我们已经看过了驱动程序对象的创建过程,现在来看设备对象的创建。在Windows 中,这是通过I/O 管理器的IoCreateDevice 函数来完成的,该函数原型如下:
NTSTATUS
IoCreateDevice(
    IN PDRIVER_OBJECT DriverObject,
    IN ULONG DeviceExtensionSize,
    IN PUNICODE_STRING DeviceName OPTIONAL,
    IN DEVICE_TYPE DeviceType,
    IN ULONG DeviceCharacteristics,
    IN BOOLEAN Exclusive,
    OUT PDEVICE_OBJECT *DeviceObject
    );

DriverObject参数指向负责该设备的驱动程序对象;DeviceExtensionSize 参数定义了待创建设备对象的扩展部分的大小,此扩展部分是由驱动程序来指定和使用的;DeviceName 可选参数指定了设备的名称。DeviceType 参数定义了设备的类型,DEVICE_TYPE 是无符号整数类型,Microsoft 已经预定义了所有常用的设备,它们的整数值小于32 767 ,若创建者需要使用自定义的设备类型,可以使用大于32 767 的值。关于这些预定义值,参见public\sdk\inc\devioctl.h 文件中的常量定义。eviceCharacteristics参数指定了设备的特征;Exclusive 参数指定了在创建设备对象时是否使用互斥标志;DeviceObject 参数是一个输出参数,用于存放所创建的设备对象。

IoCreateDevice 函数的代码位于 base\ntos\io\iomgr\iosubs.c 文件的 4 275~4 713 行。它首先根据参数中指定的要求,构造出设备的名称,并且创建一个安全描述符,用于对该设备
的访问控制。然后,调用 ObCreateObject函数创建一个 IoDeviceObjectType 类型的内核对象。如果由于自动产生的名称发生冲突而导致 ObCreateObject 调用不成功,则重试此过程。然后,IoCreateDevice 函数初始化新建的设备对象中的成员,并调用ObInsertObject函数,将设备对象插入到进程的句柄表中。最后,设定该设备对象中的驱动程序对象,并将设备对
象插入到驱动程序对象的设备链表中,因而将设备对象与驱动程序对象关联起来。

下面来看一下驱动程序对象和设备对象的定义。以下是驱动程序对象的数据结构:
typedef struct _DRIVER_OBJECT {
    CSHORT Type;
    CSHORT Size;
 
    PDEVICE_OBJECT DeviceObject;    // 指向设备对象,所有的设备对象构成一个链表
    ULONG Flags;     //  驱动程序标志
 
    PVOID DriverStart;      //  驱动程序映像起始地址
    ULONG DriverSize;     //  驱动程序映像大小
    PVOID DriverSection;     //  指向驱动程序映像的内存区对象
    PDRIVER_EXTENSION DriverExtension;  //  指向驱动程序对象的扩展部分
 
    UNICODE_STRING DriverName;    //  驱动程序名称
 
    PUNICODE_STRING HardwareDatabase;   //  指向注册表中包含硬件信息的路径
 
    PFAST_IO_DISPATCH FastIoDispatch;   //  指向快速I/O 的分发结构
 
    PDRIVER_INITIALIZE DriverInit;   //  驱动程序的初始化例程
    PDRIVER_STARTIO DriverStartIo;   //  驱动程序的启动 I/O 例程
    PDRIVER_UNLOAD DriverUnload;    //  驱动程序的卸载例程
    PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT; 
 
//  以下是扩展部分的数据结构 DRIVER_EXTENSION 的定义
typedef struct _DRIVER_EXTENSION {
    struct _DRIVER_OBJECT *DriverObject;   //  指向驱动程序
 
    // AddDevice是一个函数指针,当即插即用管理器检测到一个新的设备由该驱动程序负责时,
    //  调用该函数,以便通知该驱动程序
    PDRIVER_ADD_DEVICE AddDevice;
 
    ULONG Count;  //  记录了重新初始化调用的次数
    UNICODE_STRING ServiceKeyName;  //  驱动程序的服务名称
 
    PIO_CLIENT_EXTENSION ClientDriverExtension; // 指向驱动程序的客户扩展部分
    PFS_FILTER_CALLBACKS FsFilterCallbacks;  // 用于文件系统过滤驱动程序
} DRIVER_EXTENSION, *PDRIVER_EXTENSION;

驱动程序对象中的MajorFunction数组包含了一组例程,当I/O 管理器接收到一个I/O请求时,它将根据 I/O 请求中的有关信息,找到设备对象的驱动程序对象,并调用驱动程序中相应的例程来处理该I/O 请求。通常,设备驱动程序的初始化例程会填充MajorFunction数组中的例程。对于初始化例程未填充的数组项,创建驱动程序对象的函数(如IopLoadDriver和IoCreateDriver)会将其填充为 IopInvalidDeviceRequest 函数。

下面是设备对象的数据结构:
typedef struct _DEVICE_OBJECT {
    CSHORT Type;
    USHORT Size;
    LONG ReferenceCount;  //  引用计数值
    struct _DRIVER_OBJECT *DriverObject;   //  指向关联的驱动程序对象
    struct _DEVICE_OBJECT *NextDevice;       //  指向关联同一驱动程序对象的下一个设备对象
    struct _DEVICE_OBJECT *AttachedDevice;  //  附载的设备,与 AttachedTo域构成双链表关系
    struct _IRP *CurrentIrp;  //  当前正在处理的 I/O 请求包
    PIO_TIMER Timer;   //  设备对象的定时器
    ULONG Flags;  //  设备对象标志,以 DO_ 作为前缀的一组常量
    ULONG Characteristics;  //  设备的特征,以 FILE_ 作为前缀的一组常量
    PVPB Vpb;  // 指向设备的卷参数块(Volumn Parameter Block)
    PVOID DeviceExtension;  //  指向设备对象的扩展部分
    DEVICE_TYPE DeviceType;   //  设备类型
    CCHAR StackSize;   //  设备栈的大小
    union {
        LIST_ENTRY ListEntry;   //  用于文件系统的设备对象,形成一个链表
        WAIT_CONTEXT_BLOCK Wcb;   //  等待环境块,用于与控制器对象协作
    } Queue;        ULONG AlignmentRequirement;  //  缓冲区的对齐要求,其值等于对齐边界减一
    KDEVICE_QUEUE DeviceQueue;   //  设备队列,存放针对该设备的 I/O 请求
    KDPC Dpc;
 
    ULONG ActiveThreadCount;  //  用于文件系统:使用此设备对象的线程数
    PSECURITY_DESCRIPTOR SecurityDescriptor;  //  设备的安全描述符
    KEVENT DeviceLock;   //  设备锁
 
    USHORT SectorSize;   //  扇区大小
    USHORT Spare1;
 
    struct _DEVOBJ_EXTENSION  *DeviceObjectExtension;  // 指向扩展部分
    PVOID  Reserved;
} DEVICE_OBJECT;
typedef struct _DEVICE_OBJECT *PDEVICE_OBJECT; 
设备对象扩展部分的定义如下:
typedef struct _DEVOBJ_EXTENSION {
    CSHORT          Type;
    USHORT          Size;
 
    PDEVICE_OBJECT  DeviceObject;  // 指向关联的设备对象
    ULONG           PowerFlags;  //  电源标志
    struct          _DEVICE_OBJECT_POWER_EXTENSION  *Dope; //设备对象的电源扩展部分
    ULONG ExtensionFlags;   //  设备对象扩展标志
 
    PVOID           DeviceNode;  //  设备节点域,由即插即用管理器使用
    PDEVICE_OBJECT  AttachedTo;  //  当前设备对象被附载到此设备对象
 
    //  以下三个域用于IoStart* 函数
    LONG           StartIoCount;  // 已启动但未完成的 I/O 的数量
    LONG           StartIoKey;  // 下一个启动I/O 的键
    ULONG          StartIoFlags;  // 启动I/O 标志
    PVPB           Vpb;           // 已挂载卷的VPB ,用于文件系统的卷设备对象
} DEVOBJ_EXTENSION, *PDEVOBJ_EXTENSION;

设备对象描述了一个特定设备的状态信息,包括它所接收到的I/O 请求和设备的电源特性等。正如我们在IoCreateDevice 函数中看到的那样,设备对象的 DriverObject域指向负责该设备的驱动程序,而它的NextDevice 域构成了同属一个驱动程序的设备对象单链表。IoCreateDevice 调用IopInsertRemoveDevice 函数,将设备对象插入到此链表中。另一方面,设备对象的AttachedDevice 域和扩展部分的 AttachedTo 域构成了一个双链表节点中的前后指针。本章前面提到过,在 Windows 的层次驱动程序模型中,I/O 请求可以被传递给一个设备栈进行处理。设备栈中的设备对象相互链接起来,创建这些设备对象的驱动程序相互协作来处理针对特定设备的I/O 请求。因而,当 I/O 管理器接收到针对这一设备的I/O 请求时,它会依次将该 I/O 请求传递给这些设备对象,交给它们处理。设备对象的StackSize 域指定了为处理 I/O 请求而最小需要的栈深度,而AttachedTo 成员将设备栈中的设备对象自栈顶向底层(接近硬件设备的设备对象)链接起来,AttachedDevice成员则相反地自底向上将这些设备对象链接起来。

图6.5显示了在虚拟机环境中 Windows Server 2003 SP1系统的三个设备对象所构成的设备栈,我们可以看到设备对象中的AttachedDevice 和AttachedTo 成员所形成的链表结构。驱动程序ACPI 创建了最底层的设备对象“\Device\0000003b ”,它要求最小的栈深度为4,这是在 ACPI驱动程序中指定的。驱动程序 i8042prt 创建了中间的无名设备对象,它的AddDevice 例程调用IoAttachDeviceToDeviceStack 函数,完成此无名设备对象的挂载工作。进一步,驱动程序kbdclass 创建了最上边的设备对象“\Device\KeyboardClass0 ”,类似地,它的 AddDevice 例程调用 IoAttachDevice 函数,完成 KeyboardClass0 设备对象的挂载工作。IoAttachDeviceToDeviceStack 和IoAttachDevice 函数的代码位于base\ntos\io\iomgr\iosubs.c 文件中,真正实现挂载功能的函数是位于同一文件中的IopAttachDeviceToDeviceStackSafe 函数,其代码非常直截了当,只是将一个设备对象按照前面描述的结构关系挂载到另一个设备对象所在设备栈的顶部。


 

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

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