Windows内存管理(1)--分配内核内存 和 使用链表
1. 分配内核内存
Windows驱动程序使用的内存资源非常珍贵,分配内存时要尽量节约。和应用程序一样,局部变量是存放在栈空间中的。但栈空间不会像应用程序那么大,所以驱动程序不适合递归调用或者局部变量是大型数据结构。如果需要大型数据结构,我们可以在堆中申请。
堆中申请的函数有以下几个:
(1)PVOID 
        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,则代表分配失败。
将分配的内存进行回收的函数如下:
(1)VOID 
    ExFreePool(
    IN PVOID  P
    );
(2)NTKERNELAPI
VOID
  ExFreePoolWithTag(
    IN PVOID  P,
    IN ULONG  Tag 
    );
2. 在驱动中使用链表
在驱动程序开发中,经常要使用链表这种数据结构。DDK为用户提供两种链表的数据结构,简化了对链表的操作。
下面主要讲的是双向链表。
(1)链表结构
DDK提供了标准的双向链表。双向链表可以将链表形成一个环。BLINK指针指向前一个元素。FLINK指针指向下一个元素。

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
  );

如何判断链表是否为空,可以判断链表头的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_RECORD(pEntry, 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)--分配内核内存 和 使用链表的更多相关文章
- 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件
		本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ... 
- Go语言内存管理(一)内存分配
		Go语言内存管理(一)内存分配 golang作为一种"高级语言",也提供了自己的内存管理机制.这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学 ... 
- Linux内存管理解析(三) : 内核对内核空间的内存管理
		内核采用 struct page 来表示一个物理页,在其中记载了诸多物理页的属性,比如 物理页被几个线程使用(如若没有则表示该页可以释放),页对应的虚拟地址. 首先需要知道的是,分配物理页可以分为两个 ... 
- java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)
		概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ... 
- 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)
		1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ... 
- JVM内存管理:深入Java内存区域与OOM
		Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ... 
- Objective-C 【多个对象内存管理(野指针&内存泄漏)】
		------------------------------------------- 多个对象内存管理(野指针&内存泄漏) (注:这一部分知识请结合"单个对象内存管理"去 ... 
- 常用Actoin算子  与   内存管理  、共享变量、内存机制
		一.常用Actoin算子 (reduce .collect .count .take .saveAsTextFile . countByKey .foreach ) collect:从集群中将所有的计 ... 
- 待解决问题:c++栈对象的析构、虚拟内存与内存管理的关系、内存管理的解决方案。
		待解决问题:c++栈对象的析构.虚拟内存与内存管理的关系.内存管理的解决方案. 
随机推荐
- Vue学习笔记【23】——Vue组件(组件的定义)
			定义Vue组件 什么是组件: 组件的出现,就是为了拆分Vue实例的代码量的,能够让我们以不同的组件,来划分不同的功能模块,将来我们需要什么样的功能,就可以去调用对应的组件即可: 组件化和模块化的不同: ... 
- 运行go代码
			go运行go代码 现在,让我们通过创建一个简单的示例,开启我们的go学习旅程,并学习如何编译和执行go程序.打开你最喜欢的文本编辑器,输入以下代码: package main func main() ... 
- 【Flutter学习】之VSCode下Flutter常用终端命令行
			Flutter 常用命令行 相关项目操作 查看Flutter版本 查看当前版本 flutter --version 查看所有版本 flutter version 打印所有命令行用法信息 flutter ... 
- ac自动机暴力跳fail匹配——hdu5880
			很简单的题,ac自动机里再维护一个len表示每个状态的串长,用s去query时每到一个结点都要暴力跳fail,因为有可能这个结点不是,但是其fail是危险结点,找到一个就直接break 再用个差分数组 ... 
- css3 动画+动画库
			css3 动画 实现原理 1.transition 过渡:https://www.runoob.com/cssref/css3-pr-transition.html 语法:( transition的 ... 
- unittest框架学习笔记三之testsuite
			# coding=utf-8 '''created :2018/3/29 author:star project: testsuite'''import unittest,time,osfrom ba ... 
- STM32时钟初始化函数SystemInit()详解【转】
			花了一天的时间,总算是了解了SystemInit()函数实现了哪些功能,初学STM32,,现记录如下(有理解错误的地方还请大侠指出): 使用的是3.5的库,用的是STM32F107VC,开发环境RVM ... 
- 什么是NVMe硬盘?
			本文摘自:https://zhidao.baidu.com/question/590890784.html NVMe硬盘指的是非易失性内存主机控制器接口规范(Non-Volatile Memory e ... 
- Django框架(一)—— 安装使用Django
			目录 Django入门 一.web应用 二.C/S 和B/S 架构 三.python中的web框架 四.http协议 五.URL简介 六.Django的安装和使用 Django入门 一.web应用 W ... 
- MySQL在Win10与Ubuntu下的安装与配置
			本文首发于cartoon的博客 转载请注明出处:https://cartoonyu.github.io/cartoon-blog 近段时间把自己电脑(win).虚拟机(Ubun ... 
