驱动内存的分配与释放

内存的分配与释放

驱动中通过调用ExAllocatePoolWithTag函数分配内存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ExAllocatePoolWithTag (
_In_ __drv_strictTypeMatch(__drv_typeExpr) POOL_TYPE PoolType,
_In_ SIZE_T NumberOfBytes,
_In_ ULONG Tag
);

//示例
UNICODE_STRING dst = {0};
UNICODE_STRING src = RTL_CONSTANT_STRING(L"My source string!");

dst.Buffer = (PWCHAR)ExAllocatePoolWithTag(NonPagedPool, src.Length, MEM_TAG);

if(dst.Buffer == NULL)
{
status = STATUS_INSUFFICIENT_RESOURCES;
}
dst.Length = dst.MaximumLength = src.Length;
RtlCopyUnicodeString(&dst, &src);

  • PoolType参数:指明内存类型,NonPagedPool 锁定内存 PagedPool 分页内存
  • NumberOfBytes:申请的内存长度
  • Tag:内存分配标识,用于检测内存泄漏

ExAllocatePoolWithTag分配的内存使用ExFreePool来释放,如果不释放将永远泄露

1
2
3
ExFreePool(dst.Buffer);
dst.Buffer = NULL;
dst.Length = dst.MaximumLength = 0;

ExFreePool释放的必须是由ExAllocatePoolWithTag分配的内存,两者由成对关系

内核链表

LIST_ENTRY是一个双向链表结构,作用是保存文件的文件名和长度,使用者可以通过FILE_OBJECT的指针遍历链表找到文件名和文件长度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//节点数据结构
typedef struct{
PFILE_OBJECT file_object;
UNICODE_STRING file_name;
LARGE_INTEGER file_length;
} MY_FILE_INFOR, *PMY_FILE_INFOR;

//LIST_ENTRY链
typedef struct _LIST_ENTRY{
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
}LIST_ENTRY, *PLIST_ENTRY;

//将LIST_ENTRY插入节点数据结构中构成链表节点
typedef struct{
LIST_ENTRY list_entry;
PFILE_OBJECT file_object;
UNICODE_STRING file_name;
LARGE_INTEGER file_length;
} MY_FILE_INFOR, *PMY_FILE_INFOR;

//代码示例
LIST_ENTRY my_list_head; //链表头

InitializeListHead(&my_list_head); //初始化链表头

//链表节点声明
typedef struct{
LIST_ENTRY list_entry;
PFILE_OBJECT file_object;
UNICODE_STRING file_name;
LARGE_INTEGER file_length;
} MY_FILE_INFOR, *PMY_FILE_INFOR;

//节点信息添加函数
NTSTATUS MyFileInforAppendNode(
PFILE_OBJECT file_object,
UNICODE_STRING file_name,
LARGE_INTEGER file_length)
{
PMY_FILE_INFOR my_file_infor = (PMY_FILE_INFOR)ExAllocatePoolWithTag(PagedPool, sizeof(MY_FILE_INFOR), mem_tag);
if(my_file_infor == NULL)
{
return STATUS_INSUFFICIENT_RESOURES;
}

//填写数据成员
my_file_infor->file_object = file_object;
my_file_infor->file_name = file_name;
my_file_infor->file_length = file_length;

//插入链表尾部,没有锁
InsertHeadList(&my_list_head, (PLIST_ENTRY)&my_file_infor);
return STATUS_SUCCESS;
}

利用CONTAINING_RECORD得到LIST_ENTRY结构所在的节点的指针CONTAINING_RECORD(p, MY_FILE_INFOR, list_entry)

LARGE_INTEFER

在驱动开发中很少直接使用64位数据类型__int64,而是包装为一个共用体

1
2
3
4
5
6
7
8
9
10
11
12
typedef __int64 LONGLONG;
typedef union _LARGE_INTEFER {
struct {
ULONG LowPart;
LONG HighPart;
}
struct {
ULONG LowPart;
LONG HighPart;
} u;
LONGLONG QuadPart;
} LARGE_INTEGER;

自旋锁

锁用于线程同步,在驱动开发中,大多是存在多线程执行环境的,可能一个线程在同时调用同一个函数

1
2
3
4
5
6
7
8
//获取自旋锁
KSPIN_LOCK my_spin_lock;
KeInitializeSpinLock(&my_spin_lock); //初始化自旋锁

KIRQL Irql; //用于保存当前IRQL级别
KeAcquireSpinLock(&my_spin_lock, &Irql); //加锁,IRQL级别升高
......
KeReleaseSpinLock(&my_spin_lock, Irql); //解锁,IRQL级别降低

自旋锁导致锁间代码单线程执行,锁变量应该定义为静态或者全局变量,或者保存在堆中

在链表中使用自旋锁

LIST_ENTRY有一系列带锁操作,只需要为其提供一个初始化完成的锁即可

1
2
3
4
5
6
7
8
9
10
11
12
//加锁添加节点
PLIST_ENTRY ExInterlockedInsertHeadList(
PLIST_ENTRY ListHead,
PLIST_ENTRY ListEntry,
PKSPIN_LOCK Lock
);

//加锁移除链表第一个节点,并将移除节点的指针返回
PLIST_ENTRY ExInterlockedRemoveHeadList(
PLIST_ENTRY ListHead,
PKSPIN_LOCK Lock
);

队列自旋锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//队列自旋锁获取函数
VOID KeAcquireInStackQueuedSpinLock(
PKSPIN_LOCK SpinLock,
PKLOCK_QUEUE_HANDLE LockHandle
);
//队列自旋锁释放函数
VOID KeReleaseInStackQueuedSpinLock(
PKLOCK_QUEUE_HANDLE LockHandle
);

//示例
KSPIN_LOCK my_Queue_SpinLock = {0};
KeInitializeSpinLock(&my_Queue_SpinLock);

KLOCK_QUEUE_HANDLE my_lock_queue_handle;

KeAcquireInStackQueuedSpinLock( &my_Queue_SpinLock. &my_lock_queue_handle );//加锁
......
KeReleaseInStackQueuedSpinLock( &my_lock_queue_handle ); //解锁
队列自旋锁增加使用KLOCK_QUEUE_HANDLE数据结构,这个数据结构唯一的表实一个队列自旋锁,普通自旋锁和队列自旋锁不能混用,采用同样的初始化方式