Windows驱动开发技术详解第四章(驱动程序的基本结构)

重要概念

  1. 每个驱动程序都会有一个或多个设备对象
  2. 驱动程序中的每个设备对象都有一个指针指向下一个设备对象(也属于当前驱动),最后一个设备对象指向空
  3. 驱动程序中尽量不要使用全局变量,而是将全局变量定义在设备扩展里
  4. 设备对象记录通用设备的信息,另外一些特殊信息记录在设备扩展(由程序员设计)里
  5. 驱动程序中,字符串都使用UNICODE编码(16位为一个字符)
  6. 可以使用NT_SUCCESS宏来判断NTSTATUS是否为成功
    typedef  struct _UNICODE_STRING {
    	USHORT Length;			//该字符串共占用多少字节(非字符数)
    	USHORT MaximumLength;	//Buffer的大小,最大能记录的字符数,大于等于Length
    	PWSTR Buffer;			//字符串的指针
    } UNICODE_STRING, *PUNICODE_STRING;
    
  7. 在Windows下所有设备的命名方式为:\Device\[设备名]
    如C盘:\Device\Harddisk Volume1,D盘:\Device\Harddisk Volume2
  8. 用户模式下,想要识别设备,①通过设备的链接符号②通过设备接口
  9. 设备的符号链接是设备对象的一个别名,如符号链接C:,对应于设备名称\Device\Harddisk Volume1
  10. 在系统中,有很多总线,不同设备的PDO由不同总线驱动所创建,虚拟设备的PDO由\Driver\PnpManger所创建(即插即用管理器)

重要数据结构

1. 驱动对象(DRIVER_OBJECT)

驱动对象作为驱动的一个实例,内核对一个驱动只加载一次实例

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;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO    DriverStartIo;
  PDRIVER_UNLOAD     DriverUnload;
  PDRIVER_DISPATCH   MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
} DRIVER_OBJECT, *PDRIVER_OBJECT;

在这里插入图片描述

DeviceObject:指向当前驱动程序中包含的第一个设备对象
DriverName:驱动程序的名字,UNICODE,一般为\Driver\[驱动程序名称]
HardwareDatabase:记录设备的硬件数据库键名,UNICODE,一般为\REGISTAY\MACHINE\HARDWARE\DESCRIPTION\SYSTEM
DriverStarIo:记录StarIO例程的函数地址,用于串行化操作
DriverUnload:记录驱动卸载时所用的回调函数地址
MajorFunction:一个数组,数组中的每个成员是一个指针,指向一个处理IRP的派遣函数
FastIoDispatch:文件驱动用的派遣函数

2. 设备对象(DEVICE_OBJECT)
typedef struct _DEVICE_OBJECT {
  CSHORT                   Type;
  USHORT                   Size;
  LONG                     ReferenceCount;
  struct _DRIVER_OBJECT    *DriverObject;
  struct _DEVICE_OBJECT    *NextDevice;
  struct _DEVICE_OBJECT    *AttachedDevice;
  struct _IRP              *CurrentIrp;
  PIO_TIMER                Timer;
  ULONG                    Flags;
  ULONG                    Characteristics;
  __volatile PVPB          Vpb;
  PVOID                    DeviceExtension;
  DEVICE_TYPE              DeviceType;
  CCHAR                    StackSize;
  union {
    LIST_ENTRY         ListEntry;
    WAIT_CONTEXT_BLOCK Wcb;
  } Queue;
  ULONG                    AlignmentRequirement;
  KDEVICE_QUEUE            DeviceQueue;
  KDPC                     Dpc;
  ULONG                    ActiveThreadCount;
  PSECURITY_DESCRIPTOR     SecurityDescriptor;
  KEVENT                   DeviceLock;
  USHORT                   SectorSize;
  USHORT                   Spare1;
  struct _DEVOBJ_EXTENSION *DeviceObjectExtension;
  PVOID                    Reserved;
} DEVICE_OBJECT, *PDEVICE_OBJECT;

设备对象结构

DriverObject:指向驱动程序中的驱动对象
NextDevice:指向下一个设备对象(当前驱动中的)
AttachDevice:指向更高一层附加在此驱动上的驱动
CurrentIrp:在使用StarIO例程时,指向的是当前的IRP结构
Flags:一个32位无符号整型,每一位的含义为

