VS2013 WDK8.1驱动开发7(派遣函数)

本系列博客为学习《Windows驱动开发技术详解》一书的学习笔记。

前言

驱动程序的主要功能是负责处理I/O请求。用户模式下所有对驱动程序的I/O请求,全部由操作系统转化为一个叫做IRP的数据结构,不同的IRP数据会被“派遣”到不同的派遣函数中。

IRP与派遣函数

在前面介绍的HelloNTDriver和HelloWDM驱动程序中,我们会在DriverEntry里注册IRP的派遣函数。在HelloNTDriver中我们的DriverEntry内容如下:

    /// @brief 驱动程序入口函数
    /// @param[in] pDriverObject 从I/O管理器中传进来的驱动对象
    /// @param[in] pRegPath 驱动程序在注册表中的路径
    /// @return 初始化驱动状态
    NTSTATUS DriverEntry(IN PDRIVER_OBJECT pDriverObject, IN PUNICODE_STRING pRegPath)
    {
        NTSTATUS status = STATUS_SUCCESS;

        KdPrint(("Enter DriverEntry\n"));

        UNREFERENCED_PARAMETER(pRegPath);

        // 注册驱动调用函数入口
        // 这些函数不是由驱动程序本身负责调用, 而是由操作系统负责调用
        pDriverObject->DriverUnload = HelloNTDriverUnload;
        pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloNTDriverDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_READ] = HelloNTDriverDispatchRoutine;

        // 创建驱动设备对象
        status = CreateDevice(pDriverObject);

        KdPrint(("Leave DriverEntry\n"));

        return status;
    }

驱动对象pDriverObject中有个函数指针数组MajorFunction,每个元素都记录着一个函数的地址。通过设置这个数组,可以将IRP类型和派遣函数关联起来。示例中我们只设置了4个IRP的派遣函数,对于其他没有设置的IRP类型,系统默认这些IRP类型与_IopInvalidDeviceRequest函数关联。

IRP类型

示例中我们对4个类型的IRP(IRP_MJ_CREATE,IRP_MJ_CLOSE,IRP_MJ_WRITE,IRP_MJ_READ])设置了派遣函数,它们分别由CreateFile,CloseHandle,WriteFile,ReadFile这4个I/O函数产生。还有很多IRP由系统创建,下表列出了IRP的类型,并对其产生的来源做了说明:

| IRP类型                  | 来源          
|:-------------            |:------------- 
| IRP_MJ_CREATE            | 创建设备,CreateFile会产生此IRP 
| IRP_MJ_CLOSE             | 关闭设备,CloseHandle会产生此IRP
| IRP_MJ_CLEANUP           | 清除工作,CloseHandle会产生此IRP
| IRP_MJ_DEVICE_CONTROL    | DeviceIOControl函数会产生此IRP
| IRP_MJ_PNP               | 即插即用消息,WDM驱动才支持此种IRP
| IRP_MJ_POWER             | 在操作系统处理电源消息时,产生此IRP
| IRP_MJ_QUERY_INFORMATION | 获取文件长度,GetFileSize会产生此IRP
| IRP_MJ_READ              | 读取设备内容,ReadFile会产生此IRP
| IRP_MJ_SET_INFORMATION   | 设置文件位置,SetEndOfFile会产生此IRP
| IRP_MJ_SHUTDOWN          | 关闭系统前会产生此IRP
| IRP_MJ_SYSTEM_CONTROL    | 系统内部产生的控制信息
| IRP_MJ_WRITE             | 对设备进行WriteFile时产生此IRP

对IRP的简单处理

在HelloNTDriver中我们对IRP的简单处理函数如下:

    /// @brief 对IRP进行处理
    /// @param[in] pDriverObject
    /// @param[in] pIrp
    /// @return
    NTSTATUS HelloNTDriverDispatchRoutine(IN PDEVICE_OBJECT pDevObject, IN PIRP pIrp)
    {
        KdPrint(("Enter HelloNTDriverDispatchRoutine\n"));
        NTSTATUS status = STATUS_SUCCESS;
        UNREFERENCED_PARAMETER(pDevObject);

        pIrp->IoStatus.Status = status;
        pIrp->IoStatus.Information = 0;

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

        KdPrint(("Leave HelloNTDriverDispatchRoutine\n"));

        return status;
    }

我们设置了IRP的完成状态(pIrp->IoStatus.Status)为STATUS_SUCCESS,这样发起I/O请求的Win32API(如ReadFile)将会返回TRUE。如果设置IRP的完成状态为别的值(如STATUS_TIMEOUT ),那么Win32API将会返回False。这时我们可以使用GetLastError得到错误代码,得到的错误代码就是我们设置的IRP的状态值。

除了设置完成状态,我们还需要设置这个IRP操作了多少字节(pIrp->IoStatus.Information = 0)。如果是ReadFile产生的IRP,这个字节数代表从设备读了多少字节。

