windows驱动开发学习笔记一双向链表LIST_ENTRY

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);  //释放分配的内存
自此链表的创建、插入、删除操作就都完成了 本代码没有实际跑过,也许会出现一些细节上的错误,被坑的话不要打我 (被很多文章坑过,能坑一次的话其实我的内心是满足的)。
 
  

你可能感兴趣的:(自用,学习心得)