!!! Linux内核链表
无    2017-02-27 13:15:04    1459    1    0
simon88

 

1.内核链表中的结构体定义

/*************************************************************************
    > File Name: double_link.c
    > Author:卢孙远
	> company: GDOU
    > Mail: lsy476941913@live.com 
    > Created Time: 2017年02月24日 星期五 20时57分12秒
 ************************************************************************/
typedef int datanode;
typedef struct dlistnode{
	datanode data;
	struct dlistnode *prior,*next;
}DListNode;
typedef DListNode *DListLink;
DListLink head;

    一般情况下,我们编写的结构体都带有数据域,如上所示。 这样带数据域的链表降低了链表的通用性,不容易扩展。linux内核定义的链表结构不带数据域,只需要两个指针完成链表的操作。将链表节点加入数据结构,具备非常高的扩展性,通用性。

/*
 * Simple doubly linked list implementation.
 *
 * Some of the internal functions ("__xxx") are useful when
 * manipulating whole lists rather than single entries, as
 * sometimes we already know the next/prev entries and we can
 * generate better code by using them directly rather than 
 * using the generic single-entry routines.
 */

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

    内核链表的结构如下图所示:

 

2.内核链表的实现

     内核链表是双向循环链表,内核提供了链表的基本操作

(1)初始化链表

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

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

    这里的宏定义看起来会有点懵,内核源代码都是高手写的,代码风格都非常好,结合两段代码来看就会好些,这两段代码的目的如下:

struct list_head name={ &(name), &(name) }

    从这看来,问题就解决了。

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

     前面的LIST_HEAD创建了一个头结点,并用LIST_HEAD_INIT对链表进行了初始化,INIT__LIST_HEAD对头结点进行了初始化,使得头结点和尾节点都指向头结点。

(2)添加一个节点

/*
 * Insert a new entry between two known consecutive entries.
 *
 * This is only for internal list manipulation where we know
 * the prev/next entries already!
 */
#ifndef CONFIG_DEBUG_LIST
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;
}
#else
extern void __list_add(struct list_head *new,
			      struct list_head *prev,
			      struct list_head *next);
#endif

    __list_add函数的目的是想在prev和next节点中间插入一个new节点,而且后面很多机会或者未来想要扩展功能时候可能会使用到此函数, 插入的过程图如下:

/**
 * 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)
{
	__list_add(new, head, head->next);
}

    list_add函数调用了之前的__list_add函数,扩展了他的功能,此函数的目的是在某个节点前添加一个新的节点,head是一个头节点,head->next是头结点,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)
{
	__list_add(new, head->prev, head);
}

    list_add_tail函数跟list_add函数一样,也实现了添加节点的功能,不同的地方在于,调用__list_add函数的时候传入的参数不一样。这函数的功能是实现往某个节点的前面插入一个new节点,headd是头结点,往头结点前面插入,即是往链表尾部插入节点`。

 

(3)删除一个节点

    删除一个节点前,必须要知道该节点的前驱指针和后驱指针,这样才能删除掉该节点,删掉该节点不是意味着要将该节点从内存中free,而是使得该节点与当前的链表毫无关联,删除该节点后,需要分别将该节点的前驱指针和后继指针分别指向LIST_POSITION1和LIST_POSITION2,这两个定义的地址在下面,访问这两个地址都会使得程序出现故障。

/*
 * 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;
	prev->next = next;
}
/*
 * These are non-NULL pointers that will result in page faults
 * under normal circumstances, used to verify that nobody uses
 * non-initialized list entries.
 */
#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)

__list_del函数是删除一个节点,删除的流程如下图:

/**
 * list_del - deletes entry from list.
 * @entry: the element to delete from the list.
 * Note: list_empty() on entry does not return true after this, the entry is
 * in an undefined state.
 */
