zoukankan      html  css  js  c++  java
  • 驱动中链表的使用

    链表是驱动开发中经常遇到的一个数据结构,主要是双向循环链表;要使用链表,需要用到一个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 
    ",
     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对象。
        初始化:
              有很多情况,如函数的初始化,类里成员的初始化等,即给一些变量赋予初始值,那些变量已经存在,只是赋值,
              不像实例化那样构造一个实例的时候需要在内存中开辟空间。

  • 相关阅读:
    C++结构体成员列表初始化
    hdu-3790-最短路径问题(Dijkstra)
    hdu--1878--欧拉回路(并查集判断连通,欧拉回路模板题)
    nyoj-1278-Prototypes analyze(二叉排序树模板)
    hdu-5183-Negative and Positive (NP)(hash模板)
    nyoj-130-相同的雪花(hash)
    详细介绍C++STL:unordered_map
    NYOJ-626-intersection set(二分查找)
    hdoj-2141-Can you find it?(二分查找)
    HDU-1232-畅通工程(并查集)
  • 原文地址:https://www.cnblogs.com/lfls128/p/5022210.html
Copyright © 2011-2022 走看看