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)--分配内核内存 和 使用链表的更多相关文章

  1. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  2. Go语言内存管理(一)内存分配

    Go语言内存管理(一)内存分配 golang作为一种"高级语言",也提供了自己的内存管理机制.这样一方面可以简化编码的流程,降低因内存使用导致出现问题的频率(C语言使用者尤其是初学 ...

  3. Linux内存管理解析(三) : 内核对内核空间的内存管理

    内核采用 struct page 来表示一个物理页,在其中记载了诸多物理页的属性,比如 物理页被几个线程使用(如若没有则表示该页可以释放),页对应的虚拟地址. 首先需要知道的是,分配物理页可以分为两个 ...

  4. java虚拟机学习-JVM内存管理:深入Java内存区域与OOM(3)

    概述 Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝又 ...

  5. 启动期间的内存管理之bootmem_init初始化内存管理–Linux内存管理(十二)

    1. 启动过程中的内存初始化 首先我们来看看start_kernel是如何初始化系统的, start_kerne定义在init/main.c?v=4.7, line 479 其代码很复杂, 我们只截取 ...

  6. JVM内存管理:深入Java内存区域与OOM

    Java与C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面的人却想出来. 概述: 对于从事C.C++程序开发的开发人员来说,在内存管理领域,他们即是拥有最高权力的皇帝 ...

  7. Objective-C 【多个对象内存管理(野指针&内存泄漏)】

    ------------------------------------------- 多个对象内存管理(野指针&内存泄漏) (注:这一部分知识请结合"单个对象内存管理"去 ...

  8. 常用Actoin算子 与 内存管理 、共享变量、内存机制

    一.常用Actoin算子 (reduce .collect .count .take .saveAsTextFile . countByKey .foreach ) collect:从集群中将所有的计 ...

  9. 待解决问题:c++栈对象的析构、虚拟内存与内存管理的关系、内存管理的解决方案。

    待解决问题:c++栈对象的析构.虚拟内存与内存管理的关系.内存管理的解决方案.

随机推荐

  1. 【NIO】MappedByteBuffer-内存映射文件 I/O

    操作系统会在负责执行映射,用于操作大文件 java io操作中通常采用BufferedReader,BufferedInputStream等带缓冲的IO类处理大文件:java nio中引入了一种基于M ...

  2. 网格图必经点+dfs——cf1214D

    先正着走一次把所有可行路径标记出来,然后倒着走两条路径,一条是能向下就向下的路径,另一条能向右就向右. 如果这两条路径相交,那么(1,1)-(n,m)路径上比有个必经点,把这个必经点封上,答案是1,如 ...

  3. 工程师技术(一):启用SELinux保护、自定义用户环境、配置IPv6地址、配置聚合连接、配置firewalld防火墙

    一.启用SELinux保护 目标: 本例要求为虚拟机 server0.desktop0 配置SELinux: 确保 SELinux 处于强制启用模式 在每次重新开机后,此设置必须仍然有效 方案: SE ...

  4. NGINX配置之一:日志篇

    打开nginx.conf配置文件: vi /usr/local/nginx/conf/nginx.conf 日志部分内容: 日志生成的到Nginx根目录logs/access.log文件,默认使用“m ...

  5. Go语言TCP Socket编程

      Golang的主要 设计目标之一就是面向大规模后端服务程序,网络通信这块是服务端 程序必不可少也是至关重要的一部分.在日常应用中,我们也可以看到Go中的net以及其subdirectories下的 ...

  6. PAT_A1038#Recover the Smallest Number

    Source: PAT A1038 Recover the Smallest Number (30 分) Description: Given a collection of number segme ...

  7. 剑指offer——58数组中数值和下标相等的元素

    题目三: 数组中数值和下标相等的元素. 假设一个单调递增的数组里的每个元素都是整数并且是唯一的.请编程实现一个函数,找出数组中任意一个数值等于其下标的元素.例如,在数组{-3,-1,1,3,5}中,数 ...

  8. CSS:Stacking Context

    通常情况下,HTML页面可以被认为是二维的,因为文本,图像和其他元素被排列在页面上而不重叠.在这种情况下,只有一个渲染进程,所有元素都知道其他元素所占用的空间.z-index属性可让你在渲染内容时调整 ...

  9. axios获取本地文件配置步骤

    首先修改一下config文件夹下面的index.js文件里面的配置,如下: 然后 ,通过axios 请求配置的接口 <script> import axios from 'axios' e ...

  10. Vue番外篇-路由进阶(一)

    Vue的router默认是 export default new Router({ mode: 'history', routes: [ { path: '/', name: 'HelloWorld' ...