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;
char buf[512];
}STR_NODE,*PSTR_NODE; //这里定义了结构和结构指针的别名
这里有一点需要说明一下,LIST_ENTRY结构在STR_NODE中的位置是随意的,可以放STR_NODE中的最前、中间或是最后。但是我们知道,一个结构内的数据在内存中是连续存放的,如果我们把LIST_ENTRY插在最前面,那么这两个结构的存放地址从指针来看就是一样的,举例说明:
STR_NODE node; //&(node.list_entry) 和 &node的值是相等的
这样设计在后面的链表操作中就能不用考虑地址偏移的问题(具体什么问题我没有专门去探究,应该就是一些指针的使用问题,而且相关计算好像也已经封装成函数了,有个函数CONTAINING_RECORD后面会提到)。设计好STR_NODE后我们还需要一个LIST_ENTRY结构的链表头,并在使用链表前初始化链表头。完成了以上操作后,就可以使用InsertHeadList插入链表节点,使用RemoveTailList取下链表节点,然后使用CONTAINING_RECORD获取节点中存放的数据了。具体的操作步骤及代码如下:
(1)定义自己的链表节点数据结构(可以将LIST_ENTRY结构放在自定义结构的最前);
(2)定义一个链表头,并初始化;
(3)定义一个链表节点指针,为其分配内存,然后插入链表中;
(4)取出一个链表节点,读取节点中存放的数据。
LIST_ENTRY head_list; //链表头
InitializeListHead(&head_list); //初始化链表头
PSTR_NODE pstr_node; //定义一个指针
char msg[512]; //要输入的字符数串存放于此
memset(msg , 0 , sizeof(char) * 512 ); //保险起见,清零
strcpy(msg , " This is a test . " );
//这里 'tag1'的用法并没有错误,要求的参数格式是单引号中有四个任意字符,作为分配内存的标记
pstr_node = (PSTR_NODE)ExAllocatePoolWithTag(NonPagedPool , sizeof(STR_NODE) , 'tag1' ); //分配内存
memset(pstr_node -> buf , 0 , sizeof(char) * 512); //清零
if(pstr_node == NULL)
; //出错处理
else
strcpy(pstr_node -> buf , msg ); //将数据拷贝进链表节点中
InsertHeadList(&head_list , &str_node -> list_entry ); //插入链表
if(IsListEmpty( &head_list ))
; //插入链表失败 相应的处理
else
; //插入链表成功 相应信息输出
/**
** 自此,插入链表的操作就完成了
** 接下来是取出一个链表节点
** 这里有两种方法,分别对应LIST_ENTRY在STR_NODE中不同位置的情况
**/
// 第一种:LIST_ENTRY 结构在 STR_NODE 中位于最前(这种方法我没有实测,代码如有差错请自行debug)
void * plist_entry = RemoveTailList( &head_list );
if( plist_entry == NULL )
; //取出节点失败 相应处理
else
/**
**实际上这里用了一个不正确的方式:用printf输出,而内核程序不用控制台输出信息。
**我只是想说明节点数据已经获得了,可行的办法是将获得的数据写入一个文件,这不是本次笔记的主要内容,不做赘述
**/
printf( " %s " , ((PSTR_NODE)plist_entry) -> buf );
// 第二种:LIST_ENTRY结构在STR_NODE中不位于最前(位于最前也可以使用这个方法)
/**
**这里说明一下CONTAINING_RECORD的用法:
**第一个参数:RemoveTailList的返回值,类型PLIST_ENTRY
**第二个参数:要获取数据的自定义结构,这里就是STR_NODE
**第三个参数:STR_NODE结构中是LIST_ENTRY结构的元素,这里也就是list_entry(见前面STR_NODE的定义)
**返回值一个地址值,也就是计算后得到的STR_NODE结构的地址值
**/
PLIST_ENTRY plist_entry = RemoveTailList( &head_list );
if( plist_entry == NULL )
; //取出节点失败 相应处理
PSTR_NODE pstr_node = CONTAINING_RECORD( plist_entry , STR_NODE , list_entry );
printf( "%s" , pstr_node -> buf ); //这里也是不正确的用法,用意同上一个printf
/**
**这里请注意,ExFreePool和ExAllocatrPoolWithTag是成对出现的,之前分配的内存使用的ExAllocatrPoolWithTag
**所以这里可以使用ExFreePool,否则会出大问题(没试过所以不造什么大问题)
**/
ExFreePool(pstr_node); //释放分配的内存
自此链表的创建、插入、删除操作就都完成了
本代码没有实际跑过,也许会出现一些细节上的错误,被坑的话不要打我
(被很多文章坑过,能坑一次的话其实我的内心是满足的)。