您的位置:首页 > 其它

浅谈Windows内存管理以及链表使用

2015-04-12 16:51 162 查看
/*

author:少仲

blog: http://blog.csdn.net/py_panyu
weibo: http://weibo.com/u/3849478598
欢迎转载,转载请注明出处.

*/

驱动程序和应用程序一样,局部变量是存放在栈空间中的.但是栈的空间不会像应用程序一样大,所以驱动程序不适合用递归调用或者局部变量是大型结构体.如果需要大型数据结构,就要在堆中申请内存.

以下是几个在堆中申请内存的函数:

PVOID
ExAllocatePool(
IN POOL_TYPE  PoolType,
IN SIZE_T  NumberOfBytes
);
PVOID
ExAllocatePoolWithTag(
IN POOL_TYPE  PoolType,
IN SIZE_T  NumberOfBytes,
IN ULONG  Tag
);
PVOID
ExAllocatePoolWithQuota(
IN POOL_TYPE  PoolType,
IN SIZE_T  NumberOfBytes
);
PVOID
ExAllocatePoolWithQuotaTag(
IN POOL_TYPE  PoolType,
IN SIZE_T  NumberOfBytes,
IN ULONG  Tag
);
typedef enum _POOL_TYPE
{
NonPagedPool,                       //要求分配非分页内存
PagedPool,                          //要求分配分页内存
NonPagedPoolMustSucceed,            //要求分配非分页内存,必须成功
DontUseThisType,                    //未指定
NonPagedPoolCacheAligned,           //要求分配非分页内存,且必须内存对齐
PagedPoolCacheAligned,              //要求分配分页内存,且必须内存对齐
NonPagedPoolCacheAlignedMustS       //要求分配非分页内存,且必须内存对齐,必须成功
} POOL_TYPE;
NumberOfBytes是分配内存大小,最好是4的整数倍

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

Tag 是一个4字节标记,便于查找

下面是回收的函数:

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


P就是要释放的地址

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Lookaside结构

频繁的申请和回收内存,会导致在内存上产生大量的内存“空洞”,从而导致最终无法申请内存.DDK为程序员提供了Lookaside结构来解决这个问题.

我们可以将Lookaside对象看成是一个内存容器.在初始化的时候,它先向Windows申请了一块比较大的内存.以后程序员每次申请内存的时候,不是直接向Windows申请内存,而是想Lookaside对象申请内存.Looaside会智能的避免产生内存“空洞”.如果Lookaside对象内部内存不够用时,它会向操作系统申请更多的内存.

Lookaside一般会在以下情况下使用:

1.程序员每次申请固定大小的内存.

2.申请和回收的操作十分频繁.

初始化Lookaside对象,有两个函数

VOID
ExInitializeNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST  Lookaside,
IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,
IN PFREE_FUNCTION  Free  OPTIONAL,
IN ULONG  Flags,
IN SIZE_T  Size,
IN ULONG  Tag,
IN USHORT  Depth
);
VOID
ExInitializePagedLookasideList(
IN PPAGED_LOOKASIDE_LIST  Lookaside,
IN PALLOCATE_FUNCTION  Allocate  OPTIONAL,
IN PFREE_FUNCTION  Free  OPTIONAL,
IN ULONG  Flags,
IN SIZE_T  Size,
IN ULONG  Tag,
IN USHORT  Depth
);
这两个函数分别是对非分页和分页Lookaside对象进行初始化
下面是申请内存的操作:

PVOID
ExAllocateFromNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST  Lookaside
);
PVOID
ExAllocateFromPagedLookasideList(
IN PPAGED_LOOKASIDE_LIST  Lookaside
);
这两个函数分别是对非分页内存和分页内存的申请
下面是Lookaside对象回收内存的操作:

VOID
ExFreeToNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST  Lookaside,
IN PVOID  Entry
);
VOID
ExFreeToPagedLookasideList(
IN PPAGED_LOOKASIDE_LIST  Lookaside,
IN PVOID  Entry
);
这两个函数分别是对非分页内存和分页内存的回收
下面是删除Lookaside对象的操作:

VOID
ExDeleteNPagedLookasideList(
IN PNPAGED_LOOKASIDE_LIST  Lookaside
);
VOID
ExDeletePagedLookasideList(
IN PPAGED_LOOKASIDE_LIST  Lookaside
);


测试函数:

VOID LookasideTest()
{
int i = 0;

DbgPrint("enter LookasideTest");

PAGED_LOOKASIDE_LIST PageList;

//初始化Lookaside对象
ExInitializePagedLookasideList(&PageList,NULL,NULL,0,sizeof(MYDATASTRUCT),'tset',0);

PMYDATASTRUCT MyObjectArray[50]; //PMYDATASTRUCT定义见后文

//模拟频繁申请
for (i = 0; i <50; i++)
{
MyObjectArray[i] = (PMYDATASTRUCT)ExAllocateFromPagedLookasideList(&PageList);
}

//模拟频繁回收
for (i = 0; i <50; i++)
{
ExFreeToPagedLookasideList(&PageList,MyObjectArray[i]);
MyObjectArray[i] = NULL;
}

//删除Lookaside对象
ExDeletePagedLookasideList(&PageList);
}


---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在驱动中使用链表

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

typedef struct _LIST_ENTRY 
<span style="font-family: Arial, Helvetica, sans-serif;">{</span>
struct _LIST_ENTRY  *Flink;
struct _LIST_ENTRY  *Blink;
} LIST_ENTRY, *PLIST_ENTRY;




链表初始化

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

链表头初始化函数如下:

VOID
InitializeListHead(
IN PLIST_ENTRY  ListHead
);




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

例如:

typedef struct _MYDATASTRUCT
{
LIST_ENTRY  ListEntry;
ULONG  x;
ULONG  y;
}MYDATASTRUCT,*PMYDATASTRUCT;


元素插入链表

从链表头部插入:

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宏得到自定义数据结构的指针。

关于这个宏

当知道了某个struct内的某个field 的address, 利用其在结构体中的偏移, 用address减去偏移值,即可反推出这个大的struct 的instance address (结构地址)

这里主要是为了获取PLDR_DATA_TABLE_ENTRY的地址,对这个宏还有疑问的同学

(推荐http://www.cnblogs.com/nbsofer/archive/2013/01/07/2849913.html,这篇写的很详细)

测试代码:

typedef struct _MYDATASTRUCT
{
ULONG number;
LIST_ENTRY ListEntry;
}MYDATASTRUCT, *PMYDATASTRUCT;
VOID LinkListTest()
{
DbgPrint("entry list\n");

LIST_ENTRY ListHead;

InitializeListHead(&ListHead);

PMYDATASTRUCT pData;

ULONG i;

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

{

pData = (PMYDATASTRUCT)ExAllocatePoolWithTag(PagedPool, sizeof(MYDATASTRUCT),'TSIL');

pData->number = i;

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

}

DbgPrint("Insert Ok");

DbgPrint("Delete Ing!");

while(!IsListEmpty(&ListHead))

{

PLIST_ENTRY pListEntry = RemoveHeadList(&ListHead);

pData = CONTAINING_RECORD(pListEntry, MYDATASTRUCT, ListEntry);

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

ExFreePoolWithTag(pData,'TSIL');

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