首页
论坛
课程
招聘
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝

[分享]驱动开发学习笔记一

2009-4-10 20:50 11140

[分享]驱动开发学习笔记一

2009-4-10 20:50
11140
原本打算自己留着,因为写的太差,怕放出来让牛们看了笑话,不过一是最近刚开始学驱动,也遇到过不少问题,所以打算还是把学习笔记放出来,和大家交流下;也希望能给想学驱动的朋友点帮助;

驱动开发学习笔记一
  Windows 驱动的基本功能是为上层服务提供一些调用例程,总的来说,驱动程序可以分为用户模式的驱动(如,打印机驱动程序,和VDD)和内核模式的驱动,而内核模式的驱动有如下三种类型:
a ::文件系统驱动 File System drviers    处理I/O请求
b :即插即用驱动 Plug and Play drivers  用于pnp和电源管理的硬件设备
c :非即插即用驱动 NO plug and play drivers   用于扩展系统功能

关于对驱动分类的介绍,还可以进一步划分,可查看,《windows internal Fourth edition》第八章,这里我们只需要知道windows中的驱动可分NT式驱动程序和WDM驱动程序即可;

NT驱动的加载过程之一  DriverEntry:
和所有的应用层面上编写的程序一样,windows驱动程序也有一个入口函数,其函数名一般为DriverEntry,在这个入口函数中主要是对驱动程序进行初始化工作,它由系统程序调用,即System进程,它在系统启动的时候就被创建;当驱动加载的时候,system进程启动一个新的线程,调用执行体组件中的对象管理器,创建一驱动对象;驱动对象是一个DRIVER_OBJECT的结构体,同时,system进程调用执行体组件的配置管理程序,查询此驱动程序对应的注册表项;下面我们来看一下Driver_Entry例程:

/************************************************************************
* 函数名称:DriverEntry
* 功能描述:初始化驱动程序,定位和申请硬件资源,创建内核对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
      pRegistryPath:驱动程序在注册表的中的路径
* 返回 值:返回初始化驱动状态
*************************************************************************/
#pragma INITCODE
extern "C" NTSTATUS DriverEntry (
                        IN PDRIVER_OBJECT pDriverObject,
                        IN PUNICODE_STRING pRegistryPath        )
{
        NTSTATUS status;
        KdPrint(("Enter DriverEntry\n"));

        //注册其他驱动调用函数入口
        pDriverObject->DriverUnload = HelloDDKUnload;
        pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_CLOSE] = HelloDDKDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_WRITE] = HelloDDKDispatchRoutine;
        pDriverObject->MajorFunction[IRP_MJ_READ] = HelloDDKDispatchRoutine;
       
        //创建驱动设备对象
        status = CreateDevice(pDriverObject);

        KdPrint(("DriverEntry end\n"));
        return status;
}

DriverEntry的第一个参数是一个指针,指向一个刚被初始化的驱动程序对象,该对象就代表你的驱动程序。WDM驱动程序的DriverEntry例程应完成对这个对象的初始化并返回。非WDM驱动程序需要做大量额外的工作,它们必须探测自己的硬件,为硬件创建设备对象(用于代表硬件),配置并初始化硬件使其正常工作。而对于WDM驱动程序,颇麻烦的硬件探测和配置工作由PnP管理器自动完成,如果你想知道非WDM驱动程序是如何初始化自身的,参见Art Baker的《The Windows NT Device Driver Book (Prentice Hall, 1997)》、Viscarola和Mason的《Windows NT Device Driver Development (Macmillan, 1998)》。
DriverEntry的第二个参数是设备服务键的键名。这个串不是长期存在的(函数返回后可能消失),如果以后想使用该串就必须先把它复制到安全的地方。
这里我们只需要知道,DriverEnter函数的功能只是为由System进程创建的驱动对象进行初始化,例如,为常用IRP注册派遣例程,或者是startI/O例程,以及设置卸载例程函数和创建设备对象;

最后需要说明的是DriverEnter的参数修饰,IN,OUT,INOUT,它们在驱动程序中都被定义为空串: #define  IN()  #define OUT() 它们的功能是对参数进行注释,当看到一个“IN”参数时,代表其后面的参数纯粹用于输入目的,如在DriverEnter中的第一个参数,你可以改变这个DRIVER_OBJECT指针指向的对象,但你不能改变这个指针本身,也就是不能传一个不是DRIVER_OBJECT类型的指针给它;OUT代表这个参数仅用于输出参数,INOUT代表这个参数即用于输入也用于输出;