标志描述
DO_BUFFERED_IO读写操作使用缓冲方式(系统复制缓冲区)访问用户模式数据
DO_EXCLUSIVE一次只允许一个线程打开设备句柄
DO_DIRECT_IO读写操作使用直接方式(内存描述符表)访问用户模式数据
DO_DEVICE_INITIALIZING设备对象正在初始化
DO_POWER_PAGABLE必须在PASSIVE_LEVEL级上处理IRP_MJ_PNP请求
DO_POWER_INRUSH设备上电期间需要大电流
DeviceExtension:指向设备的扩展对象,由程序员自己定义的结构体(由IO管理器创建,保存在非分页内存中)通常包含:①设备对象的反向指针②设备状态或驱动环境信息③中断对象指针④控制器对象指针
DeviceType:设备的类型,常用类型为(虚拟设备选择FILE_DEVICE_UNKNOW):
设备类型描述
#define FILE_DEVICE_BEEP蜂鸣器设备
#define FILE_DEVICE_CD_ROMCD光驱设备
#define FILE_DEVICE_CD_ROM_FILE_SYSTEM光驱文件系统设备
#define FILE_DEVICE_CONTROLLER控制器设备
#define FILE_DEVICE_DATALINK数据链设备
#define FILE_DEVICE_DFSDFS设备
#define FILE_DEVICE_DISK磁盘设备
#define FILE_DEVICE_DISK_FILE_SYSTEM磁盘文件系统设备
#define FILE_DEVICE_FILE_SYSTEM文件系统设备
#define FILE_DEVICE_INPORT_PORT输入端口设备
#define FILE_DEVICE_KEYBOARD键盘设备
#define FILE_DEVICE_MAILSLOT邮槽设备
#define FILE_DEVICE_MIDI_INMIDI输入设备
#define FILE_DEVICE_MIDI_OUTMIDI输出设备
#define FILE_DEVICE_MOUSE鼠标设备
#define FILE_DEVICE_MULTI_UNC_PROVIDER多UNC设备
#define FILE_DEVICE_NAMED_PIPE命名管道设备
#define FILE_DEVICE_NETWORK网络设备
#define FILE_DEVICE_NETWORK_BROWSER网络浏览器设备
#define FILE_DEVICE_NETWORK_FILE_SYSTEM网络文件系统设备
#define FILE_DEVICE_NULL空设备
#define FILE_DEVICE_PARALLEL_PORT并口设备
#define FILE_DEVICE_PHYSICAL_NETCARD物理网卡设备
#define FILE_DEVICE_PRINTER打印机设备
#define FILE_DEVICE_SCANNER扫描仪设备
#define FILE_DEVICE_SERIAL_MOUSE_PORT串口鼠标设备
#define FILE_DEVICE_SERIAL_PORT串口设备
#define FILE_DEVICE_SCREEN屏幕设备
#define FILE_DEVICE_SOUND声音设备
#define FILE_DEVICE_STREAMS流设备
#define FILE_DEVICE_TAPE磁带设备
#define FILE_DEVICE_TAPE_FILE_SYSTEM磁带文件系统设备
#define FILE_DEVICE_TRANSPORT传输设备
#define FILE_DEVICE_UNKNOWN未知设备
#define FILE_DEVICE_VIDEO视频设备
#define FILE_DEVICE_VIRTUAL_DISK虚拟磁盘设备
#define FILE_DEVICE_WAVE_IN声音输入设备
#define FILE_DEVICE_WAVE_OUT声音输出设备
#define FILE_DEVICE_8042_PORT8402端口设备
#define FILE_DEVICE_NETWORK_REDIRECTOR网卡设备
#define FILE_DEVICE_BATTERY电池设备
#define FILE_DEVICE_BUS_EXTENDER总线扩展设备
#define FILE_DEVICE_MODEM调制解调器设备
#define FILE_DEVICE_VDMVDM设备
#define FILE_DEVICE_MASS_STORAGE大容量存储设备
#define FILE_DEVICE_SMBSMB设备
#define FILE_DEVICE_KS内核流设备
#define FILE_DEVICE_CHANGER充电设备
#define FILE_DEVICE_SMARTCARD智能卡设备
#define FILE_DEVICE_ACPIACPI设备
#define FILE_DEVICE_DVDDVD设备
#define FILE_DEVICE_FULLSCREEN_VIDEO全屏视频设备
#define FILE_DEVICE_DFS_FILE_SYSTEMDFS文件系统设备
#define FILE_DEVICE_DFS_VOLUMEDFS卷设备
StackSize:多层驱动情况下的层数
AlignmentRequirement:设备大容量传输时,需要内存对齐,保证传输速度

