在上一篇我们介绍了list_head;并定义了结构体节点,如何把该类型的结构体节点串成一个链表呢?
1、必须专门定义一个专有链表头,并初始化:
struct list_head ListHead;
INIT_LIST_HEAD(&ListHead);
其中INIT_LIST_HEAD的定义为
#define INIT_LIST_HEAD(ptr) do { \
(ptr)->next = (ptr); \
(ptr)->prev = (ptr); \
} while (0)
2、用list_add/list_add_tail 来添加链表节点
list_add(&entry, &ListHead);
其中list_add,list_add_tail定义为
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
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;
}
void list_add_tail(struct list_head *new, struct list_head *head);
static __inline__ void list_add_tail(struct list_head *_new, struct list_head *head)
{
__list_add(_new, head->prev, head);
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
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;
}
offsetof(TYPE, MEMBER) ((size_t)&((TYPE *)0)->MEMBER)
理解offsetof的关键在于&((TYPE *)0)->MEMBER,几乎可以说只要理解了这一部分,后面的几个函数都能够解决,那么我们看看这一部分究竟完成了怎样的工作。根据优先级的顺序,最里面的小括号优先级最高,TYPE *将整型常量0强制转换为TYPE型的指针,且这个指针指向的地址为0,也就是将地址0开始的一块存储空间映射为TYPE型的对象,接下来再对结构体中MEMBER成员进行取址,而整个TYPE结构体的首地址是0, 这里获得的地址就是MEMBER成员在TYPE中的相对偏移量 。再将这个偏移量强制转换成size_t型数据(无符号整型)。
/*
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); /
(type *)( (char *)__mptr - offsetof(type,member) );})
/*list_entry /**
* list_entry - get the struct for this entry
* @ptr: the &;struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_struct within the struct.
*/
#define list_entry(ptr, type, member) /
container_of/(ptr,type,member)
功能等同于container_of。接下来分析我们最终想要知道的list_for_each_entry的实现过程。
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#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的基础上分析list_for_each_entry本来是一件比较轻松的事情,但在这里还是要强调一下双向链表及链表头的概念,否则对list_for_each_entry的理解还是一知半解。建立一个双向链表通常有一个独立的用于管理链表的链表头,链表头一般是不含有实体数据的,必须用INIT_LIST_HEAD()进行初始化,表头建立以后,就可以将带有数据结构的实体链表成员加入到链表中,链表头和链表的关系如图所示:
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) /
for (pos = list_entry((head)->next, typeof(*pos), member), /
n = list_entry(pos->member.next, typeof(*pos), member); /
&pos->member != (head); /
pos = n, n = list_entry(n->member.next, typeof(*n), member))
相比于list_for_each_entry,list_for_each_entry_safe用指针n对链表的下一个数据结构进行了临时存储,所以如果在遍历链表的时候可能要删除链表中的当前项,用list_for_each_entry_safe可以安全的删除,而不会影响接下来的遍历过程(用n指针可以继续完成接下来的遍历, 而list_for_each_entry则无法继续遍历)。