由上我们知道,在DriverEnter函数中只是对由系统创建的驱动对象做些初始化工作,而我们在应用程序下用CreateFileAPI函数打开的是设备,所以,在DriverEnter中有一函数用来创建设备对象,由于一个驱动对象可以创建多个设备对象,因此,创建设备对象的过程单独写成一函数;函数原型如下:

/************************************************************************
* 函数名称:CreateDevice
* 功能描述:初始化设备对象
* 参数列表:
      pDriverObject:从I/O管理器中传进来的驱动对象
* 返回 值:返回初始化状态
*************************************************************************/
#pragma INITCODE
NTSTATUS CreateDevice (
                IN PDRIVER_OBJECT        pDriverObject)
{
        NTSTATUS status;
        PDEVICE_OBJECT pDevObj;
        PDEVICE_EXTENSION pDevExt;
       
        //创建设备名称
        UNICODE_STRING devName;
        RtlInitUnicodeString(&devName,L"\\Device\\MyDDKDevice");
       
        //创建设备
        status = IoCreateDevice( pDriverObject,
                                                sizeof(DEVICE_EXTENSION),
                                                &(UNICODE_STRING)devName,
                                                FILE_DEVICE_UNKNOWN,
                                                0, TRUE,
                                                &pDevObj );
        if (!NT_SUCCESS(status))
                return status;

        pDevObj->Flags |= DO_BUFFERED_IO;
        pDevExt = (PDEVICE_EXTENSION)pDevObj->DeviceExtension;
        pDevExt->pDevice = pDevObj;
        pDevExt->ustrDeviceName = devName;
        //创建符号链接
        UNICODE_STRING symLinkName;
        RtlInitUnicodeString(&symLinkName,L"\\??\\HelloDDK");
        pDevExt->ustrSymLinkName = symLinkName;
        status = IoCreateSymbolicLink( &symLinkName,&devName );
        if (!NT_SUCCESS(status))
        {
                IoDeleteDevice( pDevObj );
                return status;
        }
        return STATUS_SUCCESS;
}

其实,上面这段代码中,真正创建设备的是IoCreateDevice函数,此函数在DDk中声明如下;
NTKERNELAPI
NTSTATUS
IoCreateDevice(
    IN PDRIVER_OBJECT DriverObject,    //设备对象
    IN ULONG DeviceExtensionSize,      //扩展大小
    IN PUNICODE_STRING DeviceName OPTIONAL,  //设备对象名
    IN DEVICE_TYPE DeviceType,    //设备类型
    IN ULONG DeviceCharacteristics,   //设备对象特征
    IN BOOLEAN Exclusive,          //设置设备对象是否可在内核模式下使用
    OUT PDEVICE_OBJECT *DeviceObject  //输出参数,I/o管理器负责创建这个设备对象
);

CreateDevice这个函数时,有几点需要注意:
1: 设备名称用unicode 字符指定,并且字符串必须是 “\Device\设备名 的形式,如磁盘分区C盘,为\Device\harddisk\volume1,当然你也可以不用指定设备名,但是这样做,不是设备就不需要设备名了,而是I/O管理器为设备创建一设备名,如果指定了设备名,只能被内核模式下的其他驱动程序识别,但是在用户模式下的应用程序能识别它吗?答案是否定的; 为了让用户模式的应用程序能够识别或者是使用设备,有两种方法可以使用: 一是通过创建一符号链接给用户模式应用程序使用,二是使用设备接口,一般NT中很少使用设备接口,而WDM中使用的较多;
2:你可以将符号链接理解为是设备对象的一个别名,设备对象的名称只能被内核模式下的驱动识别,而别名也可以被用户模式下的应用程序识别.创建符号链接用如下函数:
NTKERNELAPI
NTSTATUS
IoCreateSymbolicLink(
    IN PUNICODE_STRING SymbolicLinkName,
    IN PUNICODE_STRING DeviceName
);

注意:在内核模式下使用字符串必须使用unicode字符串,关于字符串的定义和使用有与之相关的函数,后面会单独介绍;

我们可以再设备扩展中记录我们需要的使用的对象或者是变量,也可以是其它的一些数据结构;

