文章作者:grayfox
作者主页:http://nokyo.blogbus.com
原始出处:http://nokyo.blogbus.com/logs/33271026.html
在驱动程序的开发中经常需要用到链表,常见的链表有单向链表和双向链表,我们只介绍双向链表的使用方法,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);
}
}
上述代码可以正常地通过编译并运行,但其中存在着一个很大的隐患:它不是多线程安全的。如果有多个线程同时操作同一个链表的话,可能会引发不可预料的后
果,我们可以通过使用自旋锁来避免,修改后的代码如下所示:
VOID
LinkListTest()
{
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);
}
上述代码介绍了自旋锁的使用方法,但需要注意的是:上面这段代码在实际应用中是没有任何价值的。因为在上述代码我们定义的锁是一个局部变量,被分配在栈
中,这样每个线程在调用该函数的时候,都会重新初始化一个锁,因此这个锁就失去了本来的作用。在实际的编程中,我们应该把锁定义成一个全局变量,或者静态
(static)变量,或者将其创建在堆空间中。
另外,我们还可以为每个链表都定义并初始化一个锁,在需要向该链表插入或移除节点时不使用前面介绍的普通函数,而是使用如下方法:
ExInterlockedInsertHeadList(&linkListHead,
&pData->ListEntry, &spin_lock);
pData =
(PMYDATASTRUCT)ExInterlockedRemoveHeadList(&linkListHead,
&spin_lock);
此时在向链表中插入或移除节点时会自动调用关联的锁进行加锁操作,有效地保证了多线程安全性。
分享到:
相关推荐
在驱动中使用链表的原因主要在于其灵活性。例如,当需要动态添加或删除元素时,链表比固定大小的数组更高效。此外,链表也常用于实现设备队列、缓存管理和I/O请求队列等。 4. 驱动中的链表应用 - 设备队列:驱动...
虽然C语言标准库并未内置链表数据结构,但Linux内核提供了一套高效且灵活的链表操作函数,使得在驱动程序中使用链表变得简单。 首先,让我们理解链表的基本概念。链表是一种动态数据结构,由一系列节点组成,每个...
windows x64驱动程序开发 13.驱动中使用链表
四、在驱动中使用链表 链表是内核编程中常用的动态数据结构。在驱动中操作链表需要注意内存分配与释放的管理,以及线程安全问题。使用LIST_ENTRY宏可以方便地在内核模式下创建和操作双向链表。为了保证多线程环境下...
这一系列文章不仅覆盖了基础知识,如驱动开发环境的搭建、驱动程序的基本结构,还深入探讨了具体的技术细节,如如何在驱动中使用链表和读写文件等高级主题。 #### 知识点二:驱动开发环境的搭建 驱动开发的第一步...
#### 四、在驱动中使用链表 - **内存的分配与释放**: 使用`ExAllocatePoolWithTag`和`ExFreePoolWithTag`等函数来分配和释放内存。内存管理是内核模式编程中的关键部分。 - **使用LIST_ENTRY**: `LIST_ENTRY`是一种...
四、在驱动中使用链表 链表是内核模式下管理资源和数据结构的常见方式。学习如何分配和释放内存,以及如何使用LIST_ENTRY结构来构建和操作链表,是驱动开发中的重要技能。此外,为了保证并发访问的安全性,还需要...
#### 在驱动中使用链表 1. **内存的分配与释放** - 在内核模式下,内存的分配和释放需要特别注意。 - 使用`ExAllocatePool`和`ExFreePool`来分配和释放非页面内存;使用`ExAllocatePoolWithTag`和`...
#### 五、在驱动中使用链表 - **内存分配与释放**: 在内核模式下,内存管理和用户模式不同,需要使用特定的API来分配和释放内存。 - **使用LIST_ENTRY**: `LIST_ENTRY`结构体用于链接列表中的节点,是内核模式下...
- **第四章:在驱动中使用链表** - **4.1 内存的分配与释放**:在内核模式下,内存管理是非常关键的环节之一,需要特别注意内存泄漏等问题。 - **4.2 使用LIST_ENTRY**:LIST_ENTRY是Windows内核中用于构建双向...
5.2 在驱动中使用链表 5.2.1 链表结构 5.2.2 链表初始化 5.2.3 从首部插入链表 5.2.4 从尾部插入链表 5.2.5 从链表删除 5.2.6 实验 5.3 Lookaside结构 5.3.1 频繁申请内存的弊端 5.3.2...
5.2 在驱动中使用链表 5.2.1 链表结构 5.2.2 链表初始化 5.2.3 从首部插入链表 5.2.4 从尾部插入链表 5.2.5 从链表删除 5.2.6 实验 5.3 Lookaside结构 5.3.1 频繁申请内存的弊端 5.3.2...
- **第四章**:在驱动中使用链表 - 说明内核模式下内存分配与释放的操作方法。 - 介绍`LIST_ENTRY`结构体的使用。 - 讲解自旋锁(Spin Locks)的应用场景。 - 给出使用链表的具体示例程序。 - **第五章**:在...
..............\............\2.9.2在驱动中使用链表sys部分 -(36课).doc ..............\............\2.A.1驱动下的异常处理 -(37课).doc ..............\............\2.A.2内核模式下的字串操作 -(38课).doc ...
在驱动中使用链表 - **链表使用**:链表是驱动程序中常用的数据结构之一,用于组织和管理数据。 - **应用场景**:数据包队列、缓存管理等。 #### 36. 使用DeviceIoControl通信 - **DeviceIoControl**:用于与...
`CList, NAME&>`定义了一个双向链表,其中`NAME`是链表节点的数据类型,而`NAME&`表示链表中的每个节点将包含`NAME`类对象的一个引用。这种设计允许我们高效地存储和管理`NAME`类的对象。 #### `NAME`类定义 `NAME...
本文主要探讨了如何在Linux内核设备驱动中使用链表,包括其基本结构、初始化、操作和遍历。 首先,链表的基本结构由`struct list_head`定义,它包含两个指针成员`next`和`prev`,用于构建双循环链表。与传统的双...