内存的分配与释放
驱动中通过调用ExAllocatePoolWithTag函数分配内存1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18ExAllocatePoolWithTag (
_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
3ExFreePool(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
12typedef __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 | //队列自旋锁获取函数 |
队列自旋锁增加使用KLOCK_QUEUE_HANDLE数据结构,这个数据结构唯一的表实一个队列自旋锁,普通自旋锁和队列自旋锁不能混用,采用同样的初始化方式