最后我们通过IoCompleteRequest将IRP请求结束。

I/O堆栈

在前面我们介绍过驱动的层次结构。不同的驱动对象会创建一个个设备对象,并将这些设备对象附加成一个垂直结构,这种结构被称为设备栈。IRP会被操作系统发送到设备栈的顶层,如果某层的设备对象的派遣函数结束了IRP的请求,则这次I/O请求结束。如果没有将IRP的请求结束,那么操作系统将IRP转发到设备栈的下一层处理。如下图所示:

因此一个IRP可能会被转发多次,为了记录IRP在每层设备中做的操作,IRP会有一个IO_STACK_LOCATION数组。数组的元素大于IRP穿越过的设备数。每个IO_STACK_LOCATION元素记录着对应设备中做的操作。对于本层设备对于的IO_STACK_LOCATION,可以通过IoGetCurrentIrpStackLocation函数得到,IO_STACK_LOCATION中会记录IRP的类型,我们修改我们的IRP派遣函数,让它打印当前的IRP类型:

    /// @brief 对IRP进行处理
    /// @param[in] pDriverObject
    /// @param[in] pIrp
    /// @return
    NTSTATUS HelloNTDriverDispatchRoutine(IN PDEVICE_OBJECT pDevObject, IN PIRP pIrp)
    {
        static char* s_IRPName[] =
        {
            "IRP_MJ_CREATE               ",
            "IRP_MJ_CREATE_NAMED_PIPE    ",
            "IRP_MJ_CLOSE                ",
            "IRP_MJ_READ                 ",
            "IRP_MJ_WRITE                ",
            "IRP_MJ_QUERY_INFORMATION    ",
            "IRP_MJ_SET_INFORMATION      ",
            "IRP_MJ_QUERY_EA             ",
            "IRP_MJ_SET_EA               ",
            "IRP_MJ_FLUSH_BUFFERS        ",
            "IRP_MJ_QUERY_VOLUME_INFORMAT",
            "IRP_MJ_SET_VOLUME_INFORMATIO",
            "IRP_MJ_DIRECTORY_CONTROL    ",
            "IRP_MJ_FILE_SYSTEM_CONTROL  ",
            "IRP_MJ_DEVICE_CONTROL       ",
            "IRP_MJ_INTERNAL_DEVICE_CONTR",
            "IRP_MJ_SHUTDOWN             ",
            "IRP_MJ_LOCK_CONTROL         ",
            "IRP_MJ_CLEANUP              ",
            "IRP_MJ_CREATE_MAILSLOT      ",
            "IRP_MJ_QUERY_SECURITY       ",
            "IRP_MJ_SET_SECURITY         ",
            "IRP_MJ_POWER                ",
            "IRP_MJ_SYSTEM_CONTROL       ",
            "IRP_MJ_DEVICE_CHANGE        ",
            "IRP_MJ_QUERY_QUOTA          ",
            "IRP_MJ_SET_QUOTA            ",
            "IRP_MJ_PNP                  "
        };

        KdPrint(("Enter HelloNTDriverDispatchRoutine\n"));
        NTSTATUS status = STATUS_SUCCESS;
        UNREFERENCED_PARAMETER(pDevObject);

        PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp);
        UCHAR irpType = pStack->MajorFunction;
        KdPrint((s_IRPName[irpType]));

        pIrp->IoStatus.Status = status;
        pIrp->IoStatus.Information = 0;

        IoCompleteRequest(pIrp, IO_NO_INCREMENT);

        KdPrint(("Leave HelloNTDriverDispatchRoutine\n"));

        return status;
    }

如果我们完成一个Win32程序,对我们的设备对象进行打开和关闭动作,就会得到如下结果:

其他章节链接

VS2013 WDK8.1驱动开发1(最简单的NT驱动)

VS2013 WDK8.1驱动开发2(最简单的WDM驱动)

VS2013 WDK8.1驱动开发3(手动加载NT驱动程序)

VS2013 WDK8.1驱动开发4(NT式驱动基本结构)

VS2013 WDK8.1驱动开发5(WDM驱动基本结构)

VS2013 WDK8.1驱动开发6(内存管理)

VS2013 WDK8.1驱动开发7(派遣函数)

VS2013 WDK8.1驱动开发8(设备读写操作)

VS2013 WDK8.1驱动开发9(IO设备控制操作)


分享给朋友阅读吧


您还未登录,登录微博账号发表精彩评论

 微博登录


最新评论

    还没有人评论...

 

 

刘杰

28岁, 现居苏州

微博:

微信:

BurnellLIU

邮箱:

burnell_liu@outlook.com

Github:

https://github.com/BurnellLiu

简介:

努力做一个快乐的程序员, good good study, day day up!

友情链接: Will Mao

苏ICP备16059872号. Copyright © 2016. http://www.burnelltek.com. All rights reserved.