卸载设备驱动程序:
/************************************************************************
* 函数名称:HelloDDKUnload
* 功能描述:负责驱动程序的卸载操作
* 参数列表:
      pDriverObject:驱动对象
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
VOID HelloDDKUnload (IN PDRIVER_OBJECT pDriverObject)
{
        PDEVICE_OBJECT        pNextObj;
        KdPrint(("Enter DriverUnload\n"));
        pNextObj = pDriverObject->DeviceObject;
        while (pNextObj != NULL)
        {
                PDEVICE_EXTENSION pDevExt = (PDEVICE_EXTENSION)
                        pNextObj->DeviceExtension;

                //删除符号链接
                UNICODE_STRING pLinkName = pDevExt->ustrSymLinkName;
                IoDeleteSymbolicLink(&pLinkName);
                pNextObj = pNextObj->NextDevice;
                IoDeleteDevice( pDevExt->pDevice );
        }
}

由于在DriverEnter函数中创建了设备对象和符号链接,有可能还申请了其它一些资源,所以,当退出时,需要删除设备对象和符号链接,以及回收DriverEnter函数中申请的资源;我们知道,驱动对象可能拥有不至一个的设备对象,并且通过链表来管理这些设备对象,所以,在DriverUnload函数中,我们遍历由DriverObject其中一个愈传进来的设备对象链表,并将它们一一删除,删除设备使用函数IoDeleteDevice函数:

派遣函数:
/************************************************************************
* 函数名称:HelloDDKDispatchRoutine
* 功能描述:对读IRP进行处理
* 参数列表:
      pDevObj:功能设备对象
      pIrp:从IO请求包
* 返回 值:返回状态
*************************************************************************/
#pragma PAGEDCODE
NTSTATUS HelloDDKDispatchRoutine(IN PDEVICE_OBJECT pDevObj,
                                                                 IN PIRP pIrp)
{
        KdPrint(("Enter HelloDDKDispatchRoutine\n"));
        NTSTATUS status = STATUS_SUCCESS;
        // 完成IRP
        pIrp->IoStatus.Status = status;
        pIrp->IoStatus.Information = 0;        // bytes xfered
        IoCompleteRequest( pIrp, IO_NO_INCREMENT );
        KdPrint(("Leave HelloDDKDispatchRoutine\n"));
        return status;
}

在win32中,我们都知道应用程序是消息驱动的,而在驱动模式中,可以把驱动理解为是IRP驱动的,驱动程序一般是分层结构,对设备的操作会转化为对HAL,硬件抽象层的操作,IRP由I/O管理器创建,并传递给对应的驱动程序处理,而驱动程序调用我们在DriverEnter中注册的pDriverObject->MajorFunction[IRP_MJ_CREATE] = HelloDDKDispatchRoutine;函数来处理与之对应的IRP;

IRP是驱动开发的核心,关于IRP的数据结构和运行机制,学完后单独用一章来总结;

2020,给你一个诚意满满的夏令营!

最新回复 (15)
雪    币: 249
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
chengxjj 活跃值 2009-4-10 23:44
2
0
期待
雪    币: 203
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tcbhj 活跃值 2009-4-11 00:00
3
0
LZ是张帆?

这不明摆着抄袭?
雪    币: 203
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tcbhj 活跃值 2009-4-11 00:05
4
0
这种帖子也精华?

抄袭的还真是彻底

用到什么C++特性了?
雪    币: 651
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
轩辕小聪 活跃值 7 2009-4-11 04:18
5
0
汗,这些代码的确和《Windows驱动开发技术详解》书中的代码是一模一样的,这个看过书的一眼就能看出来。从楼主说“最近刚学驱动”,显然楼主不是张帆本人。楼主把代码注释中标明“作者:张帆”的字样删除掉,然后就原模原样的发上来,当成你自己的?引人家的代码,却故意回避提到人家的名字和原始出处,这是很不厚道的行为

楼主copy的痕迹在其他地方也很明显,如“我将在第六章讨论PnP。”明显就是在某本书里摘出来的。
还有驱动的入口点函数名DriverEntry,楼主的行文中有时说成DriverEnter,DriverEntry和DriverEnter这两个名词间隔出现,这已经不是笔误的问题了,显然表明相关的内容不是同一出处,至少不是楼主自己打出来的字。

楼上说得好,“抄袭得还真是彻底”,连嘴都不擦一下的。
雪    币: 204
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
Laona 活跃值 2009-4-11 18:10
6
0
这样都精华了..
早知我也写一个..
雪    币: 244
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
dapro 活跃值 2009-4-11 18:48
7
0
哈哈。有点明显
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝
kanghtta 活跃值 12 2009-4-11 20:22
8
0
晕。。没看是学习笔记嘛;
      我又没说我是张帆,,,,我就是看张帆的书入门的,。。。

