DDK 中LIST_ENTRY的用法
来源:互联网 发布:python return 编辑:程序博客网 时间:2024/06/14 14:23
数据类型 LIST_ENTRY
另一个常见的 Windows 2000 数据类型是 LIST_ENTRY 结构。内核使用该结构将所有对象维护在一个双向链表中。一个对象分属多个链表是很常见的, Flink 成员是一个向前链接,指向下一个 LIST_ENTRY 结构, Blink 成员则是一个向后链接,指向前一个 LIST_ENTRY 结构。通常情况下,这些链表都成环形,也就是说,最后一个 Flink 指向链表中的第一个 LIST_ENTRY 结构,而第一个 Blink 指向最后一个。这样就很容易双向遍历该链表。如果一个程序要遍历整个链表,它需要保存第一个 LIST_ENTRY 结构的地址,以判断是否已遍历了整个链表。如果链表仅包含一个 LIST_ENTRY 结构,那么该 LIST_ENTRY 结构必须引用其自身,也就是说, Flink 和 Blink 都指向其自己。
typedef struct _LIST_ENTRY {
struct _LIST_ENTRY *Flink;
struct _LIST_ENTRY *Blink;
} LIST_ENTRY, *PLIST_ENTRY;
---------------------------------------------------------------
LIST_ENTRY使用:
VOID LinkListTest() {
LIST_ENTRY linkListHead;
//初始化链表
InitializeListHead(&linkListHead);
PMYDATASTRUCT pData;
ULONG i = 0;
//在链表中插入10个元素
KdPrint(("Begin insert to link list"));
for (i=0 ; i<10 ; i++) {
pData = (PMYDATASTRUCT)
ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
pData->number = i;
InsertHeadList(&linkListHead,&pData->ListEntry);
}
//从链表中取出,并显示
KdPrint(("Begin remove from link list\n"));
while(!IsListEmpty(&linkListHead)) {
PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
pData = CONTAINING_RECORD(pEntry,
MYDATASTRUCT,
ListEntry);
KdPrint(("%d\n",pData->number));
ExFreePool(pData);
}
}
遍历:
PLIST_ENTRY pLink=NULL;
for(pLink = glinkListRule.Flink; pLink !=(PLIST_ENTRY) &glinkListRule.Flink; pLink = pLink->Flink){
pRegPrtRule pData= CONTAINING_RECORD(pLink,RegPrtRule,ListEntry);
}
/*
#define CONTAINING_RECORD(address, type, field) ((type *)( \
(PCHAR)(address) - \
(ULONG_PTR)(&((type *)0)->field)))
*/
------------------------------------------------------------------
在驱动中使用链表:
在驱动程序的开发中经常需要用到链表,常见的链表有单向链表和双向链表,我们只介绍双向链表的使用方法,DDK为我们提供了标准的双向链表LIST_ENTRY,但这个链表里面没有数据,不能直接使用,我们需要自己定义一个结构体类型,然后将LIST_ENTRY作为结构体的一个子域,如下所示:
typedef struct _MYDATASTRUCT{
ULONG number;
LIST_ENTRY ListEntry;
} MYDATASTRUCT, *PMYDATASTRUCT;
实际上把LIST_ENTRY放在结构体的第一个子域才是较好的做法,此处我们不过多地关心,反正用法都是大同小异。下面我们就在驱动程序中创建一个链表,使用刚刚定义的结构体作为节点类型。代码如下所示:
VOID LinkListTest()
{
LIST_ENTRY linkListHead; // 链表
PMYDATASTRUCT pData; // 节点数据
ULONG i = 0; // 计数
//初始化
InitializeListHead(&linkListHead);
//向链表中插入10个元素
KdPrint(("[ProcessList] Begin insert to link list"));
for (i=0 ; i<10 ; i++) {
// pData是我们定义的指针,必须被初始化后才能使用
pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
pData->number = i;
// 将其作为一个节点插入链表
InsertHeadList(&linkListHead,&pData->ListEntry);
}
// 从链表中取出所有数据并显示
KdPrint(("[ProcessList] Begin remove from link list\n"));
while(!IsListEmpty(&linkListHead)) {
// 取出一个节点
PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
// 获取节点内容
pData = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);
KdPrint(("%d\n",pData->number));
// 释放节点,ExAllocatePool必须与ExFreePool成对使用
ExFreePool(pData);
}
}
上述代码可以正常地通过编译并运行,但其中存在着一个很大的隐患:它不是多线程安全的。如果有多个线程同时操作同一个链表的话,可能会引发不可预料的后果,我们可以通过使用自旋锁来避免,修改后的代码如下所示:
{
LIST_ENTRY linkListHead; // 链表
PMYDATASTRUCT pData; // 节点数据
ULONG i = 0; // 计数
KSPIN_LOCK spin_lock; // 自旋锁
KIRQL irql; // 中断级别
// 初始化
InitializeListHead(&linkListHead);
KeInitializeSpinLock(&spin_lock);
//向链表中插入10个元素
KdPrint(("[ProcessList] Begin insert to link list"));
// 锁定,注意这里的irql是个指针
KeAcquireSpinLock(&spin_lock, &irql);
for (i=0 ; i<10 ; i++) {
pData = (PMYDATASTRUCT)ExAllocatePool(PagedPool,sizeof(MYDATASTRUCT));
pData->number = i;
InsertHeadList(&linkListHead,&pData->ListEntry);
}
// 解锁,注意这里的irql不是指针
KeReleaseSpinLock(&spin_lock, irql);
//从链表中取出所有数据并显示
KdPrint(("[ProcessList] Begin remove from link list\n"));
// 锁定
KeAcquireSpinLock(&spin_lock, &irql);
while(!IsListEmpty(&linkListHead))
{
PLIST_ENTRY pEntry = RemoveTailList(&linkListHead);
pData = CONTAINING_RECORD(pEntry, MYDATASTRUCT, ListEntry);
KdPrint(("%d\n",pData->number));
ExFreePool(pData);
}
// 解锁
KeReleaseSpinLock(&spin_lock, irql);
}
另外,我们还可以为每个链表都定义并初始化一个锁,在需要向该链表插入或移除节点时不使用前面介绍的普通函数,而是使用如下方法:
ExInterlockedInsertHeadList(&linkListHead, &pData->ListEntry, &spin_lock);
pData = (PMYDATASTRUCT)ExInterlockedRemoveHeadList(&linkListHead, &spin_lock);
此时在向链表中插入或移除节点时会自动调用关联的锁进行加锁操作,有效地保证了多线程安全性。
- DDK 中LIST_ENTRY的用法
- DDK 中LIST_ENTRY的用法
- 内核链表中list_entry()函数 以及typeof的用法详解
- DDK中VPORT Mini-Driver的使用说明
- DDK中对IRP_MJ_CREATE的描述
- win ddk中字符串的使用方法
- DDK中VPORT Mini-Driver的使用说明
- list.h中宏list_entry宏的解析与应用
- Linux内核中list_head、list_for_each、list_entry、container_of之间的关系
- linux 链表(list_entry) 用法
- list_entry的宏定义
- list_entry的实现
- list_entry的使用说明
- list_entry
- list_entry
- list_entry
- list_entry
- list_entry
- zTree实例
- 键盘过滤驱动(修正了动态卸载问题)
- ztree多树实现异步交互
- VS2008+DDK(3790.1830)+ddkwizard搭建驱动开发环境
- jQuery zTree
- DDK 中LIST_ENTRY的用法
- Windows驱动程序入门
- 利用NTDLL导出函数枚举进程
- LSP信息查看
- C#.NET强制保留两位小数
- 网络数据包捕获与发送的多重实现 (学习)
- WinDBG 技巧:设断点命令详解(bp, bu, bm, ba 以及bl, bc, bd, be)
- IRP概述
- 驱动和应用层的三种通信方式 (学习)