失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > Windows驱动开发(一)WDM/WDF驱动概述

Windows驱动开发(一)WDM/WDF驱动概述

时间:2018-08-26 11:34:53

相关推荐

Windows驱动开发(一)WDM/WDF驱动概述

距离开发windows驱动已经快要满一年了,从一开始的小白什么都不知道,到现在,也算是有不少心得,准备抽时间写一些比较详细的经验分享,关于驱动开发的文章相对比较少,写的较深入的也不多,所以我想只有大家多共享一些内容,才能进步的更快!话不多说,开始。

本章主要讲述一些入门的概念性内容,有些部分可能会显得不好记,不用慌,等看了后续的文章用到的时候,再回头复习一下,就能理解它的用处了。

大家都知道,最早的windows驱动为NT式驱动,这个架构过于的旧,所以也就不在我的文章范围里面了,我主要介绍WDM/WDF的驱动,当然了,WDM/WDF驱动其实也有很多NT驱动的影子,这些共有的部分,我也会做简单介绍。下面是一个DriverEntry实例:

NTSTATUS

DriverEntry(

IN PDRIVER_OBJECT DriverObject,

IN PUNICODE_STRING RegistryPath

)

{

WDF_DRIVER_CONFIG config;

NTSTATUS status;

WDF_DRIVER_CONFIG_INIT(

&config,

EvtDeviceAdd

);

status = WdfDriverCreate(DriverObject,

RegistryPath,

WDF_NO_OBJECT_ATTRIBUTES,

&config,

WDF_NO_HANDLE);

if (!NT_SUCCESS(status)) {

//KdPrint(("WdfDriverCreate failed with status 0x%x\n", status));

}

return status;

}

NT以及WDM/WDF的驱动,每一个驱动的入口例程都是DriverEntry,和编写普通应用程序一样,驱动程序也会有类似Main函数的例程,它就是DriverEntry。它的原型为DriverEntry(IN PDRIVER_OBJECT DriverObject,IN PUNICODE_STRING RegistryPath),这个例程主要对驱动程序进行初始化,它是被系统进程所调用的,是的,就是进程管理器里面的system进程了。系统线程调用驱动程序的DriverEntry时,同时传进上述的两个参数,DriverObject式指向刚才被创建驱动对象的指针,RegistryPath则是指向设备服务键的键名字符串的指针。另外,这个RegistryPath有时候是需要保存下来的,因为这个字符串并不是长期存在的,函数返回时有可能会消失,如果以后想要用这个串的话,那就必须先把它复制到安全的地方。这个字符串的内容一般会是\REGISTRY\MACHINE\SYSTEM\ControlSet\Services\[服务名],另外,在驱动程序中,字符串需要用UNICODE来表示。

在DriverEntry例程中,一般设置卸载例程和IRP的派遣函数,另外的一部分代码则负责创建设备对象,在非NT驱动中,需要添加AddDevice函数,此回调函数为WDM/WDF驱动程序独有,作用为创建设备对象并由PNP(即插即用)管理器调用。同时也需要设置IRP_MJ_PNP回调函数,对PNP的IRP处理,是NT驱动和WDM驱动的重大区别之一。另外,在驱动程序中,所有的函数和变量要被指明加载到分页内存或非分页内存中,其中DriverEntry需要放在INIT标志的内存中,INIT标志指明该函数只在加载的时候载入内存,成功加载之后,可以从内存中卸载。下面是一个AddDevice实例:

NTSTATUS

EvtDeviceAdd(

IN WDFDRIVER Driver,

IN PWDFDEVICE_INIT DeviceInit

)

{

WDF_IO_QUEUE_CONFIG queueConfig;

WDF_OBJECT_ATTRIBUTES attributes;

NTSTATUS status;

WDFDEVICE device;

PFDO_DEVICE_DATA deviceData;

PNP_BUS_INFORMATION busInfo;

WDFQUEUE queue;

WDFDEVICE hDevice;

UNREFERENCED_PARAMETER(Driver);

PAGED_CODE ();

WdfDeviceInitSetDeviceType(DeviceInit, FILE_DEVICE_BUS_EXTENDER);

WdfDeviceInitSetExclusive(DeviceInit, TRUE);

WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, FDO_DEVICE_DATA);

