文章转载请标注原文地址:http://blog.csdn.net/uranus_wm/article/details/11933201
linux驱动开发经常用到链表,在此总结一下链表相关操作:
1. 文件位置:/include/linux/type.h
linux定义的链表是双向循环链表,结构体类型定义如下:
struct list_head {
struct list_head *next, *prev;
};
使用者一般在自己结构体内部包含一个list_head成员,例如:
struct s3c_pl330_chan {
unsigned long sdaddr;
struct list_head node;
struct pl330_req *lrq;
struct list_head xfer_list;
};
上面s3c_pl330_chan这个结构体内部有两个struct list_head类型成员,说明s3c_pl330_chan这个结构体会同时出现在两个链表中。
2. 直接用list_head类型子成员初始化链表,初始只有ch节点自己,prev和next指针域都指向自己:
struct s3c_pl330_chan *ch;
INIT_LIST_HEAD(&ch->xfer_list);
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
使用宏定义LIST_HEAD建立一个名称为name的全局链表,初始只有一个head节点,prev和next指针域都指向自己:
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/*建立链表chan_list*/
static LIST_HEAD(chan_list);
3. 添加链表节点,可加在链表头或尾部,删除和添加类似,这里不详细说明
ch = kmalloc(sizeof(*ch), GFP_KERNEL);
/*添加到链表头*/
list_add(&ch->node, &chan_list);
/*添加到链表尾*/
list_add_tail(&ch->node, &chan_list);
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
4. 以上都比较容易理解,下面说说链表的使用:
struct s3c_pl330_chan *ch;
list_for_each_entry(ch, &chan_list, node)
if (ch->id == id)
return ch;
这段代码作用是:以ch类型作为父类型,node作为其子成员,以&chan_list为链表头头指针向next遍历,并返回ch类型指针
然后通过id来匹配是否是需要查找的节点
list_for_each_entry(pos, head, member)是一个for(初始条件;条件判断;块结束语句);循环
初始条件语句:通过list_entry获得链表chan_list之后的第一个节点,并返回父类型s3c_pl330_chan节点指针
条件判断语句:替换后就是 &ch.node != (&chan_list);如果当前s3c_pl330_chan父类型节点的node链表指针不是&chan_list头指针,说明链表还没有遍历完
块结束语句:pos指向当前节点的下一个节点
#define list_for_each_entry(pos, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member); \
&pos->member != (head); \
pos = list_entry(pos->member.next, typeof(*pos), member))
再说明一下list_entry实现:
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr - offsetof(type,member) );})
主要就是container_of的定义,这个宏的作用是:已知成员member在type中的偏移地址和member在内存中的地址,获取父类型type的内存地址
可以理解有这么一个结构体,ptr是一个member类型的指针,指向member在内存中的地址
struct type {
member0;
member1;
member; //ptr是一个member类型的指针,指向member在内存中的地址
member3;
};