一个简单的NT驱动之驱动入口函数(DriverEntry)


一个简单的NT驱动之驱动入口函数(DriverEntry)
2011年12月16日
          选了本驱动开发方面的书《Windows驱动开发技术详解》,这本书类似于MFC方面孙鑫的《VC++深入详解》,看了一部分感觉还不错。
  在开始看这本书之前查了许多资料,是想看看自己的实际需求需要看些哪方面的书,但发现要学的技术太多了,汇编要学,操作系统要学。还是不管了,先找本书来看看再说。
  像学SDK时一样,照着书上的代码敲出了第一个驱动程序,称之为HelloDDK。
  在开发驱动程序之前,有些概念是需要了解的,但我了解得不多,反正把环境架设好,可以写驱动程序了。首先在微软的官方网站上下载了WDK,许多资料中都是让装DDK,其实WDK只是DDK的升级版本而已,性质跟SDK一样,属于开发包之列。
  这个驱动程序包含了三个函数:DriverEntry、HelloDDKUnload和HelloDDKDispatchRoutine。其中DriverEntry是驱动程序的入口函数,相当于C/C++程序的main函数,HelloDDKUnload函数是驱动卸载函数。而HelloDDKDispatchRuntine则是IRP的派遣函数,因为驱动程序主要是处理IO请求,而IO请求大多是在派遣函数中处理的。
  先来看看这个驱动程序的第一个函数:DriverEntry
  /****************************************************************
  * 函数名称: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 = ( PDRIVER_UNLOAD ) 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 = CreateDeivce( pDriverObject );
  KdPrint( ( "DriverEntry end!\n" ) );
  return status;
  }
  这个函数的第一句代码是一个#pragma预处理指令:
  #pragma INITCODE
  这段代码表明将此函数加入INIT内存区域。
  在函数头中的“extern "C"”是指定编译器的编译方式,若不加此修饰,编译器则会按C++的编译方式来编译,会导致编译错误。
  NTSTATUS是函数的返回状态,用来检测函数的状态是否正确,常用的NTSTATUS值有:
  #define STATUS_SUCCESS
  #define STATUS_BUFFER_OVERFLOW
  #define STATUS_UNSUCCESSFUL
  #define STATUS_NOT_IMPLEMENTED
  #define STATUS_ACCESS_VIOLATION
  #define STATUS_INVALID_HANDLE
  #define STATUS_INVALID_PARAMETER
  接着是函数参数中的IN修饰,该修饰是定义参数的类型,“IN”修饰表示是一个输入参数,“OUT”修饰表示是一个输出参数,“INOUT”修饰表示是一个输入输出参数。
  接下来就得了解了解驱动程序中的第一个结构体:PDRIVER_OBJECT,该结构体我在帮助文档里找了半天也没找到,最后在“wdm.h”中找到了其定义:
  typedef struct _DRIVER_OBJECT {
  CSHORT Type;
  CSHORT Size;
  PDEVICE_OBJECT DeviceObject;
  ULONG Flags;
  PVOID DriverStart;
  ULONG DriverSize;
  PVOID DriverSection;
  PDRIVER_EXTENSION DriverExtension;
  UNICODE_STRING DriverName;
  PUNICODE_STRING HardwareDatabase;
  PFAST_IO_DISPATCH FastIoDispatch;
  PDRIVER_INITIALIZE DriverInit;
  PDRIVER_STARTIO DriverStartIo;
  PDRIVER_UNLOAD DriverUnload;
  PDRIVER_DISPATCH MajorFunction[IRP_MJ_MAXIMUM_FUNCTION + 1];
  } DRIVER_OBJECT;
  typedef struct _DRIVER_OBJECT *PDRIVER_OBJECT;
  该结构体共有15个成员,这个结构体表示的是一个驱动对象,是驱动的一个实例,类似于用户模式下的线程句柄一类。该对象在DriverEntry函数中被始化,然后由内核中的I/O管理器来加载,确切的加载函数是IoCreateDevice。
  下面要说说该结构体中的几个重要成员:
  DeviceObject:这是一个设备对象的结构体。一个驱动程序会创建多个设备对象,每个设备对象都有指向下一个设备对象的指针,最后一个设备象的指针为空。此处的DeviceObject指向驱动程序的第一个设备对象,
  DriverName:驱动名称。其数据类型为UNICODE_STRING,该字符串一般为“\\driver\\[驱动名称]”,UNICODE_STRING字符串的赋值用RtlInitUnicodeString函数。
  DriverUnload:指定驱动卸载时使用的回调函数地址。
  MajorFunction:该成员是一个PDRIVER_DISPATCH结构体组数指针,其定义如下:
  __drv_functionClass(DRIVER_DISPATCH)
  __drv_requiresIRQL(PASSIVE_LEVEL)
  __drv_sameIRQL
  typedef
  NTSTATUS
  DRIVER_DISPATCH (
  __in struct _DEVICE_OBJECT *DeviceObject,
  __inout struct _IRP *Irp
  );
  typedef DRIVER_DISPATCH *PDRIVER_DISPATCH;
  每个指针指向一个函数,这个函数就是处理IRP的派遣函数。
  FastIoDispatch:文件驱动中用到的派遣函数。
  派遣函数相当于WIN32子系统下的事件响应函数,驱动程序的主要功能是负责处理IO请求,而绝大多数的IO请求是在派遣函数中处理的。关于IRP派遣函数的相关解释另作笔记。
  在PDRIVER_OBJECT中有一个PDEVICE_OBJECT成员,下面再来说说这个结构体,其定义在“wdm.h”中。该结构体保存的是设备对象指针链表,其成员包含了当前设备对象指针和下一个设备对象指针。在卸载驱动时要遍历驱动程序的所有设备对象,并用IoDeleteDevice来删除所有设备对象。
  好了,再来看看DriverEntry的函数体,在对以上的几个概念作了了解后,函数体的作用就容易理解了。函数体只有两个功能,一是初始化DRIVER_OBJECT结构体,二是创建设备对象。
  糊里糊涂的写完了这篇笔记,没想一个只有十来行的驱动函数涉及的内容居然如此之多。虽然还是云里雾里的,但对驱动对象PDRIVER_OBJECT还是有了一点了解,在下一篇笔记中再来好好研究研究CreateDevice函数。
  在敲这第一个驱动程序的代码时,老把Driver和Device敲错,到不是不知道这两个单词的意思,只是不知道在哪些地方该用驱动对象(DRIVER_OBJET),哪些地方用设备对象(DEVICE_OBJECT)。

猜你喜欢

转载自dg239dg.iteye.com/blog/1359251