status = WdfDeviceCreate(&DeviceInit, &attributes, &device);

if (!NT_SUCCESS(status)) {

//KdPrint(("WdfDriverCreate FDO_DEVICE_DATA failed with status 0x%x\n", status));

return status;

}

//

// Get the device context.

//

deviceData = FdoGetData(device);

WDF_OBJECT_ATTRIBUTES_INIT(&attributes);

WDF_IO_QUEUE_CONFIG_INIT_DEFAULT_QUEUE(

&queueConfig,

WdfIoQueueDispatchParallel

);

queueConfig.EvtIoDeviceControl = EvtIoDeviceControl;

status = WdfIoQueueCreate(device,

&queueConfig,

WDF_NO_OBJECT_ATTRIBUTES,

&queue

);

if (!NT_SUCCESS(status)) {

//KdPrint(("WdfIoQueueCreate failed with status 0x%x\n", status));

return status;

}

status = WdfDeviceCreateDeviceInterface(

device,

&GUID_DEVINTERFACE_HELLO,

NULL // No Reference String

);

if (!NT_SUCCESS(status)) {

//KdPrint(("WdfDriverCreateDeviceInterface failed with status 0x%x\n", status));

return status;

}

return status;

}

windows操作驱动的一些基本概念,win32子系统是最纯正的windows子系统,提供了大量的API函数。windows API分为三类,USER函数,GDI函数,KERNEL函数,分别对应了user32.dll,gdi32.dll,kernel32.dll。操作系统除了将应用程序加载到内存中,同时也会将这三个dll文件加载到内存中。大部分win32子系统的API,都通过Native API实现。Native API的函数一般都是在win32 API上加上Nt两个字母,例如CreateFile函数对应NtCreateFile函数,所有Native API都是在Ntdll.dll中实现的。同时,Native API负责从用户模式穿越进入内核模式。

看了以上这些,相信你也肯定对驱动有了一些基础的概念了,那么,操作系统是如何去使用驱动程序的呢?对于WDM/WDF驱动来说,一般都是基于分层的,完成一个设备的操作,至少需要两个驱动设备共同完成。一个是物理设备对象(PDO),另一个是功能设备对象(FDO),其关系是“附加”与“被附加”的关系。物理设备驱动也就是WDF中的KMDF,在系统总线枚举驱动发现插入了新设备之后,会由总线驱动创建PDO,PDO无法单独操作设备,需配合FDO一起,此时系统会去寻找相对应的UMDF,即设备功能驱动,如果找到了,这个设备就可以正常运行了。

刚有提到WDM/WDF驱动是分层的,那么这个分层该怎么去理解呢?当一个设备的功能驱动安装成功之后,PDO设备对象的子域AttachedDevice会记录FDO的位置。PDO被称作底层驱动或下层驱动,而FDO会被称作高层驱动或上层驱动。这里的“上层”即是接近发出IO请求的地方,而“下层”指的是靠近物理设备的地方。这就是它的分层,当然,这是最简单的情况,假如PDO与FDO之间存在过滤驱动时,在FDO上面的过滤驱动被称作上层过滤驱动(High FiDO),在FDO的下层的驱动,被称作下层过滤驱动(Low FiDO)。其实设备对象中,有个StackSize子域,表明操作这个设备对象需要几层才能到达最下层的物理设备,例如刚才举的例子里,High FiDO(StackSize=4)-- FDO(StackSize=3)--Low FiDO(StackSize=2)--PDO(StackSize=1)。DRIVERENTRY入参的驱动对象DRIVER_OBJECT中有个DriverExtension,里面有AddDevice子域,用来设置AddDevice例程的函数地址,目的就是将FDO附加在PDO之上。

概述大概就写到这里,下一章的话会介绍驱动中的IO请求如何去处理。

如果觉得《Windows驱动开发(一)WDM/WDF驱动概述》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。