链表,在linux内核中非常常见。在分析 nand flash内核驱动时,在链表这里就卡住了,还是基础打得不牢啊!废话不多说,下面对链表进行一些简单的分析:
最基本的双向链表结构:struct list_head {
struct list_head *next;
struct list_head *prev;
} ;
链表的操作:添加和删除节点
void list_add (struct list_head *new,struct list_head *head);//将new节点添加到head的头部
void list_add_tail (struct list_head *new,struct list_head *head);//将new节点添加到head的尾部
内核提供的链表节点类型只包含两个指针成员,不可能保存其他有用数据。一般的用法是将它嵌在需要组成链表的结构体类型中,如:
struct my_data {
struct list_head list;/*用于组成链表*/
int data; /*有用数据*/
};
进行链表操作时,以其list成员作为代表,如:
struct my_data *new =kmalloc(sizeof(struct my_data),GFP_KERNEL);
list_add(&new->list,&my_list);
也就是说,每个结构体里有一个双向链表,我们是通过这双向链表把结构体连接起来的。所以说,从链表头出发,可以到达每一个链表节点,通过上一个链表,可以得到下一个链表节点的指针ptr,并不能得到链表节点所在的结构体。这就需要借助于一个函数:container_of(ptr,type,member)
ptr: 链表节点的指针
type:链表节点所在结构体的类型
member:链表节点
因此,可以用以下方法对链表中的数据进行遍历:
struct my_data {
struct list_head list;
int data;
}
struct list_head *pos
for(pos=my_list->next,pos!=&my_list,pos=pos->next)
{
struct my_data *data=list_entry(pos,struct my_data,list);
}
这样,data就指向了struct my_data结构体。
内核还提供了一系列的宏,来对链表进行遍历:
list_for_each(pos,head);//head为要遍历的链表,pos为(struct list_head *),每次循环,pos指向链表的下一个节点。用这个宏时,pos指向节点本,大多数情况下,需要指向包含它的结构体的指针,这就用到下面的宏。
list_for_each_entry(pos,head,member)此时,pos并不指向节点了,它指向包含节点的结构体。
下面是此宏的一个具体用法:
struct my_data *pos
list_for_each_entry(pos,&my_list,list){
pos->data=0;/*结构体的数据赋值为0*/
}