精华不精华我不在乎。。。。。。  我是看这WDM中文的电子书和张帆的书一起学的。。。。
  学习笔记,,主要是对书中的东西总结。。以便以后回顾好复习。。。
我对楼上几位很无语,,,麻烦先看看我文章的标题 。。。。。。。。。

麻烦版主看到后撤消精华。。。。。  我喜欢看雪是因为这里的气氛。。。。。

在说驱动的框架代码怎么写都是这样。。我喜欢张帆的这种风格,,因为这种写法对初学者的我来说条理比较清楚,,更容易接受;

  其它的我不想争论,不过要你你们觉得在看雪写文章的目的是为了赚精华。。我觉得你们还不如去 人性本色。。。  。。
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝
kanghtta 活跃值 12 2009-4-11 20:56
9
0
1驱动的,驱动对象,设备对象。。。为什么要叫“对象”。。。
  
2对象不是C++的特性吗?
3对象之间靠消息传递我是在C++的教材中学到的

  上面的三句话:
  1句:引用了  windows驱动开发详解     windows驱动设计模式    等等书
  3句: 我说的C++教材是 谭浩强的那本。。
  
   这样写东西有意思吗?  这一却都是我的错。。。我道歉,,我真诚的道歉。。。
雪    币: 651
活跃值: 活跃值 (10)
能力值: ( LV12,RANK:300 )
在线值:
发帖
回帖
粉丝
轩辕小聪 活跃值 7 2009-4-11 20:59
10
0
既然是学习笔记,既然大部分是摘抄书里的内容,那标【分享】是不是比标【原创】要好一点?
毕竟用【原创】标签的话,大家会认为文章的最主要内容都应该是经作者自己消化后用自己的话讲出来的,即使有所引用,哪些是引用的部分,哪些是楼主自己写的内容,也应该可以让读者很明显地区别出来。
如果楼主一开始用的不是【原创】标题,那大家就不会误会你的文章性质,版主可能也不会一下子就加精华了。大家看到【原创】标题又加精华,却又发现里面很多摘抄引用的内容,引用的内容与楼主自己说出来的话的界限又不明显,大家当然会觉得别扭,楼主觉得是不是这个道理?
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝
kanghtta 活跃值 12 2009-4-11 21:04
11
0
这个倒是,我改一下。。。不过,非常谢谢你的建议;  你很细心,向你学习;不过我也不是书都没看,就写笔记的,主要是想在学习的过程中对作者的主要部分做个总结,引用作者的话也是我一句一句的敲上去的,这样是为了以后看起来也容易,不用带本书到处跑; 再说,计算机类的东西如果是处处看是不是摘抄的话,我估计现在出版社就没那么多的书出了;  发出来是希望那些想学驱动开发,又不知道怎么下手,看了后,可以买这本书看;

以后我多注意就行,,精华嘛,那个让版主撤消算啦。。我也没想这多;
雪    币: 349
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
要学会编 活跃值 2009-4-11 21:40
12
0
  最后注明一下书目就好了.

张帆这本书我也买了, 就是太罗嗦,生怕别人看不懂似的,有时候两句不重要的话,他能在一页上重复2次3次.
这些书都互相参考的,有多有少而已.
版主的那本循序渐进,第296页 图下第3行,明明没有第22章,还写着参见第22章, 然后再一翻目录,发现似乎跟核心编程的顺序差不多,再一查,发现DLL这部分似乎连例子都一样,估计也是参考了吧.

大家都是学习的,不要在意这么多. 每本书有自己的特色就好.
楼主帖子总结的不错.学习
雪    币: 203
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
tcbhj 活跃值 2009-4-11 21:45
13
0


我看LZ你还是用C写驱动吧
雪    币: 243
活跃值: 活跃值 (10)
能力值: ( LV13,RANK:500 )
在线值:
发帖
回帖
粉丝
kanghtta 活跃值 12 2009-4-11 22:47
14
0
   
雪    币: 18
活跃值: 活跃值 (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
aliwy 活跃值 2009-4-11 23:37
15
0
笔记笔记,就是在看书学习时记下自己想加深理想的内容嘛。
支持LZ兄弟,大家一起学。
雪    币: 1675
活跃值: 活跃值 (16)
能力值: ( LV15,RANK:670 )
在线值:
发帖
回帖
粉丝
cntrump 活跃值 13 2009-4-17 13:18
16
0
学习中.........
游客
登录 | 注册 方可回帖
返回