#ifndef CONFIG_DEBUG_LIST
static inline void list_del(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	entry->next = LIST_POISON1;
	entry->prev = LIST_POISON2;
}
#else
extern void list_del(struct list_head *entry);
#endif

list_del函数调用了上面实现的__list_del函数,实现了删除entry节点,并且最后是的entry节点的前驱指针和后继指针分别指向了LIST_POSITION1和LIST_POSITION2。

 

/**
 * list_del_init - deletes entry from list and reinitialize it.
 * @entry: the element to delete from the list.
 */
static inline void list_del_init(struct list_head *entry)
{
	__list_del(entry->prev, entry->next);
	INIT_LIST_HEAD(entry);
}

list_del_init函数目的是想删除了entry节点后,重新初始化entry节点,而不是使其的前驱后继指向一个预先定义好的地址。

(4)替换一个节点

/**
 * list_replace - replace old entry by new one
 * @old : the element to be replaced
 * @new : the new element to insert
 *
 * If @old was empty, it will be overwritten.
 */
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;
}

static inline void list_replace_init(struct list_head *old,
					struct list_head *new)
{
	list_replace(old, new);
	INIT_LIST_HEAD(old);
}

list_replace实现了节点的替换,使用新的new节点更换旧的old节点,list_replace_init函数目的是想让被替换的节点重新初始化,前驱和后继都指向节点自己,替换过程如下图:

 

(5)移动节点

/**
 * list_move - delete from one list and add as another's head
 * @list: the entry to move
 * @head: the head that will precede our entry
 */
static inline void list_move(struct list_head *list, struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add(list, head);
}

/**
 * list_move_tail - delete from one list and add as another's tail
 * @list: the entry to move
 * @head: the head that will follow our entry
 */
static inline void list_move_tail(struct list_head *list,
				  struct list_head *head)
{
	__list_del(list->prev, list->next);
	list_add_tail(list, head);
}

list_move函数目的,一:将list节点从链表中删除;二:将list节点插入到头结点head的后面,变成了头结点head后继指针指向的节点。

list_move_tail函数也是差不多,只不过是将删除了的list节点插入到链表尾部。

/**
 * list_rotate_left - rotate the list to the left
 * @head: the head of the list
 */
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_left函数目的是想让head头节点的后继节点移动到链表尾。

(6)判断链表

  • 判断是否达到了末尾
/**
 * list_is_last - tests whether @list is the last entry in list @head
 * @list: the entry to test
 * @head: the head of the list
 */
static inline int list_is_last(const struct list_head *list,
				const struct list_head *head)
{
	return list->next == head;
}

由于是双向循环链表,当链表节点达到了链表末尾时,节点就是头结点head,list_is_last函数就是判断节点的下一个节点是否为头结点,如果是,返回1,如果不是,返回0。

  • 判断链表是否为空
/**
 * list_empty - tests whether a list is empty
 * @head: the list to test.
 */
static inline int list_empty(const struct list_head *head)
{
	return head->next == head;
}

此函数判断head头结点的下一个节点是否还是head头结点,因为head头结点为了方便操作,head头结点不带任何的数据,当链表中只剩下了head头结点时,我们就可以认为链表为空链表。

/**
 * list_empty_careful - tests whether a list is empty and not being modified
 * @head: the list to test
 *
 * 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 = head->next;
	return (next == head) && (next == head->prev);
} 

 list_empty()函数和list_empty_careful()函数都是用来检测链表是否为空的。但是稍有区别的就是第一个链表使用的检测方法是判断表头的结点的下一个结点是否为其本身,如果是则返回为true,否则返回false。第二个函数使用的检测方法是判断表头的前一个结点和后一个结点是否为其本身,如果同时满足则返回false,否则返回值为true。提供两个判断函数,目的如下:

1)这主要是为了应付另一个cpu正在处理同一个链表而造成next、prev不一致的情况。  

2)但代码注释也承认,这一安全保障能力有限:除非其他cpu的链表操作只有list_del_init(),否则仍然不能保证安全,也就是说,还是需要加锁保护。

 

  • 判断节点是否为单节点链表
/**
 * list_is_singular - tests whether a list has just one entry.
 * @head: the list to test.
 */
