Linux- struct list_head简介

Linux-从链表中删除一个结点 判断链表是否为空遍历链表例
简介
在Linux内核中,提供了一个用来创建双向循环链表的结构。虽然linux内核是用C语言写的,但是的引入,使得内核数据结构也可以拥有面向对象的特性,通过使用操作 的通用接口很容易实现代码的重用 。
Linux内核中的链表方式与众不同,他不是将数据结构塞入链表,而是将链表结点塞入数据结构 。链表代码在中声明 。
首先找到结构体定义,/linux/list.h 如下:
struct list_head {struct list_head *next, *prev;};
next指向下一个链表结点,prev指向前一个链表结点 。本身并没有意义,他需要嵌入到你自己的数据结构中才有效 。
需要注意的一点是,头结点head是不使用的,这点需要注意 。
使用组织的链表的结构如下图所示:
然后就开始围绕这个结构开始构建链表,然后插入、删除节点 ,遍历整个链表等等,其实内核已经提供好了现成的接口,接下来就让我们进入 //linux/list.h中:
定义一个链表
这里我们随便创建一个例子:
struct qingmu{unsigned int age;unsigned int weight;struct list_head list;};
初始化链表
内核提供了下面的这些接口来初始化链表:
//静态初始化#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){list->next = list;list->prev = list;}
动态初始化:
struct qingmu*mu;mu = kmalloc(sizeof(struct *qingmu), GPL_KERNEL);mu->age = 18;mu->weight = 60;INIT_LIST_HEAD(&mu->list);
如果一个结构在编译期静态创建,那么:
【Linux- struct list_head简介】structqingmu mu{.age = 18.weight = 60;.list = LIST_HEAD_INIT(mu.list); };
将上述分解开来如下:
#define LIST_HEAD_INIT(name) { &(name), &(name) }#define LIST_HEAD_INIT(mu.list) { &(mu.list), &(mu.list) }mu.list.prev=&mu.list;mu.list.next=&mu.list;
上述两种方式都可以把现有的数据结构改造成链表 。
链表头

Linux- struct list_head简介

文章插图
在上述的链表中我们假如有很多的结点,每一个节都都包涵有一个指针,于是我们就可以从任何一个结点气遍历链表,直到我们看到所有的结点 。不过有的时候,我们需要一个特殊的指针所引导整个链表,而不是从一个链表结点出发 。那么就有了头结点的说法,也就是一个常规的:
#define LIST_HEAD(name) \//链表头struct list_head name = LIST_HEAD_INIT(name)
例如:
structlist_head myhead; LIST_HEAD(myhead)/INIT_LIST_HEAD(&myhead);直接定义并初始化链表 。
这样我们的链表就初始化完毕,链表头的就prev 和 next指针分别指向自己了,如下图:
添加结点
给链表增加一个结点:
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;}
从上面的函数可以看出,把新结点(new)插入到head结点后面 。
和上面的例子相结合,此时我们已经创建了mu结点并初始化完成,我们需要在mu结点后面加一个结点: