【linux kernel】linux内核数据结构分析之链表

Linux内核中实现了一套经典的链表操作,定义在/include/linux/list.h文件中,本文基于linux内核源码6.2.7,记录了其常用操作链表的API函数,便于在阅读linux内核源码时更好的理解程序执行的过程和细节。

文章目录

  • 一、链表数据结构
  • 二、链表操作函数
    • (2-1)初始化链表头
    • (2-2)添加条目(向前/向后添加)
    • (2-3)删除链表条目
    • (2-4)替换条目
    • (2-5)带初始化的删除操作
    • (2-6)移动操作
    • (2-7)测试某个条目是否是链表中的第一个条目
    • (2-8)测试某个条目是否是链表中的最后一个条目
    • (2-9)判断一个条目是否是链表头部
    • (2-10)测试链表是否为空
    • (2-11) 从链表中删除条目并重新初始化
    • (2-12)测试链表是否为空且未被修改
    • (2-13)将链表向左旋转
    • (2-14)测试链表是否只有一个条目
    • (2-15)链表分割操作
    • (2-16)链表合并操作
    • (2-17)获取包含某结构体的条目
    • (2-18)从链表中获取第一个元素
    • (2-19)从链表中获取最后一个元素
    • (2-20)从链表中获取第一个元素
    • (2-21)获取链表中的下一个元素
    • (2-22)list_next_entry_circular
    • (2-23)获取list中的前一个元素
    • (2-24)遍历链表
    • (2-25)以rcu安全的方式遍历链表
    • (2-26)list_for_each_continue
    • (2-27)向后迭代链表 - list_for_each_prev
    • (2-28)list_for_each_safe
    • (2-29)逆向遍历双向链表(安全)
    • (2-30)迭代给定类型的链表
    • (2-31)在给定类型的链表上向后迭代
    • (2-32)继续迭代给定类型的链表
    • (2-33)从给定点向后迭代
    • (2-34)从当前点开始遍历给定类型的链表
    • (2-35)list_for_each_entry_from_reverse
    • (2-36)list_for_each_entry_safe
    • (2-37)list_for_each_entry_safe_continue
    • (2-38)list_for_each_entry_safe_from
    • (2-39)list_for_each_entry_safe_reverse
  • 三、总结

一、链表数据结构

定义在/include/linux/types.h文件中:

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

二、链表操作函数

定义在文件/include/linux/list.h中,下文是操作链表的API,这些API在Linux内核中各个组成子系统中几乎都会看见其声影。

(2-1)初始化链表头

#define LIST_HEAD_INIT(name) { &(name), &(name) }

#define LIST_HEAD(name) \
	struct list_head name = LIST_HEAD_INIT(name)

/**
 * INIT_LIST_HEAD - 初始化list_head结构
 * @list: 要初始化的List_head结构。
 *
 * 初始化list_head,使其指向自身。如果它是一个链表头,结果是一个空链表。
 */
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	WRITE_ONCE(list->next, list);
	WRITE_ONCE(list->prev, list);
}

(2-2)添加条目(向前/向后添加)

#ifdef CONFIG_DEBUG_LIST
extern bool __list_add_valid(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next);
extern bool __list_del_entry_valid(struct list_head *entry);
#else
static inline bool __list_add_valid(struct list_head *new,
				struct list_head *prev,
				struct list_head *next)
{
	return true;
}
static inline bool __list_del_entry_valid(struct list_head *entry)
{
	return true;
}
#endif

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next)
{
	if (!__list_add_valid(new, prev, next))
		return;

	next->prev = new;
	new->next = next;
	new->prev = prev;
	WRITE_ONCE(prev->next, new);
}

/**
 * list_add - 添加一个新的条目(头部后方)
 * @new: 要添加的新条目
 * @head: 链表头部
 *
 * 在指定的头之后插入一个新表项。这对实现堆栈很有好处。
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	__list_add(new, head, head->next);
}


/**
 * list_add_tail - 添加一个新的条目(头部前方)
 * @new: 要添加的新条目
 * @head: 链表头部
 *
 * 在指定的头之前插入一个新条目。这对于实现队列很有用。
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	__list_add(new, head->prev, head);
}

(2-3)删除链表条目

/*
 * Delete a list entry by making the prev/next entries
 * point to each other.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
	next->prev = prev;
	WRITE_ONCE(prev->next, next);
}

/*
 * Delete a list entry and clear the 'prev' pointer.
 *
 * This is a special-purpose list clearing method used in the networking code
 * for lists allocated as per-cpu, where we don't want to incur the extra
 * WRITE_ONCE() overhead of a regular list_del_init(). The code that uses this
 * needs to check the node 'prev' pointer instead of calling list_empty().
 */
static inline void __list_del_clearprev(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->prev = NULL;
}

static inline void __list_del_entry(struct list_head *entry)
{
	if (!__list_del_entry_valid(entry))
		return;

	__list_del(entry->prev, entry->next);
}

/**
 * list_del - 从链表中删除条目
 * @entry: 要从链表中删除的元素。
 * Note: 在此之后,List_empty()对条目不返回true,条目返回true处于未定义状态。
 */
static inline void list_del(struct list_head *entry)
{
	__list_del_entry(entry);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}

(2-4)替换条目

/**
 * list_replace - 用新条目替换旧条目
 * @old : 要替换的元素
 * @new :要插入的新元素
 *
 * 如果@old为空,它将被覆盖。
 */
static inline void list_replace(struct list_head *old,
				struct list_head *new)
{
	new->next = old->next;
	new->next->prev = new;
	new->prev = old->prev;
	new->prev->next = new;
}


/**
 * list_replace_init - 用新条目替换旧条目并初始化旧条目。
 * @old : 想要替换的元素
 * @new : 要插入的新元素
 *
 * If @old was empty, it will be overwritten.
 */
static inline void list_replace_init(struct list_head *old,
				     struct list_head *new)
{
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}

/**
 * list_swap - 将entry1替换为entry2,并在entry2的位置重新添加entry1。
 * @entry1: 放置entry2的位置。
 * @entry2: 放置entry1的位置。
 */
static inline void list_swap(struct list_head *entry1,
			     struct list_head *entry2)
{
	struct list_head *pos = entry2->prev;

	list_del(entry2);
	list_replace(entry1, entry2);
	if (pos == entry1)
		pos = entry2;
	list_add(entry1, pos);
}

(2-5)带初始化的删除操作

/**
 * list_del_init - 从链表中删除条目并重新初始化该条目
 * @entry: 要从该链表中删除的元素
 */
static inline void list_del_init(struct list_head *entry)
{
	__list_del_entry(entry);
	INIT_LIST_HEAD(entry);
}

本质是删除和链表初始化的组合

(2-6)移动操作

/**
 * list_move - 从一个列表中删除并添加为另一个链表的头部
 * @list: 要移动的条目
 * @head: 链表头部
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
	__list_del_entry(list);
	list_add(list, head);
}


/**
 * list_move_tail - 从一个链表中删除并添加为另一个链表的尾部
 * @list: 要移动的条目
 * @head: 链表头部
 */
static inline void list_move_tail(struct list_head *list,
				  struct list_head *head)
{
	__list_del_entry(list);
	list_add_tail(list, head);
}


/**
 * list_bulk_move_tail - 将链表的一个分段移动到它的尾部
 * @head: 链表头部
 * @first: 第一个移动的条目
 * @last: 最后一个移动的条目,可以和第一个一样
 *
 * 将@first和@last之间的所有条目移动到@head之前。
 * 所有三个条目必须属于同一个链表。
 */
static inline void list_bulk_move_tail(struct list_head *head,
				       struct list_head *first,
				       struct list_head *last)
{
	first->prev->next = last->next;
	last->next->prev = first->prev;

	head->prev->next = first;
	first->prev = head->prev;

	last->next = head;
	head->prev = last;
}

(2-7)测试某个条目是否是链表中的第一个条目

/**
 * list_is_first -- 测试@list是否是链表@head中的第一个条目
 * @list: 待测试的条目
 * @head: 链表的头部
 */
static inline int list_is_first(const struct list_head *list, const struct list_head *head)
{
	return list->prev == head;
}

(2-8)测试某个条目是否是链表中的最后一个条目


/**
 * list_is_last - 测试@list是否是列表@head中的最后一个条目
 * @list: 待测试的条目
 * @head: 链表的头部
 */
static inline int list_is_last(const struct list_head *list, const struct list_head *head)
{
	return list->next == head;
}

(2-9)判断一个条目是否是链表头部

/**
 * list_is_head - 测试@list是否为链表@head
 * @list: 待测试的条目
 * @head: 链表的头部
 */
static inline int list_is_head(const struct list_head *list, const struct list_head *head)
{
	return list == head;
}

(2-10)测试链表是否为空

/**
 * list_empty - 测试链表是否为空
 * @head: 待测试的链表
 */
static inline int list_empty(const struct list_head *head)
{
	return READ_ONCE(head->next) == head;
}

(2-11) 从链表中删除条目并重新初始化

/**
 * list_del_init_careful - 从链表中删除条目并重新初始化它。
 * @entry: 要从链表中删除的元素。
 *
 *这与list_del_init()相同,只是被设计为与list_empty_caution()一起使用,以保证排序的其他内存操作。
 *
 *在list_del_init_caution()之前执行的任何内存操作都是保证在list_del_init_careful()测试后。
 */
static inline void list_del_init_careful(struct list_head *entry)
{
	__list_del_entry(entry);
	WRITE_ONCE(entry->prev, entry);
	smp_store_release(&entry->next, entry);
}

(2-12)测试链表是否为空且未被修改

