0.前言

前文已经叙述道,linux中链表的实现是节点与数据分离,如果要使用链表,只需在数据结构中包含链表的结构(非指针)即可。

struct nf_sockopt_ops的定义为

struct nf_sockopt_ops
{
  struct list_head
list;
  u_int8_t pf;
  ……
  int
( *
set )(
struct sock * sk, int optval, void
__user *
user, unsigned int len);
  ……

  int ( * compat_get)( struct sock
* sk,
int optval,

  void __user * user, int
* len);
  ……

  struct module * owner;
};

下图为其链接图

linux中的list源码分析——遍历节点分析

链表遍历

链表的遍历是非常直观与简单的,在list.h中,有遍历链表的代码,其是以宏的形式出现的。

/**
* 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; prefetch(pos->next), pos != (head); \
pos
= pos->next)

prefetch()的作用是预取节点,以提高速度,此处可以先不管。事实上,linux还提供了一个无prefetch的遍历,适合数据量比较小的情况下。代码如下

linux中的list源码分析——遍历节点分析__list_for_each

/**
* __list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*
* This variant differs from list_for_each() in that it's the
* simplest possible list iteration code, no prefetching is done.
* Use this for code that knows the list to be very short (empty
* or 1 entry) most of the time.
*/
#define __list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)

前向遍历

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

由于可能删除pos节点,那样就无法找到后继或前继,所以linux用一个临时的变量n来提供安全遍历,代码如下

/**
* list_for_each_safe - iterate over a list 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_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos
= n, n = pos->next)
/**
* 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; \
prefetch(pos
->prev), pos != (head); \
pos
= n, n = pos->prev)

结点数据遍历

  但是链表的操作归根结底还是要对节点的数据进行处理,那么linux如何解决这个问题的呢?Linux链表将遍历操作抽象成几个宏,详细情况还是让我们进入源码去发掘吧。

Linux为此提供了一个list_entry(ptr,type,member)宏,其中ptr是指向该数据中list_head成员的指针,也就是存储在链表中的地址值,type是数据项的类型,member则是数据项类型定义中list_head成员的变量名。

list_entry的定义如下

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

重点就落在了container_of,该宏在/include/linux/kernel.h里面定义

#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type
*)( (char *)__mptr - offsetof(type,member) );})

typeof是是gcc的扩展,和sizeof()类似。

offsetof也是一个宏定义,在include/linux/stddef.h中定义,其代码为

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

通过欺骗编译器在内存地址0处有一个TYPE类型的对象,然后强制转换成TYPE类型,取出MEMBER的地址,这个内存地址便是相对于0地址的偏移字节数,

在文章linux内核V2.6.11学习笔记(2)--list和hlist,作者提供了另外一种定义(展开了offsetof宏),但是有助于理解。

#define container_of(ptr, type, member) \
((type
*)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))

container_of 宏的作用就是返回一个结构体中的某成员变量对应的包含它的结构体的指针。具体识别分析参考文献1.

原文为

linux中的list源码分析——遍历节点分析View Code

1) &((type *)0)->member)
从C的角度出发, 假设结构体node中有一个成员data, 那么对于一个指向结构体node的指针p来说,p
->data与p的地址相差为data这个域在结构体node中的偏移量.
于是,
&(p->member)就是type类型的指针p中的成员member的地址,而这个地址是p的地址+member成员在这个结构体中的偏移,
当这个p变成了0之后,自然就得出了member成员在结构体type中的偏移量.

所以,
&((type *)0)->member)获得了结构体type中成员member的偏移量.

2) (char *)(ptr)-(unsigned long)(&((type *)0)->member))
这里ptr是list_head的指针,也就是member成员的指针,因此两者相减得到了存放member的type结构体的指针.

3)((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
最后在前面加上一个类型转换,将前面得到的指针转换成type类型.

还是非常清晰的。

理解了list_entry,那么那么下面的代码功能就差不多可以分析了。

首先是获取链表的第一个节点数据list_first_entry。

/**
* list_first_entry - get the first element from a list
* @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_struct within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)
->next, type, member)

遍历

大多数情况下,遍历链表的时候都需要获得链表节点数据项,也就是说list_for_each()和list_entry()总是同时使用。对此Linux给出了一个list_for_each_entry()宏:

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

与其相对的反向遍历的list_for_each_entry_reverse(个人觉得如果对应,命名为list_for_each_entry_prev可能要好些)

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

如果遍历不是从链表头开始,而是从已知的某个节点pos开始,则可以使用list_for_each_entry_continue(pos,head,member)。有时还会出现这种需求,即经过一系列计算后,如果pos有值,则从pos开始遍历,如果没有,则从链表头开始,为此,Linux专门提供了一个list_prepare_entry(pos,head,member)宏,将它的返回值作为list_for_each_entry_continue()的pos参数,就可以满足这一要求。

linux中的list源码分析——遍历节点分析View Code

/**
* 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_struct 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_entry(pos->member.next, typeof(*pos), member); \
prefetch(pos
->member.next), &pos->member != (head); \
pos
= list_entry(pos->member.next, typeof(*pos), member))

/**
* 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_struct 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_entry(pos->member.prev, typeof(*pos), member); \
prefetch(pos
->member.prev), &pos->member != (head); \
pos
= list_entry(pos->member.prev, typeof(*pos), member))

/**
* list_for_each_entry_from - iterate over list of given type from the current point
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_struct within the struct.
*
* Iterate over list of given type, continuing from current position.
*/
#define list_for_each_entry_from(pos, head, member) \
for (; prefetch(pos->member.next), &pos->member != (head); \
pos
= list_entry(pos->member.next, typeof(*pos), member))

下面是上面代码的安全版本

linux中的list源码分析——遍历节点分析View Code

/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @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_struct within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_entry((head)->next, typeof(*pos), member), \
n
= list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos
= n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_continue
* @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_struct within the struct.
*
* Iterate over list of given type, continuing after current point,
* safe against removal of list entry.
*/
#define list_for_each_entry_safe_continue(pos, n, head, member) \
for (pos = list_entry(pos->member.next, typeof(*pos), member), \
n
= list_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos
= n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_from
* @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_struct 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_entry(pos->member.next, typeof(*pos), member); \
&pos->member != (head); \
pos
= n, n = list_entry(n->member.next, typeof(*n), member))

/**
* list_for_each_entry_safe_reverse
* @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_struct 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_entry((head)->prev, typeof(*pos), member), \
n
= list_entry(pos->member.prev, typeof(*pos), member); \
&pos->member != (head); \
pos
= n, n = list_entry(n->member.prev, typeof(*n), member))

最重要的是如何用,在网上找到一个可以用的版本,基于GCC——typeof扩展

请参考网站

 [Linux Kernel Linked List Explained]

同学也可以从此处下载

参考文献

linux内核V2.6.11学习笔记(2)--list和hlist

《深入分析 Linux
内核链表》