1 链表数据结构的定义:
struct list_head {
struct list_head *next, *prev;
};
list_head结构包含两个指向list_head结构的指针prev和next,内核的链表具备双链表功能,实际上,通常它都组织成双循环链表。
这里的list_head没有数据域。在Linux内核链表中,不是在链表结构中包含数据,而是在数据结构中包含链表节点
2 声明和初始化链表
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name)
LIST_HEAD_INIT初始化链表
next、prev指针都为指向自己,因为Linux用头指针的next是否指向自己来判断链表是否为空:
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
3 宏定义操作
#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) );})
先把0强制类型转换成为指向一个type型数据空间的指针,然后取其中的成员member,通过typeof得到这个成员member的类型,然后定义一个指向这个类型数据的指针命名为__mptr,并赋值为ptr;
把上面得到的__mptr减去成员member的偏移量,然后在强制类型转换成指向type型空间的指针,也就是地址,也就是得到了type类型的首地址,也就得到了一个指针,可以用它做“->”运算,得到其结构体中的任意一个值
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
先把0强制类型转换成为指向一个TYPE型数据的指针,然后取其中的MEMBER成员,再取它的地址,最后再强制类型转换成size_t型,就是unsigned long型,也就是MEMBER成员的地址,而该TYPE型数据的首地址为0,所以这个地址也就是MEMBER成员的偏移量,所以offsetof得到的是TYPE类型数据中成员MEMBER的地址偏移量
#define list_first_entry(ptr, type, member) /
list_entry((ptr)->next, type, member)
它是调用了list_entry来定义的,就是找到ptr->next的地址,也就是第一节点的地址
下面给出一些定义,根据上面的内容很容易看懂
#define list_for_each(pos, head) /
for (pos = (head)->next; prefetch(pos->next), pos != (head); /
pos = pos->next)
#define __list_for_each(pos, head) /
for (pos = (head)->next; pos != (head); pos = pos->next)
#define list_for_each_prev(pos, head) /
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); /
pos = pos->prev)