前言
链表是linux内核中最简单,最普通的数据结构,链表是一种存放和操作可变数量元素的数据结构,链表和静态数组的不同之处在于,它所包含的元素都是动态创建并插入链表的,在编译时不必知道具体需要创建多少个元素,链表元素在内存中无须占用连续内存区,在linux内核中父子兄弟进程之间的联系,platform总线上的设备,input输入子系统都是用链表组织起来的,可见linux内核链表是多么重要!
linux内核链表的位置及依赖
位置:
\include\linux\list.h
依赖:
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/poison.h>
#include <linux/prefetch.h>
特别注意
当我们把linux链表代码移值时记得清除平台相关的代码(GNU C)
1.({})
2. typeof
3. __builtin_prefetch
4. static inline
Linux内核链表的实现
linux内核链表是带头节点的双向循环链表,且头节点为表中成员,头结点的next指向首结点,头节点的prev指向尾结点
内核链表代码在头文件<linux/list.h>中声明,其数据结构很简单
struct list_head {
struct list_head *next, *prev;
};
next指针指向下一个链表节点
pre指针指向前一个
读者就会发问了,那么数据放在哪里呢?其实在linux内核中,链表的方式与众不同,它不是将数据结构塞入链表,而是将链表节点塞入数据结构,简单来说数据放哪里由使用链表的人来自定义了,如果我们使用Linux内核链表,那么我们需要来自定义下链表节点,而且这个链表节点必须包含类型为struct list_head的成员如以下示例
struct Node
{
struct list_head head;
Type1 value;
Type2 value2;
}
linux内核链表的创建及初始化
struct Node
{
struct list_head head;//放在第一个成员的位置
int value;//数据相关的成员
};
int main(void)
{
struct Node l = {0};//创建及初始化
struct list_head* list = (struct list_head*)&l;//是list指针指向的位置就是一个合法的struct list_head类型的变量,比如这里指向了l的head成员
INIT_LIST_HEAD(list);//所以可以调用linux链表中的init函数初始化这个头结点
}
linux内核链表源码剖析
1.INIT_LIST_HEAD
static void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
用来初始化链表头结点list的next和prev指针都指向自己,换句话说在初始化头结点的时候,让自己形成了一个双向循环的链表,
2.list_add(new,head)
在链表头部插入节点
static 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);
}
图解代码
可以看出这样就在链表头部插入一个结点
3.list_add_tail(new,head)
在链表尾部插入节点
static 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_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
图解代码,
可以看出该代码就在链表尾部插入了一个新节点
4.list_del删除元素
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void list_del(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
entry->next = LIST_POISON1;//定义成空指针
entry->prev = LIST_POISON2;//定义成空指针
}
图解代码
可以看出这样就把需要删除的节点从链表中删除了
5.正向遍历链表 list_for_each(pos,head)
#define list_for_each(pos, head) \
for (pos = (head)->next; prefetch(pos->next), pos != (head); \
pos = pos->next)
/**
可以看出遍历的实现是比较简单的,从首节点逐一遍历到尾结点结束
6.逆向遍历链表 list_for_each(pos,head)
*/
#define list_for_each_prev(pos, head) \
for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
pos = pos->prev)
/**
可以看出逆向遍历的实现也是比较简单的,从尾结点逐一遍历到首节点结束
简单使用下linux内核链表
#include <stdio.h>
#include "LinuxList.h"
void list_demo_1()
{
struct Node
{
struct list_head head;
int value;
};
struct Node l = {0};
struct list_head* list = (struct list_head*)&l;
struct list_head* slider = NULL;
int i = 0;
INIT_LIST_HEAD(list);
printf("Insert begin ...\n");
for(i=0; i<5; i++)
{
struct Node* n = (struct Node*)malloc(sizeof(struct Node));
n->value = i;
list_add_tail((struct list_head*)n, list);
}
list_for_each(slider, list)
{
printf("%d\n", ((struct Node*)slider)->value);
}
printf("Insert end ...\n");
printf("Delete begin ...\n");
list_for_each(slider, list)
{
if( ((struct Node*)slider)->value == 3 )
{
list_del(slider);
free(slider);
break;
}
}
list_for_each(slider, list)
{
printf("%d\n", ((struct Node*)slider)->value);
}
printf("Delete end ...\n");
}
void list_demo_2()
{
struct Node
{
int value;
struct list_head head;
};
struct Node l = {0};
struct list_head* list = &l.head;
struct list_head* slider = NULL;
int i = 0;
INIT_LIST_HEAD(list);
printf("Insert begin ...\n");
for(i=0; i<5; i++)
{
struct Node* n = (struct Node*)malloc(sizeof(struct Node));
n->value = i;
list_add(&n->head, list);
}
list_for_each(slider, list)
{
printf("%d\n", list_entry(slider, struct Node, head)->value);
/*
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
*/
}
printf("Insert end ...\n");
printf("Delete begin ...\n");
list_for_each(slider, list)
{
struct Node* n = list_entry(slider, struct Node, head);
if( n->value == 3 )
{
list_del(slider);
free(n);
break;
}
}
list_for_each(slider, list)
{
printf("%d\n", list_entry(slider, struct Node, head)->value);
}
printf("Delete end ...\n");
}
int main()
{
// list_demo_1();
list_demo_2();
return 0;
}
注意demo1和demo2的Node节点中的struct list_head成员的顺序不同,采用的语法也不同
结果为:
4
3
2
1
0
4
2
1
0
额外补充: