Linux内核的双向链表

内核的双向链表,搞清楚以下两点就明白了:

1. 把链表节点嵌入到数据结构中,而不是在链表节点中嵌入数据成员。

比如,我们一般习惯这么定义链表:

struct city {
	char name[128];
	char province[128];
	int  population;
	struct city *next;
	strcut city *prev;
};

内核里面的用法:

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

struct city {
	char name[128];
	char province[128];
	int  population;
	struct list_head list;
};

2. 从上面的内核定义,思考一个问题,如果拿到一个变量  struct city xi_an,我们如何遍历链表中所有的元素?我们只能访问到xi_an.list.next和xi_an.list.prev这两个指针.内核用法的巧妙之处就在于此.当已知一个数据结构变量的某个成员指针时,因为该数据结构变量在编译时,其每个成员的相对地址是固定的,因此,可以通过offset,推导出其他成员的指针.内核定义的container_of()就完成了这个工作.

#define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({				\
	void *__mptr = (void *)(ptr);					\
	((type *)(__mptr - offsetof(type, member))); })

用法:list_entry(&xi_an.list, struct city, list);  就得到了指向struct city xi_an的指针.


如下这段示例代码,从内核链表中提取了几个关键的实现函数,并演示了如何使用:

/*
 * A simple doubly linked list implementation. Refer to linux/list.h
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


#define offsetof(TYPE, MEMBER)	((size_t)&((TYPE *)0)->MEMBER)

/**
 * container_of - cast a member of a structure out to the containing structure
 * @ptr:	the pointer to the member.
 * @type:	the type of the container struct this is embedded in.
 * @member:	the name of the member within the struct.
 *
 */
#define container_of(ptr, type, member) ({				\
	void *__mptr = (void *)(ptr);					\
	((type *)(__mptr - offsetof(type, member))); })

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

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

/**
 * list_add - add a new entry
 * @new: new entry to be added
 * @head: list head to add it after
 *
 * Insert a new entry after the specified head.
 * This is good for implementing stacks.
 */
static inline void list_add(struct list_head *new, struct list_head *head)
{
	struct list_head *head_next = head->next;

	new->next = head_next;
	new->prev = head;
	
	head->next = new;

	head_next->prev = new;	
}

/**
 * list_add_tail - add a new entry
 * @new: new entry to be added
 * @head: list head to add it before
 *
 * Insert a new entry before the specified head.
 * This is useful for implementing queues.
 */
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
	struct list_head *head_prev = head->prev;

	new->next = head;
	new->prev = head_prev;

	head->prev = new;

	head_prev->next = new;
}

static inline void list_del(struct list_head *entry)
{
	struct list_head *prev = entry->prev;
	struct list_head *next = entry->next;

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

	entry->next = NULL;
	entry->prev = NULL;
}

/**
 * list_entry - get the struct for this entry
 * @ptr:	the &struct list_head pointer.
 * @type:	the type of the struct this is embedded in.
 * @member:	the name of the list_head within the struct.
 */
#define list_entry(ptr, type, member) \
	container_of(ptr, type, member)

/**
 * list_for_each_entry	-	iterate 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.
 */
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_entry((head)->next, typeof(*pos), member);	\
	     &pos->member != (head);					\
	     pos = list_entry(pos->member.next, typeof(*pos), member))

/**
 * list_for_each_entry_reverse - iterate backwards 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.
 */
#define list_for_each_entry_reverse(pos, head, member)			\
	for (pos = list_entry((head)->prev, typeof(*pos), member);		\
	     &pos->member != (head); 					\
	     pos = list_entry(pos->member.prev, typeof(*pos), member))


/* Define the real struct with struct list_head */
struct city_name {
	struct list_head list;
	char name[128];
};

int main()
{
	struct city_name city1;
	struct city_name city2;
	struct city_name city3;
	struct city_name city4;
	struct city_name city5;
	struct city_name *pos;

	memset(&city1, 0, sizeof(struct city_name));
	memset(&city2, 0, sizeof(struct city_name));
	memset(&city3, 0, sizeof(struct city_name));
	memset(&city4, 0, sizeof(struct city_name));
	memset(&city5, 0, sizeof(struct city_name));

	strcpy(city1.name, "First: Beijing");
	strcpy(city2.name, "Second: Shanghai");
	strcpy(city3.name, "Third: Guangzhou");
	strcpy(city4.name, "Fourth: Shenzhen");
	strcpy(city5.name, "Fiveth: Xian");

	/* Init list head */
	INIT_LIST_HEAD(&city1.list);
	/* Add elements to list */
	list_add(&city2.list, &city1.list);
	list_add_tail(&city3.list, &city1.list);
	list_add_tail(&city4.list, &city1.list);
	list_add_tail(&city5.list, &city1.list);

	/* Get the first element by list */
	pos = list_entry(&city1.list, struct city_name, list);
	printf("%s\n", pos->name);
	/* Output */
	list_for_each_entry(pos, &city1.list, list) {
		printf("%s\n", pos->name);
	}
	printf("Reverse:\n");
	list_for_each_entry_reverse(pos, &city1.list, list) {
		printf("%s\n", pos->name);
	}

	/* Delete */
	printf("Delete Third: Guangzhou\n");
	list_del(&city3.list);
	
	/* Output */
	printf("Output:\n");
	list_for_each_entry(pos, &city1.list, list) {
		printf("%s\n", pos->name);
	}
	printf("Reverse:\n");
	list_for_each_entry_reverse(pos, &city1.list, list) {
		printf("%s\n", pos->name);
	}

	return 0;
}


猜你喜欢

转载自blog.csdn.net/weixin_42263483/article/details/80779272