/**
 * list_empty_careful - 测试链表是否为空且未被修改
 * @head: 待测试的链表
 *
 * Description:
 * tests whether a list is empty _and_ checks that no other CPU might be
 * in the process of modifying either member (next or prev)
 *
 * NOTE: using list_empty_careful() without synchronization
 * can only be safe if the only activity that can happen
 * to the list entry is list_del_init(). Eg. it cannot be used
 * if another CPU could re-list_add() it.
 */
static inline int list_empty_careful(const struct list_head *head)
{
	struct list_head *next = smp_load_acquire(&head->next);
	return list_is_head(next, head) && (next == READ_ONCE(head->prev));
}

(2-13)将链表向左旋转

/**
 * list_rotate_left - 将链表向左旋转
 * @head: 链表的头
 */
static inline void list_rotate_left(struct list_head *head)
{
	struct list_head *first;

	if (!list_empty(head)) {
		first = head->next;
		list_move_tail(first, head);
	}
}

/**
 * list_rotate_to_front() - 旋转链表到特定的项目
 * @list: The desired new front of the list.
 * @head: 链表的头。
 *
 * 旋转链表,使@list成为链表的新前端。
 */
static inline void list_rotate_to_front(struct list_head *list,
					struct list_head *head)
{
	/*
	 * Deletes the list head from the list denoted by @head and
	 * places it as the tail of @list, this effectively rotates the
	 * list so that @list is at the front.
	 */
	list_move_tail(head, list);
}

(2-14)测试链表是否只有一个条目

/**
 * list_is_singular - 测试链表是否只有一个条目。
 * @head:待测试的链表
 */
static inline int list_is_singular(const struct list_head *head)
{
	return !list_empty(head) && (head->next == head->prev);
}

static inline void __list_cut_position(struct list_head *list,
		struct list_head *head, struct list_head *entry)
{
	struct list_head *new_first = entry->next;
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry;
	entry->next = list;
	head->next = new_first;
	new_first->prev = head;
}

(2-15)链表分割操作

/**
 * list_cut_position -把链表分成两份
 * @list: 添加所有已删除条目的新链表
 * @head:带有条目的链表
 * @entry: an entry within head, could be the head itself
 *	and if so we won't cut the list
 *
 * This helper moves the initial part of @head, up to and
 * including @entry, from @head to @list. You should
 * pass on @entry an element you know is on @head. @list
 * should be an empty list or a list you do not care about
 * losing its data.
 *
 */
static inline void list_cut_position(struct list_head *list,
		struct list_head *head, struct list_head *entry)
{
	if (list_empty(head))
		return;
	if (list_is_singular(head) && !list_is_head(entry, head) && (entry != head->next))
		return;
	if (list_is_head(entry, head))
		INIT_LIST_HEAD(list);
	else
		__list_cut_position(list, head, entry);
}

/**
 * list_cut_before - 将一链表在给定的条目之前分割成两个部分
 * @list: 将所有被移除的条目添加到其中的新链表
 * @head: 带有条目的链表
 * @entry: 在头部中的一个条目,可以是头部本身。
 *
 * This helper moves the initial part of @head, up to but
 * excluding @entry, from @head to @list.  You should pass
 * in @entry an element you know is on @head.  @list should
 * be an empty list or a list you do not care about losing
 * its data.
 * If @entry == @head, all entries on @head are moved to
 * @list.
 */
static inline void list_cut_before(struct list_head *list,
				   struct list_head *head,
				   struct list_head *entry)
{
	if (head->next == entry) {
		INIT_LIST_HEAD(list);
		return;
	}
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry->prev;
	list->prev->next = list;
	head->next = entry;
	entry->prev = head;
}

(2-16)链表合并操作

static inline void __list_splice(const struct list_head *list,
				 struct list_head *prev,
				 struct list_head *next)
{
	struct list_head *first = list->next;
	struct list_head *last = list->prev;

	first->prev = prev;
	prev->next = first;

	last->next = next;
	next->prev = last;
}


/**
 * list_splice - 连接两个列表,这是为堆栈设计的
 * @list: 要添加的新列表。
 * @head:在第一个列表中添加它的位置。
 */
static inline void list_splice(const struct list_head *list,
				struct list_head *head)
{
	if (!list_empty(list))
		__list_splice(list, head, head->next);
}


/**
 * list_splice_tail - 连接两个链表,每个链表都是一个队列
 * @list: 要添加的新链表
 * @head: 在第一个列表中添加它的位置。
 */
static inline void list_splice_tail(struct list_head *list,
				struct list_head *head)
{
	if (!list_empty(list))
		__list_splice(list, head->prev, head);
}


/**
 * list_splice_init - 连接两个链表并重新初始化空链表。
 * @list: 要添加的新链表
 * @head: 在第一个链表中添加它的位置
 *
 * @list中的链表被重新初始化
 */
static inline void list_splice_init(struct list_head *list,
				    struct list_head *head)
{
	if (!list_empty(list)) {
		__list_splice(list, head, head->next);
		INIT_LIST_HEAD(list);
	}
}