1. NT式驱动的基本结构

NT驱动主要包括:DriverEntry例程,卸载例程,各IRP派遣例程

NT驱动的DriverEntry介绍
NTSTATUS DriverEntry(
  _In_ PDRIVER_OBJECT  DriverObject,
  _In_ PUNICODE_STRING RegistryPath
);

DriverEntry由系统进程调用(任务管理器中显示的System进程)

  1. 驱动加载时,系统进程启动新线程,调用执行体组件中的对象管理器,创建一个驱动对象(一个DRIVER_OBJECT结构体)
  2. 另外系统进程调用执行体组件中的配置管理程序,查询此驱动程序对应注册表中的项
  3. 新创建的系统线程调用驱动程序的DriverEntry例程,传入两个参数,pDriverObject(指向刚才创建的驱动对象)和pRegistryPath(指向设备服务键的键名字符串,一般为\REGISTRY\MACHINE\SYSTEM\ControlSet\Services\[服务名]
  4. DriverEntry中,主要执行对传递进来的pDriverObject参数进行初始化,保存pReistryPath(因为这是一个临时的数据,可能DriverEntry结束就会被销毁掉)
  5. DriverEntry返回值为NTSTATUS类型(一个32位无符号长整型),其中0~0x7FFFFFFF约定为正确的状态,0x80000000~0xFFFFFFFF约定为错误的状态
  6. DriverEntry如果返回成功意味着驱动加载成功,否则,驱动加载失败,调用对象管程序销毁驱动对象
创建设备对象(IoCreateDevice)
NTSTATUS IoCreateDevice(
  [in]           PDRIVER_OBJECT  DriverObject,
  [in]           ULONG           DeviceExtensionSize,
  [in, optional] PUNICODE_STRING DeviceName,
  [in]           DEVICE_TYPE     DeviceType,
  [in]           ULONG           DeviceCharacteristics,
  [in]           BOOLEAN         Exclusive,
  [out]          PDEVICE_OBJECT  *DeviceObject
);

DriverObject:指向驱动对象的指针(一个驱动文件对应一个驱动对象)
DriverExtensionSize:指定设备扩展的大小,IO管理器根据这个大小,创建设备扩展与驱动关联
DeviceName:要创建的设备对象的名称,字符串必须是\Device\[设备名]的形式,如果不指定名称,IO管理器将自动分配一个数字作为设备名称,如\Device\00000001
DeviceCharacteristics:设备对象的特征
Exclusive:设置设备对象是否在内核模式下使用,一般为TRUE
DeviceObject:输出参数,IO管理器创建这个设备,返回设备的地址

创建设备的符号链接(IoCreateSymbolicLink)
NTSTATUS IoCreateSymbolicLink(
  [in] PUNICODE_STRING SymbolicLinkName,
  [in] PUNICODE_STRING DeviceName
);

SymbolicLinkName:要创建的符号链接的字符串
DeviceName:设备对象名的字符串
在内核模式下,符号链接以\??\或者\DosDevices\开头,如C盘在内核模式下符号链接为\??\C:或者\DosDevices\C:
在用户模式下,符号链接以\\.\开头,如C盘在用户模式下符号链接为\\.\C:

驱动的卸载(DriverUnload)

驱动的卸载例程一般包括删除在DriverEntry中创建的设备对象,并将与之关联的符号链接删除,回收一些资源

void IoDeleteDevice(
  [in] PDEVICE_OBJECT DeviceObject
);

NTSTATUS IoDeleteSymbolicLink(
  [in] PUNICODE_STRING SymbolicLinkName
);

DeviceObject:要删除的设备对象指针
SymbolicLinkName:要删除的符号链接

2. WDM驱动的基本机构

WDM模型是建立在NT模型之上的
WDM模型中完成一个设备的操作至少需要两个驱动:
1. 物理设备对象(PDO,上层驱动/高层驱动)
2. 功能设备对象(FDO,下层驱动/底层驱动)
在这里插入图片描述

当PC插入某个设备时,PDO会由总线驱动自动创建,PDO需要配合FDO一起使用
当PC插入某个设备时,系统提示检测到新设备,并要求安装驱动程序(WDM驱动,负责创建FDO并附加到PDO之上)
当一个FDO附加在PDO之上时,PDO设备对象的子域AttachedDevice会记录FDO的位置
在这里插入图片描述
FDO上层的过滤驱动称作:上层过滤驱动
FDO下层的过滤驱动乘坐:下层过滤驱动
过滤驱动可以嵌套,不是必须存在的,但PDO和FDO是必须的
每个设备对象中的StackSize子域,表面该设备对象需要几层才能到达最下面的物理设备
NT设备插入PC时,系统不会有提示,需要用户自行安装驱动
WDM设备插入PC时,系统自动创建PDO,请求用户安装FDO(如果已经有了FDO则会自动安装,如鼠标键盘等)

WDM驱动的DriverEntry介绍

函数原型同NT一样
WDM和NT驱动的DriverEntry不同点:

  1. WDM模型增加了AddDevice函数的设备(最主要不同点),NT模型是主动加载设备驱动的,所以直接在DriverEntry中创建了设备;而WDM模型由于是被动的,不会直接创建设备,而是等到设备插入PC,总线驱动创建PDO后,才会调用WDM驱动的AddDevice函数创建设备FDO,并附加在PDO上
  2. WDM模型必须加入IRP_MJ_PNP的派遣回调函数,负责计算机中的即插即用处理
WDM驱动的AddDevice例程

AddDevice由WDM模型独有,NT模型没有
设置方法:驱动对象中的DriverExtension子域中的AddDevice子域指向回调函数地址

pDriverObject->DriverExtension->AddDevice = HelloWDMAddDevice;

函数原型:

NTSTATUS HelloWDMAddDevice(
	IN PDRIVER_OBJECT DriverObject,
	IN PDEVICE_OBJECT PhysicalDeviceObject
)

DriverObject:IO管理器创建的驱动对象
PhysicalDeviceObject:底层总线驱动创建的PDO设备对象

AddDevice中需要执行的步骤:
① 通过IoCreateDevice等函数创建设备对象(即FDO),地址保存在设备扩展中
③ 通过IoAttachDeviceToDeviceStack将创建的FDO附加在PDO上(虽然指定了附加在PDO上,但是如果有过滤驱动的存在,则实际上附加在了过滤驱动之上,返回值为实际附加的下层设备,可能为PDO也可能为过滤驱动)

一个FDO驱动的上下层:上层在AttachDevice域中,下层在调用IoAttachDeviceToDeviceStack后将返回值保存在设备扩展中进行记录

④ 设置刚才创建出来的FDO的Flags子域,指明该设备的属性,其中fdo->Flags &= ~DO_DEVICE_INITIALIZING为必须的,意味着告诉外面设备初始化完毕

WDM驱动的DriverUnload例程

NT模型中DriverUnload负责删除设备和符号链接
WDM模型中删除设备和符号链接的工作交由IRP_MN_REMOVE_DEVICE的IRP处理函数负责
WDM模型中的DriverUnload相对简单,主要是销毁程序员自己申请的内存等资源

WDM驱动IRP_MN_REMOVE_DEVICE的IRP处理

IRP_MN_REMOVE_DEVICE的作用:当设备需要被卸载时,由即插即用管理器创建并发送到驱动程序中
对应的派遣函数在DriverEntry中指定
主要步骤:

  1. 作为一个IRP处理函数,先处理IRP本身(转发给下层驱动)
  2. 删除符号链接
  3. 将FDO设备从PDO的设备栈上摘下来(调用IoDetachDevice)
    自此FDO从设备栈上被删除,但PDO仍然存在,删除PDO由操作系统负责,与程序员无关

驱动的层次结构

垂直层次结构

在这里插入图片描述
设备创建顺序为从下往上创建,先创建底层PDO,再创建高层的FDO
设备向上查找:通过设备对象的AttachedDevice,如果某一设备为空,则到了顶部
设备向下查找:调用IoAttachDeviceToDeviceStack后,将返回值保存在设备扩展中

水平层次结构

水平层次结构存在于同一驱动创建出来的设备对象之间的关系
在这里插入图片描述
例如:电脑中插入两块相同型号网卡,这两块网卡的PDO就是同一水平层次,都是由PCI总线驱动所创建,它们的FDO也在同一水平层次

复杂层次结构

在这里插入图片描述
举例:最初windows只有一根总线,根总线驱动对插在总线上的设备进行枚举,枚举到一个USB_HUB设备,安装这个USB_HUB设备的PDO和FDO;然后USB_HUB设备的FDO又可以作为一个总线,继续枚举插在USB_HUB设备上的设备,分别安装它们的驱动
总结:FDO也可以作为一个总线驱动,继续枚举该总线上的设备(如USB_HUB)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值