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;

扫描二维码关注公众号,回复: 9008571 查看本文章

    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请求如何去处理。

发布了1 篇原创文章 · 获赞 9 · 访问量 136

猜你喜欢

转载自blog.csdn.net/o0xwh_93150o/article/details/104213348