/**
 * list_splice_tail_init - 连接两个链表并重新初始化空链表
 * @list: 要添加的新链表。
 * @head: 在第一个链表中添加它的位置。
 *
 * 每个链表都是一个队列
 * @list中的链表被重新初始化
 */
static inline void list_splice_tail_init(struct list_head *list,
					 struct list_head *head)
{
	if (!list_empty(list)) {
		__list_splice(list, head->prev, head);
		INIT_LIST_HEAD(list);
	}
}

(2-17)获取包含某结构体的条目

list_entry函数宏的作用是根据给定节点的指针和包含它的结构体成员的偏移量,计算并返回包含该节点的结构体的指针。其中,ptr是给定节点的指针,type是包含该节点的结构体类型,member是该节点在结构体中的成员名。


/**
 * list_entry - 获取该条目的结构体
 * @ptr:	&struct list_head指针
 * @type:	嵌入了该结构体的类型。
 * @member:	结构体中list_head的名称。
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

(2-18)从链表中获取第一个元素

list_first_entry函数宏的作用是返回链表的第一个节点所在结构体的指针。ptr是指向链表头部的指针,type是包含节点的结构体类型,member是节点在结构体中的成员名。

/**
 * list_first_entry - 从链表中获取第一个元素
 * @ptr:	要从中获取元素的链表头
 * @type:	含该节点的结构体类型
 * @member:	结构体中list_head的名称
 *
 * 注意,该链表不应该是空的
 */
#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

示例代码:

#include 

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

struct my_struct obj1, obj2, obj3;

// 初始化链表头
LIST_HEAD(my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 获取链表的第一个节点所在结构体的指针
struct my_struct *first_entry = list_first_entry(&my_list, struct my_struct, list);

// 访问第一个节点所在结构体的成员
printk(KERN_INFO "First Data: %d\n", first_entry->data);

在上述示例代码中,通过使用list_first_entry宏,可以获取链表的第一个节点所在struct my_struct结构体的指针,并访问其中的成员数据。这样可以方便地获取链表的第一个元素并进行相关操作。

(2-19)从链表中获取最后一个元素

list_last_entry函数宏的作用是返回链表的最后一个节点所在结构体的指针。

/**
 * list_last_entry - 从列表中获取最后一个元素
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 *
 * 注意,该链表不应该是空的。
 */
#define list_last_entry(ptr, type, member) \
	list_entry((ptr)->prev, type, member)


(2-20)从链表中获取第一个元素

list_first_entry_or_null函数宏的作用是返回链表的第一个节点所在结构体的指针,如果链表为空,则返回空指针。

/**
 * list_first_entry_or_null - 从链表中获取第一个元素
 * @ptr:	the list head to take the element from.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 *
 * Note that if the list is empty, it returns NULL.
 */
#define list_first_entry_or_null(ptr, type, member) ({ \
	struct list_head *head__ = (ptr); \
	struct list_head *pos__ = READ_ONCE(head__->next); \
	pos__ != head__ ? list_entry(pos__, type, member) : NULL; \
})

(2-21)获取链表中的下一个元素

list_next_entry函数宏的作用是返回给定节点的下一个节点所在结构体的指针。pos是给定节点的指针,member是节点在结构体中的成员名。

/**
 * list_next_entry - 获取链表中的下一个元素
 * @pos:	the type * to cursor
 * @member:	the name of the list_head within the struct.
 */
#define list_next_entry(pos, member) \
	list_entry((pos)->member.next, typeof(*(pos)), member)

使用示例:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head *pos;

