您的位置:首页 > 其它

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

2014-02-28 10:23 183 查看
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"));
}

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