Linux内核提供了list这个数据结构以及操作函数。这个数据结构在内核驱动中使用广泛,而且确实方便使用。为了详细了解这个数据结构的用法,方便以后使用,把这个数据结构里面的功能都试用了一次,清楚了很多。下面是实验程序和自己得出的使用方法,请高手多多指教。
Struct list_head { struct list_head *next, *prev ;};
List结构实际上是一个list_head结构构成的链表。如果想使用这个结构来构造自己的链表,只要把这个struct list_head结构嵌入到自己的结构体中,成为自己结构体的一个成员即可。整个list.h文件的函数都使用这个结构构成一个链表,而自己的结构体成员实际并不在链表上,但是list.h中使用container_of这个宏可以通过结构体中的struct list_head成员的地址来得到整个结构体的地址,这样就可以实现使用list.h文件中的定义实现自己的链表构造了。
list.h提供的函数构造的链表是有一个空头节点的。
如果在用户程序中使用这个结构的话,需要对这个文件做一些改动。主要是找不到如何将这个头文件直接安装到/usr/include目录下,只好直接拷贝。但是这个头文件中同时包含了另外的两个体系相关的头文件,一个是prefetch.h文件,这个文件提供perfetch这个函数用来针对特定的CPU体系做优化;另一个是asm/system.h这两个文件无法提供,只能不拷贝,将list.h中使用到这两个文件的部分直接拷贝到list.h文件中。主要是container_of宏(这个在kernel.h中;另外需要将所有的prefetch注释掉,否则无法链接通过。
LIST_HEAD(name):这个宏可以定义一个struct list_head结构体,并初始化结构体中的两个成员函数
LIST_INIT_HEAD( struct list_head *head) ;这个函数初始化给定的结构体,使的成员指向自己;
List_add ( struct list_head *new , struct list_head *list );new是要添加到list链表中的节点;list是要添加到的链表中的一个节点,new这个节点会添加到这个节点后面。如果提供的list是一个链表的头节点,那么这个函数用来模拟一个LIFO的栈是不错的。
list_add_tail( struct list_head *new , struct list_head *list );和上面的函数一样,只不过是new的位置在list的前面。如果提供的list是一个链表的头节点,那么就是一个FIFO的队列。
list_del (struct list_head *entry , struct list_head *list );从链表中删除entry节点,并将entry节点的成员指针指向无效地址。
list_replace(struct list_head * new , struct list_head *old);用节点new替换old,这个函数完成后,old中的指针仍然指向原来的链表
list_replace_init( struct list_head * new , struct list_head * old );和上面的函数一样,但是会将old中的成员指针初始化为无效地址
list_move (struct list_head * list , struct list_head * head );将list节点从原来的链表上删除,添加到head链表中
list_move_tail(struct list_head *list , struct list_head * head );这个和上面的函数一样,但是添加的位置在head的前面
list_is_last( struct list_head *list);list成员是否是它所在的链表的最后一个成员。是返回1,不是返回0;
list_is_empty( struct list_head *list )list链表是否为空,为空返回1,不为空返回0
list_is_empty_careful(struct list_head * list );和上面的函数一样,但是可以在SMP的情况下,较为准确的的到结构,但是也不很安全,最好使用同步方法来做
list_is_single( struct list_head *list )list链表中是否只有一个成员,是返回1,不是返回0
list_cut_position( struct list_head * list , struct list * head , struct list * entry );将链表head从entry位置分割,entry以后的成员链接到list上
list_splic_init( struct list_head *list , struct list_head * head );将list链表中的成员链接到head中去,并将list头重新初始化。
list_entry( per,type,member) 这个宏用来获得链表中一个节点,per表示要找的链表中的一个节点,是struct list_head *类型的变量,type表示嵌入了struct list_head结构的结构体的定义(是结构体本身的定义,就是那个struct ***{});member,表示嵌入的struct list_head结构体的变量名。返回的值是包含着struct list_head结构的结构体变量的地址。
比如:struct my_struct {
Struct list_head * node ;
Int a;
};
那么list_entry( struct list * ptr , struct my_struct , node )将返回一个struct my_struct *类型的指针,用这个指针可以操作a
List_first_entry( ptr,type,member) 和上面的一样,不过得到的是链表中的第一个成员;
List_for_each(pos, head);这个宏扩展后就是一条for语句,可以用来遍历一个链表。
List_for_each_pre(pos,head);和上面的一样,不过是反向遍历
List_for_each_entry(type * , struct list_head * ,member)这个宏扩展后也是一个for语句,但是可以用这个语句直接得到链表中的每个成员。中间的成员是链表的头
List_for_each_entry_continue( );这个宏可以用来遍历一个链表从给定的任意位置
HASH链表的有待进一步研究