// 初始化链表头
LIST_HEAD(my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 遍历链表,获取给定节点的下一个节点所在结构体的指针
list_for_each(pos, &my_list) {
    struct my_struct *entry = list_entry(pos, struct my_struct, list);
    // 访问结构体中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);

    // 获取下一个节点所在结构体的指针
    struct my_struct *next_entry = list_next_entry(entry, list);
    if (next_entry != NULL) {
        // 访问下一个节点所在结构体的成员
        printk(KERN_INFO "Next Data: %d\n", next_entry->data);
    }
}

(2-22)list_next_entry_circular

list_next_entry_circular函数宏的作用是返回循环链表中给定节点的下一个节点所在结构体的指针。

/**
 * list_next_entry_circular - get the next element in list
 * @pos:	the type * to cursor.
 * @head:	the list head to take the element from.
 * @member:	the name of the list_head within the struct.
 *
 * Wraparound if pos is the last element (return the first element).
 * Note, that list is expected to be not empty.
 */
#define list_next_entry_circular(pos, head, member) \
	(list_is_last(&(pos)->member, head) ? \
	list_first_entry(head, typeof(*(pos)), member) : list_next_entry(pos, member))

(2-23)获取list中的前一个元素

list_prev_entry,用于获取链表中给定节点的前一个节点所在结构体的指针。

该函数宏接受两个参数:pos是给定节点的指针,member是节点在结构体中的成员名。通过调用list_prev_entry函数宏会获取给定节点的前一个节点的指针,然后将其传递给list_entry宏,计算并返回该节点所在结构体的指针

/**
 * list_prev_entry - 获取list中的前一个元素
 * @pos:	the type * to cursor
 * @member:	the name of the list_head within the struct.
 */
#define list_prev_entry(pos, member) \
	list_entry((pos)->member.prev, typeof(*(pos)), member)

/**
 * list_prev_entry_circular - get the prev element in list
 * @pos:	the type * to cursor.
 * @head:	the list head to take the element from.
 * @member:	the name of the list_head within the struct.
 *
 * Wraparound if pos is the first element (return the last element).
 * Note, that list is expected to be not empty.
 */
#define list_prev_entry_circular(pos, head, member) \
	(list_is_first(&(pos)->member, head) ? \
	list_last_entry(head, typeof(*(pos)), member) : list_prev_entry(pos, member))

(2-24)遍历链表

list_for_each是一个宏定义,在Linux内核中用于遍历双向链表的宏。

list_for_each宏接受两个参数:pos和head。其中,pos是用于迭代遍历链表的临时指针变量,而head是链表的头指针。

使用list_for_each宏可以遍历整个链表,从链表的第一个节点开始,一直迭代到最后一个节点。在每次迭代时,pos指向当前节点的指针。

/**
 * list_for_each	-	iterate over a list
 * @pos:	the &struct list_head to use as a loop cursor.
 * @head:	the head for your list.
 */
#define list_for_each(pos, head) \
	for (pos = (head)->next; !list_is_head(pos, (head)); pos = pos->next)

(2-25)以rcu安全的方式遍历链表

list_for_each_rcu宏接受两个参数:pos和head。其中,pos是用于迭代遍历链表的临时指针变量,而head是链表的头指针。

与普通的list_for_each宏不同的是,list_for_each_rcu宏使用了rcu_dereference函数来读取指针的值,以保证在RCU保护下的安全访问。

使用list_for_each_rcu宏可以在RCU保护下遍历链表,从链表的第一个节点开始,一直迭代到最后一个节点。在每次迭代时,pos指向当前节点的指针。

#define list_for_each_rcu(pos, head)		  \
	for (pos = rcu_dereference((head)->next); \
	     !list_is_head(pos, (head)); \
	     pos = rcu_dereference(pos->next))

(2-26)list_for_each_continue

list_for_each_continue是一个宏定义,在Linux内核中用于继续之前的双向链表遍历的宏。

该宏的定义如下:

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

list_for_each_continue宏接受两个参数:poshead。其中,pos是用于迭代遍历链表的临时指针变量,而head是链表的头指针。

使用list_for_each_continue宏可以在链表遍历过程中继续之前的迭代,从上一个迭代结束的位置的下一个节点开始,直到最后一个节点。在每次迭代时,pos指向当前节点的指针。

以下是一个示例,展示了如何使用list_for_each_continue宏继续之前的链表遍历:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head *pos;

// 初始化链表头
LIST_HEAD(my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 遍历链表,访问前两个节点
list_for_each(pos, &my_list) {
    struct my_struct *entry = list_entry(pos, struct my_struct, list);
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);

    if (entry == &obj2) {
        // 继续之前的遍历,从下一个节点继续
        list_for_each_continue(pos, &my_list);
    }
}

在上述示例中,使用list_for_each_continue宏在链表遍历过程中继续之前的迭代。在第一次迭代中,访问到节点obj2时,调用list_for_each_continue宏继续遍历,跳过了节点obj2,从下一个节点obj3继续迭代。

通过使用list_for_each_continue宏,可以在链表遍历中灵活地控制迭代的流程,例如根据特定条件跳过某些节点,或者在某些节点之后执行额外的操作。这样可以实现更复杂的遍历逻辑和数据处理。

(2-27)向后迭代链表 - list_for_each_prev

list_for_each_prev是一个宏定义,在Linux内核中用于逆向遍历双向链表的宏。

该宏的定义如下:

#define list_for_each_prev(pos, head) \
	for (pos = (head)->prev; !list_is_head(pos, (head)); pos = pos->prev)

list_for_each_prev宏接受两个参数:poshead。其中,pos是用于迭代遍历链表的临时指针变量,而head是链表的头指针。

使用list_for_each_prev宏可以逆向遍历整个链表,从链表的最后一个节点开始,一直迭代到第一个节点。在每次迭代时,pos指向当前节点的指针。

以下是一个示例,展示了如何使用list_for_each_prev宏逆向遍历链表:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head *pos;

// 初始化链表头
LIST_HEAD(my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 逆向遍历链表,访问每个节点
list_for_each_prev(pos, &my_list) {
    struct my_struct *entry = list_entry(pos, struct my_struct, list);
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,使用list_for_each_prev宏逆向遍历了名为my_list的双向链表。在每次迭代中,通过list_entry宏将pos指针转换为节点所在的struct my_struct结构体的指针,并访问其中的成员数据。

通过使用list_for_each_prev宏,可以方便地逆向遍历链表并对每个节点执行相应的操作,例如访问节点的数据、进行计算、更新节点等。

(2-28)list_for_each_safe

list_for_each_safe是一个宏定义,在Linux内核中用于安全地遍历双向链表的宏。

该宏的定义如下:

#define list_for_each_safe(pos, n, head) \
	for (pos = (head)->next, n = pos->next; \
	     !list_is_head(pos, (head)); \
	     pos = n, n = pos->next)

list_for_each_safe宏接受三个参数:posnhead。其中,pos是用于迭代遍历链表的临时指针变量,n是用于保存下一个节点的临时指针变量,而head是链表的头指针。

使用list_for_each_safe宏可以安全地遍历整个链表,从链表的第一个节点开始,一直迭代到最后一个节点。在每次迭代时,pos指向当前节点的指针,而n指向下一个节点的指针。

该宏的安全性体现在它在遍历过程中允许对链表进行删除、插入等修改操作,而不会导致迭代器失效或发生内存访问错误。它通过在每次迭代前保存下一个节点的指针n,以确保即使当前节点被删除或移动,仍然可以安全地继续迭代下一个节点。

以下是一个示例,展示了如何使用list_for_each_safe宏安全地遍历链表并删除节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head *pos, *n;

// 初始化链表头
LIST_HEAD(my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 安全遍历链表并删除节点
list_for_each_safe(pos, n, &my_list) {
    struct my_struct *entry = list_entry(pos, struct my_struct, list);
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);

    // 删除节点
    list_del(pos);
}

在上述示例中,使用list_for_each_safe宏安全地遍历了名为my_list的双向链表,并在每次迭代中删除了当前节点。通过使用安全遍历的方式,即使删除了当前节点,仍然能够正确地获取并迭代下一个节点,而不会导致迭代器失效。

通过使用list_for_each_safe宏,可以在遍历链表的同时进行删除、插入或其他修改操作,确保遍历过程的安全性和正确性。

(2-29)逆向遍历双向链表(安全)

 * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
 * @pos:	the &struct list_head to use as a loop cursor.
 * @n:		another &struct list_head to use as temporary storage
 * @head:	the head for your list.
 */
#define list_for_each_prev_safe(pos, n, head) \
	for (pos = (head)->prev, n = pos->prev; \
	     !list_is_head(pos, (head)); \
	     pos = n, n = pos->prev)

list_for_each_prev_safe宏接受三个参数:pos、n和head。其中,pos是用于迭代遍历链表的临时指针变量,n是用于保存上一个节点的临时指针变量,而head是链表的头指针。

使用list_for_each_prev_safe宏可以安全地逆向遍历整个链表,从链表的最后一个节点开始,一直迭代到第一个节点。在每次迭代时,pos指向当前节点的指针,而n指向上一个节点的指针。

该宏的安全性体现在它在遍历过程中允许对链表进行删除、插入等修改操作,而不会导致迭代器失效或发生内存访问错误。它通过在每次迭代前保存上一个节点的指针n,以确保即使当前节点被删除或移动,仍然可以安全地继续迭代上一个节点。

(2-30)迭代给定类型的链表

list_for_each_entry 是一个宏定义,在 Linux 内核中用于遍历双向链表并访问链表节点的宏。

该宏的定义如下:

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

list_for_each_entry 宏接受三个参数:posheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry 宏可以按顺序遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

以下是一个示例,展示了如何使用 list_for_each_entry 宏遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 遍历链表并访问节点
struct my_struct *entry;
list_for_each_entry(entry, &my_list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,使用 list_for_each_entry 宏遍历了名为 my_list 的双向链表。通过 entry 指针可以访问每个节点所在的结构体,并访问结构体中的成员(例如 data)。

通过使用 list_for_each_entry 宏,可以方便地遍历链表并访问节点,简化了代码的编写过程。

(2-31)在给定类型的链表上向后迭代

list_for_each_entry_reverse 是一个宏定义,在 Linux 内核中用于逆向遍历双向链表并访问链表节点的宏。

该宏定义如下:

#define list_for_each_entry_reverse(pos, head, member)			\
	for (pos = list_last_entry(head, typeof(*pos), member);		\
	     !list_entry_is_head(pos, head, member); 			\
	     pos = list_prev_entry(pos, member))

list_for_each_entry_reverse 宏接受三个参数:posheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry_reverse 宏可以逆序遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

以下是一个示例,展示了如何使用 list_for_each_entry_reverse 宏逆序遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

// 逆序遍历链表并访问节点
struct my_struct *entry;
list_for_each_entry_reverse(entry, &my_list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,使用 list_for_each_entry_reverse 宏逆序遍历了名为 my_list 的双向链表。通过 entry 指针可以访问每个节点所在的结构体,并访问结构体中的成员(例如 data)。

通过使用 list_for_each_entry_reverse 宏,可以方便地逆序遍历链表并访问节点,简化了代码的编写过程。

(2-32)继续迭代给定类型的链表

list_for_each_entry_continue 是一个宏定义,在 Linux 内核中用于在给定位置之后继续遍历双向链表并访问链表节点的宏。

/**
 * list_for_each_entry_continue - continue iteration over list of given type
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 *
 * Continue to iterate over list of given type, continuing after
 * the current position.
 */
#define list_for_each_entry_continue(pos, head, member) 		\
	for (pos = list_next_entry(pos, member);			\
	     !list_entry_is_head(pos, head, member);			\
	     pos = list_next_entry(pos, member))

(2-33)从给定点向后迭代

list_for_each_entry_continue_reverse 是一个宏定义,在 Linux 内核中用于在给定位置之前逆向遍历双向链表并访问链表节点的宏。

/**
 * list_for_each_entry_continue_reverse - iterate backwards from the given point
 * @pos:	the type * to use as a loop cursor.
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 *
 * Start to iterate over list of given type backwards, continuing after
 * the current position.
 */
#define list_for_each_entry_continue_reverse(pos, head, member)		\
	for (pos = list_prev_entry(pos, member);			\
	     !list_entry_is_head(pos, head, member);			\
	     pos = list_prev_entry(pos, member))

list_for_each_entry_continue_reverse 宏接受三个参数:pos、head 和 member。其中,pos 是用于迭代遍历链表节点的指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

(2-34)从当前点开始遍历给定类型的链表

list_for_each_entry_from 是一个宏定义,在 Linux 内核中用于从指定位置开始遍历双向链表并访问链表节点的宏。

该宏的定义如下:

#define list_for_each_entry_from(pos, head, member) 			\
	for (; !list_entry_is_head(pos, head, member);			\
	     pos = list_next_entry(pos, member))

list_for_each_entry_from 宏接受三个参数:posheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry_from 宏可以从指定位置开始遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

以下是一个示例,展示了如何使用 list_for_each_entry_from 宏从指定位置开始遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

struct my_struct *entry;

// 从 obj2 节点开始遍历链表
list_for_each_entry_from(entry, &obj2.list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,使用 list_for_each_entry_from 宏从指定位置 obj2 开始遍历名为 my_list 的双向链表,并访问每个节点。

通过使用 list_for_each_entry_from 宏,可以方便地从指定位置开始遍历链表并访问节点,简化了代码的编写过程。

(2-35)list_for_each_entry_from_reverse

list_for_each_entry_from_reverse 是一个宏定义,在 Linux 内核中用于从指定位置之前逆向遍历双向链表并访问链表节点的宏。

该宏的定义如下:

#define list_for_each_entry_from_reverse(pos, head, member)		\
	for (; !list_entry_is_head(pos, head, member);			\
	     pos = list_prev_entry(pos, member))

list_for_each_entry_from_reverse 宏接受三个参数:posheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry_from_reverse 宏可以从指定位置之前逆向遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

以下是一个示例,展示了如何使用 list_for_each_entry_from_reverse 宏从指定位置之前逆向遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

struct my_struct *entry;

// 从 obj2 节点之前开始逆向遍历链表
list_for_each_entry_from_reverse(entry, &obj2.list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,使用 list_for_each_entry_from_reverse 宏从指定位置 obj2 之前开始逆向遍历名为 my_list 的双向链表,并访问每个节点。

通过使用 list_for_each_entry_from_reverse 宏,可以方便地从指定位置之前逆向遍历链表并访问节点,简化了代码的编写过程。

(2-36)list_for_each_entry_safe

list_for_each_entry_safe 是一个宏定义,在 Linux 内核中用于安全地遍历双向链表并访问链表节点的宏。

该宏的定义如下:

#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);			\
	     !list_entry_is_head(pos, head, member); 			\
	     pos = n, n = list_next_entry(n, member))

list_for_each_entry_safe 宏接受四个参数:posnheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,n 是一个临时指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry_safe 宏可以安全地遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

一个关键的特性是使用了临时变量 n,用于存储当前节点的下一个节点的位置,从而在遍历过程中安全地删除或添加节点。

以下是一个示例,展示了如何使用 list_for_each_entry_safe 宏安全地遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

struct my_struct *entry, *tmp;

// 安全地遍历链表并访问节点
list_for_each_entry_safe(entry, tmp, &my_list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);

    // 在遍历过程中安全删除节点
    list_del(&entry->list);
}

在上述示例中,使用 list_for_each_entry_safe 宏安全地遍历名为 my_list 的双向链表,并访问每个节点。在遍历过程中,可以安全地删除节点,而不会影响遍历的正确性。

通过使用 list_for_each_entry_safe 宏,可以方便地安全地遍历链表并访问节点,确保在遍历过程中可以进行安全的节点操作。

(2-37)list_for_each_entry_safe_continue

list_for_each_entry_safe_continue是一个宏定义,在Linux内核中用于安全地继续遍历双向链表并访问链表节点的宏。

该宏的定义如下:

#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
	for (pos = list_next_entry(pos, member), 				\
		n = list_next_entry(pos, member);				\
	     !list_entry_is_head(pos, head, member);				\
	     pos = n, n = list_next_entry(n, member))

list_for_each_entry_safe_continue 宏接受四个参数:posnheadmember。其中,pos 是用于迭代遍历链表节点的指针变量,n 是一个临时指针变量,head 是链表的头指针,member 是链表节点在结构体中的成员名。

使用 list_for_each_entry_safe_continue 宏可以安全地继续遍历链表中的每个节点,并通过 pos 获取节点所在结构体的指针。宏内部使用了 list_entry 宏,将链表节点指针转换为包含该节点的结构体的指针。

list_for_each_entry_safe 宏类似,list_for_each_entry_safe_continue 宏也使用了临时变量 n,用于存储当前节点的下一个节点的位置,从而在遍历过程中安全地删除或添加节点。

以下是一个示例,展示了如何使用 list_for_each_entry_safe_continue 宏安全地继续遍历链表并访问节点:

#include 

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

struct my_struct obj1, obj2, obj3;
struct list_head my_list;

// 初始化链表头
INIT_LIST_HEAD(&my_list);

// 添加节点到链表中
list_add(&obj1.list, &my_list);
list_add(&obj2.list, &my_list);
list_add(&obj3.list, &my_list);

struct my_struct *entry, *tmp;

// 安全地遍历链表并访问节点
list_for_each_entry_safe(entry, tmp, &my_list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);

    // 在遍历过程中安全删除节点
    list_del(&entry->list);
}

// 继续安全地遍历链表并访问节点
list_for_each_entry_safe_continue(entry, tmp, &my_list, list) {
    // 访问节点中的成员
    printk(KERN_INFO "Data: %d\n", entry->data);
}

在上述示例中,首先使用 list_for_each_entry_safe 宏安全地遍历链表并删除节点。然后,使用 list_for_each_entry_safe_continue 宏继续安全地遍历链表并访问节点,包括在前面遍历过程中没有被删除的节点。

通过使用 list_for_each_entry_safe_continue 宏,可以方便地安全地继续遍历链表并访问节点,确保在遍历过程中可以进行安全的节点操作,并继续遍历链表中的剩余节点。

(2-38)list_for_each_entry_safe_from

从当前安全点开始迭代链表,防止删除

/**
 * list_for_each_entry_safe_from - iterate over list from current point safe against removal
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 *
 * Iterate over list of given type from current point, safe against
 * removal of list entry.
 */
#define list_for_each_entry_safe_from(pos, n, head, member) 			\
	for (n = list_next_entry(pos, member);					\
	     !list_entry_is_head(pos, head, member);				\
	     pos = n, n = list_next_entry(n, member))

(2-39)list_for_each_entry_safe_reverse

该宏用于向后安全迭代链表,防止删除

/**
 * list_for_each_entry_safe_reverse - iterate backwards over list safe against removal
 * @pos:	the type * to use as a loop cursor.
 * @n:		another type * to use as temporary storage
 * @head:	the head for your list.
 * @member:	the name of the list_head within the struct.
 *
 * Iterate backwards over list of given type, safe against removal
 * of list entry.
 */
#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
	for (pos = list_last_entry(head, typeof(*pos), member),		\
		n = list_prev_entry(pos, member);			\
	     !list_entry_is_head(pos, head, member); 			\
	     pos = n, n = list_prev_entry(n, member))

三、总结

在内核中,链表是一种常用的数据结构,用于管理和组织各种内核数据。链表的主要作用包括以下几个方面:

(1)动态存储:链表提供了一种动态存储数据的方式。内核中的数据往往是动态创建和销毁的,链表能够方便地进行插入、删除和重新排序操作,以适应数据的动态变化。

(2)数据组织:链表提供了一种将多个数据元素组织在一起的方式。内核中存在各种数据结构,例如进程控制块(Process Control Block)、文件描述符表(File Descriptor Table)、网络数据结构等,链表可以将这些数据结构进行链接,形成数据的有序组织。

(3)遍历和访问:链表允许按照特定顺序遍历和访问数据。内核中的各种数据结构可能需要进行遍历和访问,链表提供了便捷的方式来遍历链表中的节点,并访问节点中的数据。

(4)插入和删除:链表支持在任意位置插入和删除节点。内核中经常需要动态地插入和删除数据,例如创建和销毁进程、打开和关闭文件等操作,链表提供了灵活的插入和删除操作,使得数据的动态管理更加高效。

(5)搜索和查找:链表可以用于搜索和查找特定的数据。内核中的某些操作需要根据条件搜索特定的数据,例如查找文件描述符、查找网络连接等,链表提供了一种有序的数据组织方式,便于进行搜索和查找。

你可能感兴趣的:(小生聊【linux,kernel】,小生聊【嵌入式linux】,链表,数据结构,linux,list_head)