Linux 内核中的 list.h
提供了一个 双向链表 实现,包含了一系列 宏和函数,用于高效操作链表。以下是常用的 list_head
相关函数和宏,并附带详细介绍。
🌟 链表结构
struct list_head {
struct list_head *next, *prev;
};
next 指向下一个节点
prev 指向上一个节点
双向循环链表
🔹初始化链表
函数/宏 |
作用 |
INIT_LIST_HEAD(struct list_head *list) |
初始化链表头,list 指向自身 |
LIST_HEAD(name) |
定义并初始化链表头 |
✅ 示例
struct list_head my_list;
INIT_LIST_HEAD(&my_list); // 初始化
// 另一种方式
LIST_HEAD(my_list2); // 直接定义一个初始化好的链表头
🔹添加节点
函数/宏 |
作用 |
list_add(struct list_head new, struct list_head head) |
插入到头部(head 之后) |
list_add_tail(struct list_head new, struct list_head head) |
插入到尾部(head 之前) |
✅ 示例
struct my_node {
int data;
struct list_head list;
};
struct my_node node;
INIT_LIST_HEAD(&node.list);
list_add(&node.list, &my_list); // 插入到头部
list_add_tail(&node.list, &my_list); // 插入到尾部
🔹删除节点
函数/宏 |
作用 |
list_del(struct list_head *entry) |
删除 entry,并不会释放内存 |
list_del_init(struct list_head *entry) |
删除 entry,并重新初始化它 |
✅ 示例
list_del(&node.list); // 删除 node
list_del_init(&node.list); // 删除并重新初始化,防止野指针
🔹判断链表是否为空
函数/宏 |
作用 |
list_empty(const struct list_head *head) |
判断链表是否为空 |
list_empty_careful(const struct list_head *head) |
多线程安全的空判断 |
✅ 示例
if (list_empty(&my_list)) {
printf("链表为空\n");
}
🔹遍历链表
函数/宏 |
作用 |
list_for_each(pos, head) |
遍历 list_head 结构 |
list_for_each_safe(pos, n, head) |
安全遍历(可删除) |
list_for_each_entry(pos, head, member) |
直接获取结构体 |
list_for_each_entry_safe(pos, n, head, member) |
安全遍历结构体(可删除) |
✅ 示例:遍历 list_head
struct list_head *pos;
list_for_each(pos, &my_list) {
struct my_node *node = list_entry(pos, struct my_node, list);
printf("Data: %d\n", node->data);
}
✅ 示例:安全删除遍历
struct list_head *pos, *n;
list_for_each_safe(pos, n, &my_list) {
struct my_node *node = list_entry(pos, struct my_node, list);
list_del(pos);
free(node);
}
✅ 示例:直接获取结构体
struct my_node *node;
list_for_each_entry(node, &my_list, list) {
printf("Data: %d\n", node->data);
}
✅ 示例:安全删除遍历结构体
struct my_node *node, *tmp;
list_for_each_entry_safe(node, tmp, &my_list, list) {
list_del(&node->list);
free(node);
}
🔹获取链表中的元素
函数/宏 |
作用 |
list_entry(ptr, type, member) |
通过 list_head 获取包含它的结构体 |
list_first_entry(head, type, member) |
获取链表中的第一个元素 |
list_last_entry(head, type, member) |
获取链表中的最后一个元素 |
list_next_entry(pos, member) |
获取下一个元素 |
list_prev_entry(pos, member) |
获取上一个元素 |
✅ 示例
struct my_node *first = list_first_entry(&my_list, struct my_node, list);
struct my_node *last = list_last_entry(&my_list, struct my_node, list);
🔹移动和替换
函数/宏 |
作用 |
list_move(new, head) |
移动 new 到 head 头部 |
list_move_tail(new, head) |
移动 new 到 head 尾部 |
list_replace(old, new) |
用 new 替换 old |
list_replace_init(old, new) |
替换并初始化 old |
✅ 示例
list_move(&node.list, &my_list); // 移动到头部
list_replace(&node.list, &node2.list); // 用 node2 替换 node
🔹计算链表偏移
函数/宏 |
作用 |
container_of(ptr, type, member) |
通过成员指针获取结构体指针 |
offsetof(type, member) |
获取成员偏移量 |
✅ 示例
struct my_node *node = container_of(ptr, struct my_node, list);
size_t offset = offsetof(struct my_node, list);
🔹双链表连接
函数/宏 |
作用 |
list_splice(list, head) |
连接 list 到 head 头部 |
list_splice_tail(list, head) |
连接 list 到 head 尾部 |
list_splice_init(list, head) |
连接后清空 list |
list_splice_tail_init(list, head) |
连接 list 到 head 尾部后清空 list |
✅ 示例
LIST_HEAD(new_list);
list_splice(&new_list, &my_list); // 连接两个链表
🌟 总结
类别 |
函数/宏 |
作用 |
初始化 |
INIT_LIST_HEAD()、LIST_HEAD() |
初始化链表 |
添加节点 |
list_add()、list_add_tail() |
添加到头部或尾部 |
删除节点 |
list_del()、list_del_init() |
删除节点 |
遍历链表 |
list_for_each()、list_for_each_safe() |
遍历 list_head |
获取元素 |
list_entry()、list_first_entry() |
获取结构体指针 |
移动/替换 |
list_move()、list_replace() |
移动/替换节点 |
拼接链表 |
list_splice()、list_splice_tail() |
连接链表 |
🔹 Linux 内核链表 list_head
是一个高效的双向循环链表。
🔹 内核链表操作主要通过 list_entry()
和 list_for_each_entry()
遍历并操作数据。
🔹 list_for_each_safe()
用于 安全删除 避免 use-after-free
错误。
🚀 理解这些函数,能更高效地在 Linux 内核编程中操作链表!