linux kernel --- struct list_head 结构

本文深入解析Linux内核中struct list_head结构的定义、初始化、常用操作函数,包括list_add_tail、list_add、list_for_entry等,并介绍了如何在实际使用中进行链表的插入、遍历和删除。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内核中采用链表来管理对象,一般都是将链表头嵌入到数据结构中。通过链表头链接可以算出各数据结构的地址。
以下分析的源码是kernel4.14。

一.、内核中链表的结构定义

struct list_head {
	struct list_head *next, *prev;
};

list_head结构只有两个成员:next和prev

二、链表头初始化

有两种方法可以初始化一个链表头节点,前提是定义了一个链表头 struct list_head mylist;

1、 LIST_HEAD(mylist);

#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

2、 INIT_LIST_HEAD(&mylist);

static inline void INIT_LIST_HEAD(struct list_head *list)
{
	list->next = list;
	list->prev = list;
}

由此可见,链表初始化后,next和prev都是mylist的地址,也就是说next和prev都是指向自己的.

三、 链表常用函数

1、 list_add_tail(new, head)
把new增加到head左边,也就是add 到之前存在list的末尾
从下图可以看出,(是双向链表,双箭头)

在这里插入图片描述

static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}
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;
}

2、 list_add(new, head)
把new增加到head右边,也就是add 到head的后面
从下图可以看出,(是双向链表,双箭头)

在这里插入图片描述

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;
}

2、list_for_entry系列
(1) list_for_each_entry
遍历链表找到自己需要的

#define list_for_each_entry(pos, head, member)				\
	for (pos = list_first_entry(head, typeof(*pos), member);	\
	     &pos->member != (head);					\
	     pos = list_next_entry(pos, member))

for循环的3个参数:
第1个: 已知pos结构体的的成员member的地址head->next,求出pos结构体的首地址赋值给pos
第2个: 遍历到第1个head停止
第3个: 遍历下一个list
(2) list_for_each_entry_safe

#define list_for_each_entry_safe(pos, n, head, member)			\
  	for (pos = list_first_entry(head, typeof(*pos), member),	\
  		n = list_next_entry(pos, member);			\
  	     &pos->member != (head); 					\
  	     pos = n, n = list_next_entry(n, member))

(3) list_first_entry

#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

已知结构体type的成员member的地址(ptr)->next,求解结构体type的起始地址。
(4) list_entry

#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

已知结构体type的成员member的地址ptr,求解结构体type的起始地址。
(5) list_for_each

#define list_for_each(pos, head) \
	for (pos = (head)->next; pos != (head); pos = pos->next)

3、 list_del
通过将prev和next互指来删除一个节点

static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
    next->prev = prev;
    prev->next = next;
}

四、使用方法:

1、定义结构体和链表头

struct sender_first {
	int from;
	int to;
	struct list_head sender_list;
}
struct sender_second {
	int two;
	struct list_head list;
}

2、初始化链表头

struct sender_first *sender_first;
struct sender_second *sender_second;
INIT_LIST_HEAD(&sender_first->sender_list);

3、把新的链表接到head上去

list_add_tail(&sender_second->list, &sender_first->sender_list);

4、遍历

list_for_each_entry(sender_second, &sender_first->sender_list, list) {
	if (sender_second->two)
		break;
}

for循环第1个参数: 已知 struct sender_second中的成员list的地址:sender_first->sender_list->next(之前list_add连接上的),返回的是struct sender_second的首地址。
找到struct sender_second首地址之后,判断他的成员two

5、删除链表

list_del(&sender_second->list);

删除链表sender_second->list 与sender_first->sender_list的连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值