欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 房产 > 建筑 > Linux内核链表

Linux内核链表

2025/4/28 5:01:15 来源:https://blog.csdn.net/u012041204/article/details/147410552  浏览:    关键词:Linux内核链表

一、简介

Linux 内核使用了一种 双向循环链表 作为基础的数据结构,主要用于管理各种内核对象,如进程、任务、设备等。它的实现位于 include/linux/list.h 头文件中。

二、链表结构定义

struct list_head {struct list_head *next, *prev;
};
  • next 指向下一个节点
  • prev 指向上一个节点
  • 由于是 双向循环链表,最后一个节点的 next 指向头节点,头节点的 prev 指向最后一个节点

三、初始化链表

#define LIST_HEAD_INIT(name) { &(name), &(name) }static inline void INIT_LIST_HEAD(struct list_head *list)
{WRITE_ONCE(list->next, list);list->prev = list;
}

前者直接传入链表变量,然后返回给变量。后者要先定义链表变量,然后再传入。

示例:

//第一种方式
struct list_head my_list = LIST_HEAD_INIT(my_list);//第二种方式
struct list_head my_list;
INIT_LIST_HEAD(&my_list);

四、链表操作

假设我们有一个包含 list_head 结构的自定义结构:

struct my_struct {int data;struct list_head list;
};

4.1. 插入节点

/*
** new: 要插入的节点
** head: 插入的节点位于该节点后面
*/
void list_add(struct list_head *new, struct list_head *head)
/*
** new: 要插入的节点
** head: 插入的节点位于该节点前面
** 注意: 千万别被 list_add_tail中的 tail 误导了,它只有 head 是链表头时,才是在链表尾部插入,否则它是插入 head 的前面。
**      但是当你用双向循环链表的方式去理解的,其实它就是在head前面插入。
*/
void list_add_tail(struct list_head *new, struct list_head *head)

头部插入

struct list_head my_list = LIST_HEAD_INIT(my_list);
list_add(&new_node->list, &my_list);
  • list_add()my_list 之后 插入新节点
  • my_list 仍然是头节点,链表顺序:my_list → new_node → …

尾部插入

list_add_tail(&new_node->list, &my_list);
  • list_add_tail()my_list 之前 插入新节点
  • my_list 仍然是头节点,链表顺序:my_list → … → new_node

4.2. 删除节点

list_del(&node->list);
  • 只从链表中移除节点,但不释放内存
  • 需要 kfree() 释放节点内存(如果是动态分配的)

4.3. 遍历链表

/*
** pos: 用于遍历的临时指针,指向包含list_head的自定义结构体
** head: 链表头指针,类型为struct list_head *
** member: 自定义结构体中list_head的成员名
*/
#define list_for_each_entry(pos, head, member)/*
** pos: 用于遍历的临时指针,指向包含list_head的自定义结构体
** n: 临时保存下一个节点,用于遍历时避免因删除而出错
** head: 链表头指针
** member: 自定义结构体中list_head的成员名
*/
#define list_for_each_entry_safe(pos, n, head, member)

普通遍历:

struct my_struct {int data;struct list_head list;
};struct list_head my_list;
INIT_LIST_HEAD(&my_list);// 遍历
struct my_struct *pos;
list_for_each_entry(pos, &my_list, list) {printk(KERN_INFO "Data = %d\n", pos->data);
}

安全遍历(删除时使用)

struct my_struct *pos, *n;
list_for_each_entry_safe(pos, n, &my_list, list) {if (pos->data == 123) {list_del(&pos->list);  // 安全删除当前节点kfree(pos);}
}

如果经常操作内核链表,建议默认使用 *_safe 版本,防止一些野指针和崩溃问题。

4.4. 判断链表是否为空

if (list_empty(&my_list)) {printk(KERN_INFO "List is empty\n");
}

4.5. 获取结构体指针

struct my_struct *entry = list_entry(ptr, struct my_struct, list);
  • ptrlist_head 指针
  • struct my_struct 是目标结构体
  • listmy_struct 中的 list_head 成员名称

五、总结

  1. 通常我们要自定义一个结构体,然后结构体中包含 struct list_headlist_head 本质上就是链表中的一个节点,而且是一个 通用节点结构,用来把自己的结构体组织成链表。可以把 struct list_head 理解为“钩子”,用来将节点一个个挂到链表上。
  2. 链表的头(head) 是一个 struct list_head不代表任何实际数据
&my_list  -->  item1->list  <-->  item2->list  <-->  item3->list▲                     ▲                 ▲│                     │                 │item1                 item2             item3(struct my_struct)   (struct my_struct)   (struct my_struct)

六、链表示例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/list.h>struct my_node {int data;struct list_head list;  // 链表节点
};static LIST_HEAD(my_list);  // 定义并初始化链表头// 模块加载时执行
static int __init list_demo_init(void)
{int i;struct my_node *node;printk(KERN_INFO "list Demo module init!\n");// 添加 5 个节点for (i = 0; i < 5; i++) {node = kmalloc(sizeof(*node), GFP_KERNEL);if (!node)return -ENOMEM;node->data = i;list_add_tail(&node->list, &my_list);  // 添加到链表尾部printk(KERN_INFO "add node:data = %d\n", node->data);}// 遍历链表printk(KERN_INFO "iterator list:\n");list_for_each_entry(node, &my_list, list) {printk(KERN_INFO "node data = %d\n", node->data);}return 0;
}// 模块卸载时执行
static void __exit list_demo_exit(void)
{struct my_node *node, *tmp;printk(KERN_INFO "list Demo module exit...\n");list_for_each_entry_safe(node, tmp, &my_list, list) {printk(KERN_INFO "delete node:data = %d\n", node->data);list_del(&node->list);  // 从链表中删除kfree(node);            // 释放内存}
}module_init(list_demo_init);
module_exit(list_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("yang");
MODULE_DESCRIPTION("a list demo");

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

热搜词