• Windows内存管理(1)


    1.      分配内核内存

    Windows驱动程序使用的内存资源非常珍贵,分配内存时要尽量节约。和应用程序一样,局部变量是存放在栈空间中的。但栈空间不会像应用程序那么大,所以驱动程序不适合递归调用或者局部变量是大型数据结构。如果需要大型数据结构,我们可以在堆中申请。

    堆中申请的函数有以下几个:

    1PVOID 
            ExAllocatePool(
               IN POOL_TYPE  PoolType,
               IN SIZE_T  NumberOfBytes
               );

             2PVOID 
           ExAllocatePoolWithTag(
            IN POOL_TYPE  PoolType,
            IN SIZE_T  NumberOfBytes,
           IN ULONG  Tag
            );

    3 PVOID 
          ExAllocatePoolWithQuota(
           IN POOL_TYPE  PoolType,
          IN SIZE_T  NumberOfBytes
          );

        4PVOID 
          ExAllocatePoolWithQuotaTag(
          IN POOL_TYPE  PoolType,
          IN SIZE_T  NumberOfBytes,
          IN ULONG  Tag
           );

    PoolType:是个枚举变量,如果此值为NonPagedPool,则分配非分页内存。如果此值为PagedPool,则分配的内存为分页内存。

    NumberOfBytes是分配的内存大小,最好是4的整数倍。

    返回值是分配的内存地址,一定是内核模式下的地址。如果返回0,则代表分配失败。

     

    将分配的内存进行回收的函数如下:

    1VOID 
        ExFreePool(
        IN PVOID  P
        );

    2NTKERNELAPI
    VOID
      ExFreePoolWithTag(
        IN PVOID  P,
        IN ULONG  Tag 
        ); 

     

     

    2. 在驱动中使用链表

    在驱动程序开发中,经常要使用链表这种数据结构。DDK为用户提供两种链表的数据结构,简化了对链表的操作。

    下面主要讲的是双向链表。

    1)链表结构

    DDK提供了标准的双向链表。双向链表可以将链表形成一个环。BLINK指针指向前一个元素。FLINK指针指向下一个元素。

    Windows内存管理(1)--分配内核内存 和 使用链表 - Fly - 从C开始
     

    DDK提供的双向链表的数据结构如下:

    typedef struct _LIST_ENTRY {
      struct _LIST_ENTRY *Flink;
      struct _LIST_ENTRY *Blink;
    } LIST_ENTRY, *PLIST_ENTRY;

     

    2)链表初始化

    每个双向链表都是以一个链表头作为链表的第一个元素。初次使用链表头需要进行初始化。主要是将链表头的FLINKBLINK两个指针指向自己。表示链表头所代表的链是空链。

    链表头初始化函数如下:

    VOID 
     InitializeListHead(
      IN PLIST_ENTRY  ListHead
      );

    Windows内存管理(1)--分配内核内存 和 使用链表 - Fly - 从C开始
     如何判断链表是否为空,可以判断链表头的BLINKFLINK指针是否指向自己。DDK为我们提供了一个宏简化了这种检查:

    BOOLEAN 
      IsListEmpty(
        IN PLIST_ENTRY  ListHead
        );

     

    上面定义的链表头只有前后指针而没有数据元素。因此我们需要自己定义链表中每个元素的数据类型。并将LIST_ENTRY结构作为自定义结构的一个子域。LIST_ENTRY的作用就是将自定义数据结构串成一个链表。

    例如:

    typedef struct _MYDATASTRUCT{

    LIST_ENTRY  ListEntry;

    ULONG  x;

    ULONG  y;

    }MYDATASTRUCT,*PMYDATASTRUCT;

     

    3)插入删除链表元素

    从链表头部插入:

    VOID 
      InsertHeadList(
        IN PLIST_ENTRY  ListHead,
        IN PLIST_ENTRY  Entry
        );

    用法如下:

    InsertHeadList(&head, &mydata->ListEntry);

    其中,headLIST_ENTRY结构的链表头,mydata是自定义的数据结构。

     

    从链表尾部插入:

    VOID 
      InsertTailList(
        IN PLIST_ENTRY  ListHead,
        IN PLIST_ENTRY  Entry
        );

     

    从链表中删除:

    PLIST_ENTRY 
      RemoveHeadList(
        IN PLIST_ENTRY  ListHead
        );

     

    PLIST_ENTRY 
      RemoveTailList(
        IN PLIST_ENTRY  ListHead
        );

     

    这两个函数返回的是从链表删除下来的元素中的LIST_ENTRY子域。当我们想获得用户自定义数据结构的指针时,有两种情况:

    (1)   自定义数据结构的第一个字段就是LIST_ENTRY结构,如下:

    typedef struct _MYDATASTRUCT{

    LIST_ENTRY  ListEntry;

    ULONG  x;

    ULONG  y;

    }MYDATASTRUCT,*PMYDATASTRUCT;

    这时,要得到自定义的数据结构可以如下:

    PLIST_ENTRY pEntry = RemoveHeadList&head);

    PMYDATASTRUCT pMyData = PMYDATASTRUCTpEntry

     

    (2)   自定义数据结构的第一个字段就不是LIST_ENTRY结构,如下:

    typedef struct _MYDATASTRUCT{

    ULONG  x;

    ULONG  y;

    LIST_ENTRY  ListEntry;

    }MYDATASTRUCT,*PMYDATASTRUCT;

    此时,前面的方法就是错误的,我们可以使用DDK为我们提供的一个宏

    PCHAR 
      CONTAINING_RECORD(
        IN PCHAR  Address,
        IN TYPE  Type,
        IN PCHAR  Field
        );

    要得到自定义的数据结构可以如下:

    PLIST_ENTRY pEntry = RemoveHeadList&head);

    PMYDATASTRUCT pMyData =

    CONTAINING_RECORDpEntry MYDATASTRUCT, ListEntry

     

    注意DDK建议无论自定义数据结构的第一个字段是否为LIST_ENTRY结构,最好都使用CONTAINING_RECORD宏得到自定义数据结构的指针。

    测试代码:

    typedef struct _MYDATASTRUCT{

         ULONG number;

         LIST_ENTRY ListEntry;

    }MYDATASTRUCT, *PMYDATASTRUCT;

     

    #pragma INITCODE

    VOID LinkListTest()

    {

         KdPrint(("进入双向链表测试函数!\n"));

         LIST_ENTRY ListHead;

         InitializeListHead(&ListHead);

        

         PMYDATASTRUCT pData;

         ULONG i;

         KdPrint(("开始往链表中插入数据!\n"));

         for (i=1; i<=10; i++)

         {

             pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool, sizeof(MYDATASTRUCT));

             pData->number = i;

            

             InsertHeadList(&ListHead, &pData->ListEntry);

         }

         KdPrint(("插入数据完毕!\n"));

         KdPrint(("-----------------------------------------------------------------\n"));

         KdPrint(("开始删除链表中的数据!\n"));

         while(!IsListEmpty(&ListHead))

         {

             PLIST_ENTRY pListEntry = RemoveHeadList(&ListHead);

             pData = CONTAINING_RECORD(pListEntry, MYDATASTRUCT, ListEntry);

             KdPrint(("Remove %d element.\n", pData->number));

             ExFreePool(pData);

         }

         KdPrint(("删除链表中的数据完毕!\n"));

         KdPrint(("-----------------------------------------------------------------\n"));

    }

    Windows内存管理(1)--分配内核内存 和 使用链表 - Fly - 从C开始
  • 相关阅读:
    中金所金融业务知识学习笔记(含股指期货、股指期权、国债期货)
    人工智能中小样本问题相关的系列模型演变及学习笔记(四):知识蒸馏、增量学习
    人工智能中小样本问题相关的系列模型演变及学习笔记(三):迁移学习、深度迁移学习
    人工智能中小样本问题相关的系列模型演变及学习笔记(二):生成对抗网络 GAN
    人工智能中小样本问题相关的系列模型演变及学习笔记(一):元学习、小样本学习
    行业知识图谱的构建及应用
    Verilog代码和FPGA硬件的映射关系(一)
    FPGA内部硬件结构简介
    Altera的Cyclone系列器件命名规则
    学会使用Hdlbits网页版Verilog代码仿真验证平台
  • 原文地址:https://www.cnblogs.com/forlina/p/2116190.html
Copyright © 2020-2023  润新知