Windows驱动开发(二)WDM/WDF的IOControl

上一章大概介绍了WDM/WDF的驱动模型,

链接:https://blog.csdn.net/o0xwh_93150o/article/details/104213348

这一章我们主要来看一下驱动程序中的IO请求是如何处理的。

其实驱动程序的主要功能也就是负责处理IO请求,其中大部分IO请求是在派遣函数中处理的。用户模式下的所有对驱动程序的IO请求,全部由操作系统转化为一个叫做IRP(IO request package)的数据结构,不同的IRP会被“派遣”到不同的派遣函数(dispatch function)中。IRP的处理机制类似windows应用程序中的“消息处理”机制,驱动程序接收到不同类型的IRP后,会进入不同的派遣函数。

首先,IRP拥有两个基本的属性,一个是MajorFunction,另一个则是MinorFunction,分别记录IRP的主类型和子类型。操作系统根据MajorFunction将IRP派遣到不同的派遣函数中,然后再判断这个IRP属于哪种MinorFunction。如下所示:

//创建设备,CreateFile时触发

DriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDispatchCreate;

//关闭设备,CloseHandle时触发

DriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDispatchClose;

//对设备进行WriteFile时触发

DriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDispatchWrite;

//对设备进行ReadFile时触发

DriverObject->MajorFunction[IRP_MJ_READ] = HelloDispatchRead;

当然,IRP的类型远不止这四种,对于其他没有设置的IRP类型,系统会默认将这些IRP类型与IoInvalidDeviceRequest函数关联。大部分的IRP都源于文件IO处理Win32 API,例如CreateFile与ReadFile。假如我们对这个类型的IRP不需要特别处理的话,只需要在派遣函数中直接返回success就可以了。如下:

NTSTATUS status = STATUS_SUCCESS;

//设置irp的完成状态

pIrp->IoStatus.Status = status;

//设置irp操作的实际字节数

pIrp->IoStatus.Infomation = 0;

//结束irp请求时需调用IoCompleteRequest

IoCompleteRequest(pIrp, IO_NO_INCREMENT);

return status;

在驱动程序设置完这些IRP之后,是不是就可以使用用户应用程序向该驱动发送IO请求了呢?其实并不是如此,我们都知道,在用户态假如要操作这个设备,CreateFile时必要的一个参数,就是设备句柄,但是在用户态,其实是看不到内核态设备的句柄的,例如该设备的句柄为“\\Devices\\HelloDevice0”,这个句柄仅能被内核模式下的其他驱动所看见,所以我们需要创建一个符号链接,让用户态的程序可以得到这个句柄,类似“\\DosDevices\\HelloDevice0_LINK”这样。如下实例:

#define MYWDF_KDEVICE L“\\Devices\\HelloDevice0”

#define MYWDF_LINKNAME L“\\DosDevices\\HelloDevice0_LINK”

        // 创建设备

        WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&object_attribs, DEVICE_CONTEXT_CTRL);

//为设备首先分配内存

device_init = WdfControlDeviceInitAllocate(WdfDeviceGetDriver(Device), &SDDL_DEVOBJ_SYS_ALL_ADM_RWX_WORLD_RW_RES_R);

RtlInitUnicodeString(&ustring, MYWDF_KDEVICE);

//将设备名字存入device_init中

status = WdfDeviceInitAssignName(device_init, &ustring);

        if (!NT_SUCCESS(status)) {

             //

        }

//创建一个controldevice,用于创建符号链接

        status = WdfDeviceCreate(&device_init, &object_attribs, &control_device);

        if (!NT_SUCCESS(status)) {

//

        }

        RtlInitUnicodeString(&ustring, MYWDF_LINKNAME);

//创建符号链接

        status = WdfDeviceCreateSymbolicLink(control_device, &ustring);

        if (!NT_SUCCESS(status)) {

   //

        }

这样,你的设备就可以被用户态的程序所使用了,接下来,再在用户态使用CreateFIle打开这个符号链接就可以了。驱动程序中我们已经设置好了Write和Read的派遣函数,只要使用WriteFile和ReadFile,就可以触发这两个IRP回调函数了,这个就不再举例子了。

当然,有时候应用程序并不需要真的向这个设备进行读和写操作,它可能仅仅需要“告知”驱动程序,我想要做什么,驱动程序可以在自己的会话空间里就足够处理,这时,微软为我们准备了另一个Win32 API:DeviceIoControl。这个API会在内部使操作系统创建一个IRP_DEVICE_CONTROL类型的IRP,你只需要在驱动中设置这个类型IRP的回调函数就可以了。

DeviceIoControl的函数原型如下:

BOOL WINAPI DeviceIoControl(

_In_ HANDLE hDevice,

_In_ DWORD dwIoControlCode,

_In_opt_ LPVOID lpInBuffer,

_In_ DWORD nInBufferSize,

_Out_opt_ LPVOID lpOutBuffer,

  _In_ DWORD nOutBufferSize,

_Out_opt_ LPDWORD lpBytesReturned,

_Inout_opt_ LPOVERLAPPED lpOverlapped);

其中第二个参数dwIoControlCode,就是IO控制码,即IOCTL值,用来告诉驱动程序,我需要进行哪种类型的交互,当然,前提是这个IOCTL值是驱动程序预先设置好已知的,我们可以用CTL_CODE这个宏来定义一组IOCTL值。CTL_CODE的定义为(DeviceType,Function,Method,Access),DeviceType为设备对象的类型,这个类型应该与创建设备(IoCreateDevice)时的类型匹配,如FILE_DEVICE_XX这样的宏;Function为驱动程序定义的IOCTL宏,0x0000-0x7FFFF为微软保留,0x800-0xFFF为程序员自定义;Method为操作模式,应使用如下四种之一,METHOD_BUFFERED(使用缓冲区操作),METHOD_IN_DIRECT(使用直写操作),METHOD_OUT_DIRECT(使用直写操作),METHOD_NEITHER(使用其他方式操作);Access为访问权限,如果没有特殊要求,一般使用FILE_ANY_ACCESS。如下是一个实例:

#define IOCTL_HELLO CTL_CODE(\

 FILE_DEVICE_UNKNOWN, \

 0x999, \

 METHOD_BUFFERED, \

   FILE_ANY_ACCESS)

这里说一下关于METHOD_BUFFERED,使用缓冲内存模式的IOCTL,要避免在驱动中直接访问用户模式下的内存地址。在这种模式下,DeviceIoControl的大致流程是这样的,定义CTL_CODE宏->得到当前堆栈(IoGetCurrentIrpStackLocation)->得到输入缓冲区大小(DeviceIoControl.InputBufferLength)->得到输出缓冲区大小(DeviceIoControl.OutputBufferLength)->得到IOCTL码(DeviceIoControl.IoControlCode)->switch处理该IOCTL->设置IRP状态->结束IRP请求(IoCompleteRequest)。

关于驱动程序的IO请求就说到这里,下一章的话,我们就直接进入实战篇吧~拿一个WDF的总线驱动来分析一下。

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值