首先找到list_head 结构体定义,kernel/inclue/linux/types.h 如下:
struct list_head {
struct list_head *next, *prev;
};
然后就开始围绕这个结构开始构建链表,然后插入、删除节点 ,遍历整个链表等等,其实内核已经提供好了现成的接口,接下来就让我们进入 kernel/include/linux/list.h中:
1创建链表(天生环状链表)
①宏替换
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
②函数实现
static inline void INIT_LIST_HEAD(struct list_head *list)
{
WRITE_ONCE(list->next, list);
list->prev = list;
}
//最初创建的链表指向自己
//只有指针,没有数据则没有意义
初始化有数据的链表={数据+链表指针结构}:
创建宿主结构体:
struct my_task_list {
int val ;
struct list_head mylist;//只包含前后指针
}
实例比如:
struct my_task_list first_task =
{
.val = 1;
.mylist = LIST_HEAD_INIT(first_task.mylist);//宏替换
};
或者
struct my_task_list my_second_task;
my_second_task.val = 2;
INIT_LIST_HEAD(&my_second_task.mylist);
添加节点(依旧是环装)
list_add(struct list_head *new, struct list_head *head)
传参1:struct list_head *new待插入的链表节点
传参2:struct list_head *head在该节点后面插入新节点、
【向head节点后面插入节点】
先来的节点靠后,而后来的节点靠前,“先进后出,后进先出”。所以此种结构类似于 stack“堆栈”, 而 header_task就类似于内核stack中的栈顶指针esp, 它都是紧靠着最后push到栈的元素。
list_add_tail(struct list_head *new, struct list_head *head)
传参1:struct list_head *new待插入的链表节点
传参2:struct list_head *head在该节点前面插入新节点
【该函数类似于向链表尾部插入节点】
list_add_tail类似于队列尾部的放入节点
删除节点(删除后还是环状)//没有free链表结构体??
list_del(struct list_head *entry)//删除链表单元entry
实现:
{
下一个节点的prev=prev
上一个节点的next=next //WRITE_ONCE???
}
链表遍历
从前向后遍历
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
从后向前遍历
/**
* list_for_each_prev - iterate over a list backwards
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; pos != (head); pos = pos->prev)
而且,list.h 中也提供了list_replace( 节点替换) list_move(节点移位) ,翻转,查找等接口,这里就不在一一分析了。