驱动中链表的使用

链表是驱动开发中经常遇到的一个数据结构,主要是双向循环链表;要使用链表,需要用到一个LIST_ENTRY的结构,其定义如下:

typedef struct _LIST_ENTRY {
    struct _LIST_ENTRY  *Flink;    // 指向下一个节点(后继)
    struct _LIST_ENTRY  *Blink;    // 指向前一个节点(前驱)
} LIST_ENTRY, *PLIST_ENTRY;

在实际的编程中,我们需要自己定义链表的节点,并把节点的第一个成员设置为LIST_ENTRY类型的变量(不一定放在第一位,但通常是这样);不过经测试,不放在第一位会导致蓝屏!!

此外,我们还需要一个LIST_ENTRY类型的链表头;其他的就靠下面的函数或者宏来操作了:

InitializeListHead,初始化链表头
IsListEmpty,判断链表是否为空
InsertHeadList,从链表头部插入节点
InsertTailList,从链表尾部插入节点
RemoveHeadList,从链表头部删除节点
RemoveTailList,从链表尾部删除节点

CONTAINING_RECORD,从RemoveHeadList或者RemoveTailList返回的数据获取一个指向删除节点的指针

下面是在头文件中的链表的实现:

//给节点定义一个结构类型,这里定义为结构体类型
typedef struct _strNode{
    LIST_ENTRY Entry;    //Windows自己提供的 LIST_ENTRY结构双向循环链表
    int x;              //调用LIST_ENTRY 结构时,一定要把它写在第一位,数据域写在它的下面,不然会导致蓝屏!!
    int y;
}Node, *pNode;

//建立链表
pNode SetNode(void){

    pNode Head, A;   // 定义两个结构体的指针类型变量;

    Head =(pNode)ExAllocatePool(PagedPool,sizeof(Node)) ;   //实例化一个Head 的结构体对象并在分页内存中分配空间

    //LIST_ENTRY的结构表头必须要用InitializeListHead初始化
    InitializeListHead(&Head->Entry);   

    Head->x = 0; 
    Head->y = 0;  //给头节点的两个数据域赋值

    //下面开始向链表中插入5个元素(节点)并给对应的节点的数据域赋值
    for(int i = 1; i <= 5; i++){

        A = (pNode)ExAllocatePool(PagedPool, sizeof(Node));   //实例化节点 A ,在分页内存中分配空间

        //给数据域赋值
        A->x = i;
        A->y = i + 1;

        //由于LIST_ENTRY 结构已是双向循环链表形式,所以这里就不用自己进行循环指向设定了
        InsertHeadList(&Head->Entry, &A->Entry);  //参数一:被插入的节点,参数二:要插入的节点
    }
    return Head;
}

//打印出双向循环链表的数据和节点地址
void GetPrint(pNode head){
    pNode temp;
    temp = head;
    if (temp != NULL){
        do{
            DbgPrint("节点 %d = %x,Next = %x, prior = %x, x = %d, y = %d \n",
 temp->x, temp, temp->Entry.Flink, temp->Entry.Blink, temp->x, temp->y);
            temp = (pNode)temp->Entry.Blink;   //强制将 "_LIST_ENTRY *" 类型下的成员转换为 "pNode " 指针类型
        } while (!(temp == head));
    }
    return;
}

下面只需要在驱动入口进行调用就可以打印出来,最好事在驱动加载语句的后边调用,先等驱动加载成功!

extern "C" NTSTATUS DriverEntry(PDRIVER_OBJECT pDriverObject, PUNICODE_STRING pRegistrPath)
{
    CreateMyDevice(pDriverObject);                                      //调用自定义的 CreateMyDevice 来创建设备对象

    //注册派遣函数,与IRP 请求包的数据类型一一对应
    pDriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = Send_Irp;     // 请求发送的I/O管理器和其他操作系统组件、其他内核模式驱动程序,DeviceIoControl函数会产生此IRP
    pDriverObject->MajorFunction[IRP_MJ_CREATE] = Send_Irp;             // 创建设备,CreateFile会产生此IRP,返回一个句柄给用户应用程序
    pDriverObject->MajorFunction[IRP_MJ_READ] = Send_Irp;               // 读取设备内容,ReadFile会产生此IRP
    pDriverObject->MajorFunction[IRP_MJ_WRITE] = Send_Irp;              // 改写设备内容,WriteFile时会产生此IRP
    pDriverObject->MajorFunction[IRP_MJ_CLOSE] = Send_Irp;              // 关闭保留的通往目标设备对象的句柄,CloseHandle会产生此IRP
    pDriverObject->MajorFunction[IRP_MJ_CLEANUP] = Send_Irp;            // 清除用户模式应用程序的目标设备对象的句柄,CloseHandle会产生此IRP    
    
    /*MemoryOpe();*/
    GetPrint(SetNode()); // 调用双向循环链表函数
    pDriverObject->DriverUnload = Unload;                               //调用卸载函数    
    KdPrint(("AppDriver 驱动加载成功!"));
    return STATUS_SUCCESS;
}

实例化和初始化的区别是什么?
    实例化:
          一般是由类创建对象。如有 class A ,而 A a;//这就是实例化,创造了一个名为 a 的 A对象。
    初始化:
          有很多情况,如函数的初始化,类里成员的初始化等,即给一些变量赋予初始值,那些变量已经存在,只是赋值,
          不像实例化那样构造一个实例的时候需要在内存中开辟空间。

    原文作者:算法小白
    原文地址: https://www.cnblogs.com/lfls128/p/5022210.html
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