内核中采用链表来管理对象,一般都是将链表头嵌入到数据结构中。通过链表头链接可以算出各数据结构的地址。
以下分析的源码是kernel4.14。
一.、内核中链表的结构定义
struct list_head {
struct list_head *next, *prev;
};
list_head结构只有两个成员:next和prev
二、链表头初始化
有两种方法可以初始化一个链表头节点,前提是定义了一个链表头 struct list_head mylist;
1、 LIST_HEAD(mylist);
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
2、 INIT_LIST_HEAD(&mylist);
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
由此可见,链表初始化后,next和prev都是mylist的地址,也就是说next和prev都是指向自己的.
三、 链表常用函数
1、 list_add_tail(new, head)
把new增加到head左边,也就是add 到之前存在list的末尾
从下图可以看出,(是双向链表,双箭头)
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
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;
}
2、 list_add(new, head)
把new增加到head右边,也就是add 到head的后面
从下图可以看出,(是双向链表,双箭头)
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;
}
2、list_for_entry系列
(1) list_for_each_entry
遍历链表找到自己需要的
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
&pos->member != (head); \
pos = list_next_entry(pos, member))
for循环的3个参数:
第1个: 已知pos结构体的的成员member的地址head->next,求出pos结构体的首地址赋值给pos
第2个: 遍历到第1个head停止
第3个: 遍历下一个list
(2) list_for_each_entry_safe
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
&pos->member != (head); \
pos = n, n = list_next_entry(n, member))
(3) list_first_entry
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
已知结构体type的成员member的地址(ptr)->next,求解结构体type的起始地址。
(4) list_entry
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
已知结构体type的成员member的地址ptr,求解结构体type的起始地址。
(5) list_for_each
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
3、 list_del
通过将prev和next互指来删除一个节点
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
四、使用方法:
1、定义结构体和链表头
struct sender_first {
int from;
int to;
struct list_head sender_list;
}
struct sender_second {
int two;
struct list_head list;
}
2、初始化链表头
struct sender_first *sender_first;
struct sender_second *sender_second;
INIT_LIST_HEAD(&sender_first->sender_list);
3、把新的链表接到head上去
list_add_tail(&sender_second->list, &sender_first->sender_list);
4、遍历
list_for_each_entry(sender_second, &sender_first->sender_list, list) {
if (sender_second->two)
break;
}
for循环第1个参数: 已知 struct sender_second中的成员list的地址:sender_first->sender_list->next(之前list_add连接上的),返回的是struct sender_second的首地址。
找到struct sender_second首地址之后,判断他的成员two
5、删除链表
list_del(&sender_second->list);
删除链表sender_second->list 与sender_first->sender_list的连接