• Windows内存管理(1)--分配内核内存 和 使用链表


    1.      分配内核内存

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

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

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

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

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

        (4)PVOID 
          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)链表初始化

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

    链表头初始化函数如下:

    VOID 
     InitializeListHead(
      IN PLIST_ENTRY  ListHead
      );

    Windows内存管理(1)--分配内核内存 和 使用链表 - Fly - 从C开始
     如何判断链表是否为空,可以判断链表头的BLINK和FLINK指针是否指向自己。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);

    其中,head是LIST_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 = (PMYDATASTRUCT)pEntry;

     

    (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(("进入双向链表测试函数! "));

         LIST_ENTRY ListHead;

         InitializeListHead(&ListHead);

        

         PMYDATASTRUCT pData;

         ULONG i;

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

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

         {

             pData = (PMYDATASTRUCT)ExAllocatePool(PagedPoolsizeof(MYDATASTRUCT));

             pData->number = i;

            

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

         }

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

         KdPrint(("----------------------------------------------------------------- "));

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

         while(!IsListEmpty(&ListHead))

         {

             PLIST_ENTRY pListEntry = RemoveHeadList(&ListHead);

             pData = CONTAINING_RECORD(pListEntryMYDATASTRUCTListEntry);

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

             ExFreePool(pData);

         }

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

         KdPrint(("----------------------------------------------------------------- "));

    }

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

  • 相关阅读:
    EOJ 2743 Stock Exchange
    POJ-3468 A Simple Problem with Integers
    EOJ-1104 bitmap
    【转】旋转卡壳——凸多边形间对踵点对(定义)
    Ring 3层枚举进程的四种方法
    XX-Net项目,免费浏览谷歌的伟大项目
    浅析Java中的内存机制
    Ubuntu下eclipse中安装Scala插件
    注入(5)---导入表注入(HookINT)
    Linux下MySQL导入文件出错ERROR 1290 (HY000)
  • 原文地址:https://www.cnblogs.com/vcerror/p/4289083.html
Copyright © 2020-2023  润新知