问题描述:
linux内核中链表的基本使用:
使用LIST_HEAD宏定义链表头
使用list_add向链表添加节点
使用list_for_each_entry遍历链表
使用list_del从链表删除节点
使用list_empty检查链表是否为空
使用list_for_each_entry_safe安全遍历(在删除节点时使用)
日志
添加打印日志信息
分析步骤
第1步:
第2步:
...
代码片段
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
// 定义链表节点结构 - list成员位置示例(此处放在中间)
struct my_node {
int data; // 节点数据
int extra_info; // 额外信息,演示list成员不一定要在最后
struct list_head list; // 链表头,可放在结构体任意位置
};
// 定义链表头 - 全局变量
static LIST_HEAD(my_list);
// 从链表中删除指定数据的节点
static void delete_node_with_data(int target) {
struct my_node *node, *temp;
bool found = false;
// 安全遍历链表(允许删除操作)
list_for_each_entry_safe(node, temp, &my_list, list) {
if (node->data == target) {
printk(KERN_INFO "List demo: Found node with data %d, deleting...\n", target);
/*
static inline void list_del(struct list_head *entry)
struct list_head *entry:指向要从链表中删除的节点中 list_head 成员的指针
*/
list_del(&node->list); // 从链表中移除
kfree(node); // 释放内存
found = true;
break; // 只删除第一个匹配的节点
}
}
if (!found) {
printk(KERN_INFO "List demo: Node with data %d not found\n", target);
} else {
// 删除后打印当前链表状态
printk(KERN_INFO "List demo: List after deletion:");
list_for_each_entry(node, &my_list, list) {
printk(KERN_CONT " %d", node->data);
}
printk(KERN_CONT "\n");
}
}
// 初始化函数 - 插入节点并演示删除
static int __init list_demo_init(void) {
struct my_node *node;
int i;
// 初始化局部链表头
LIST_HEAD(local_my_list);
//使用 INIT_LIST_HEAD 宏(常用于动态初始化)
struct list_head *heap_list = kmalloc(sizeof(struct list_head), GFP_KERNEL);
INIT_LIST_HEAD(heap_list); // 初始化链表头(替代LIST_HEAD)
printk(KERN_INFO "List demo: Initializing\n");
// 向链表中插入5个节点
for (i = 0; i < 5; i++) {
// 分配内存
node = kmalloc(sizeof(struct my_node), GFP_KERNEL);
if (!node) {
printk(KERN_ERR "List demo: Failed to allocate memory\n");
// 清理已分配的节点(简化处理,实际应完整清理)
return -ENOMEM;
}
// 设置节点数据
node->data = i + 1;
node->extra_info = (i + 1) * 10; // 初始化额外信息
/*
1. list_add 函数
作用:将新节点添加到链表的头部(在链表头之后插入)
static inline void list_add(struct list_head *new, struct list_head *head)
struct list_head *new:指向新节点中 list_head 成员的指针(要插入的节点的链表项)
struct list_head *head:指向链表头的指针(要插入到哪个链表)
*/
// 将节点添加到链表头部,从哪里添加到哪里
list_add(&node->list, &my_list);
printk(KERN_INFO "List demo: Added node (data: %d, extra: %d)\n",
node->data, node->extra_info);
}
// 遍历链表并打印所有节点数据
printk(KERN_INFO "List demo: Initial list:");
/*
list_for_each_entry 宏 define list_for_each_entry(pos, head, member)
作用:遍历链表中的所有节点,获取包含 list_head 成员的结构体指针(即完整的节点数据)
pos:循环变量,是指向链表节点结构体的指针(如 struct my_node *node)
head:指向链表头的指针(遍历的目标链表)
member:节点结构体中 list_head 成员的变量名(如 list)
*/
//知道从哪里开始获取到
list_for_each_entry(node, &my_list, list) {
printk(KERN_CONT " %d", node->data);
}
printk(KERN_CONT "\n");
// 演示删除操作:删除data=3的节点
delete_node_with_data(3);
return 0;
}
// 退出函数 - 清理链表
static void __exit list_demo_exit(void) {
struct my_node *node, *temp;
printk(KERN_INFO "List demo: Exiting\n");
// 遍历并删除所有剩余节点
list_for_each_entry_safe(node, temp, &my_list, list) {
printk(KERN_INFO "List demo: Removing node (data: %d, extra: %d)\n",
node->data, node->extra_info);
list_del(&node->list); // 从链表中移除节点
kfree(node); // 释放内存
}
// 验证链表是否为空
if (list_empty(&my_list)) {
printk(KERN_INFO "List demo: List is empty after cleanup\n");
} else {
printk(KERN_ERR "List demo: Error - List is not empty after cleanup\n");
}
}
// 模块注册
module_init(list_demo_init);
module_exit(list_demo_exit);
// 模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Linux kernel list demo with delete operation");
MODULE_VERSION("0.2");
图片
结论
输出结论
待查资料问题
参考链接