static inline int list_is_singular(const struct list_head *head)
{
	return !list_empty(head) && (head->next == head->prev);
}

(7)分割链表

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;   //为了避免entry的后继发生改变,使用一个新的节点来记录entry的后继节点。
	list->next = head->next;
	list->next->prev = list;
	list->prev = entry;
	entry->next = list;
	head->next = new_first;
	new_first->prev = head;
}

/**
 * list_cut_position - cut a list into two
 * @list: a new list to add all removed entries
 * @head: a list with entries
 * @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) &&
		(head->next != entry && head != entry))
		return;
	if (entry == head)
		INIT_LIST_HEAD(list);
	else
		__list_cut_position(list, head, entry);
}
static inline void list_cut_position(struct list_head *list,   \
                            struct list_head *head, struct list_head *entry) 
@list:将剪切的结点要加进来的链表
@head:被剪切的链表
@entry:所指位于由head所指领头的链表内,它可以指向head,但是这样的话,head就不能被剪切了,在代码中调用了INIT_LIST_HEAD(list)。
是将head(不包括head)到entry之间的所有结点剪切下来加到list所指向的链表中。这个操作之后就有了两个链表head和list。具体的结果参看下图

分割之前的情况:

 分割之后的情况:

 (8)链表的合并

提供的接口有四个:
static inline void list_splice(const struct list_head *list,
                        struct list_head *head)
static inline void list_splice_tail(struct list_head *list,
                        struct list_head *head)
static inline void list_splice_init(struct list_head *list,
                        struct list_head *head)
static inline void list_splice_tail_init(struct list_head *list,
                        struct list_head *head)
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领头的这个链表合并到prev和next之间,不包括list这个结点。

 

/**
 * list_splice - join two lists, this is designed for stacks
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
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函数目的是想把list链表插入到head和head的后继节点中间,类似于头插法。

 

/**
 * list_splice_tail - join two lists, each list being a queue
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 */
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_tail函数目的是想把list链表插入到head的前驱节点和head节点中间,类似于尾插法。

 

/**
 * list_splice_init - join two lists and reinitialise the emptied list.
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * The list at @list is reinitialised
 */
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 - join two lists and reinitialise the emptied list
 * @list: the new list to add.
 * @head: the place to add it in the first list.
 *
 * Each of the lists is a queue.
 * The list at @list is reinitialised
 */
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);
	}
}

这两个函数就更加简单了,插入节点后,将list头结点初始化为空链表。

(9)链表遍历

在分析链表遍历之前,我们必须搞懂container_of的定义,因为链表遍历需要调用container_of的功能。

container_of包含在include/linux/kernel.h中

/**
 * 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) ({			\
	const typeof( ((type *)0)->member ) *__mptr = (ptr);	\
	(type *)( (char *)__mptr - offsetof(type,member) );})
在container_of(ptr, type, member)中,ptr表示指向struct list_head成员变量的地址,type表示这个结构体的类型,member表示struct list_head在该结构体中的变量名称。其中offsetof宏定义在include/linux/stddef.h中
#undef offsetof
#ifdef __compiler_offsetof
#define offsetof(TYPE,MEMBER) __compiler_offsetof(TYPE,MEMBER)
#else
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
#endif
#endif /* __KERNEL__ */
#endif
所以整个container_of的意思就是如下:
const typeof(((type **)0)->member) *__mptr=(ptr);
(type *)((char *)__mptr) - ((size_t) &((type *)0)->member));

再次注意一下,这里将__mptr强制转化成了(char *)类型进行了地址相减操作。

 

 

 

 

 

上一篇: LCD

下一篇: 关于const的用法

1459 人读过
0 条评论