linux内核list.h头文件分析(四)

660阅读 0评论2017-08-25 wzzushx
分类:C/C++

(十一)链表的宏遍历
在开始链表的遍历之前,先看一个问题:通过一个结构体的成员变量如何访问其他结构体成员的变量。
我们先看一个例子吧:
有这个结构体
  1. struct symbol_list {
  2.     int num;
  3.     char value[STR_TOKEN]; //STR_TOKEN为一个宏定义的一个整数
  4.     struct list_head list;
  5. };
我们的目标是通过for_each_symbol()函数得到这个结构体变量的首地址,这样,我们就能通过这个首地址访问这个结构体的其他成员变量了。
  1. struct symbol_list *for_each_symbol(struct list_head *pos) {
  2.     const typeof(((struct symbol_list *)0)->list) *ptr = pos;
  3.     int offset = (int)(&((struct symbol_list *)0)->list);
  4.     struct symbol_list *p = (struct symbol_list *)((char *)ptr - offset);
  5.     return p;
  6. }
先看第一句const typeof(((struct symbol_list *)0)->list) *ptr = pos,其中   typeof(type)是gcc的扩展,是得到type的数据类型,和我们比较熟悉的sizeof()比较类似。这一句的执行结果是申请一个struct list_head类型的指针变量,并将pos这个变量赋值给ptr,至于为什么要赋值给ptr,这里是为了防止修改pos的值。
接着,我们看第二句,int offset = (int)(&((struct symbol_list *)0)->list),将0强制转化为struct symbol_list类型的指针,并且取出list变量的地址给offset,这一句是为了得到list所指向的变量相对于整个结构体变量的相对地址。
最后一句,struct symbol_list *p = (struct symbol_list *)((char *)ptr - offset),是将ptr的值与offset相对地址相减,这样就可以得到了这个结构体变量的首地址,这样我们就可以通过这个p来获取这个结构体其他成员变量的值了。这里需要注意一点,就是将ptr强制转化的时候不是转化成了(int *),而是转化成了(char *)。指针相减:在数组中的定义是说明两个元素之间的相隔元素为单位的距离。这里就是一些指针相减的知识,char指针+1是1字节地址偏移,int指针+1是4字节地址偏移。不相信的话,你自己可以
printf("%p", pointer)
看看。

下面我们还需要看一个东西才能正式进入内核链表的遍历。
container_of宏定义在include/linux/kernel.h中:
  1. #define container_of(ptr, type, member) ({            \
  2.         const typeof(((type *)0)->member ) *__mptr = (ptr);    \
  3.         (type *)((char *)__mptr - offsetof(type,member));})
offsetof宏定义在include/linux/stddef.h中:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
在container_of(ptr, type, member)中,ptr表示指向struct list_head成员变量的地址,type表示这个结构体的类型,member表示struct list_head在该结构体中的变量名称。
所以container_of(ptr, type, member)就等价于下面的东西:
  1. const typeof(((type *)0)->member ) *__mptr = (ptr);
  2. (type *)((char *)__mptr - ((size_t) &((type *)0)->member));
再次注意一下,这里将__mptr强制转化成了(char *)类型进行了地址相减操作。
(1)这个宏好和container_of(ptr, type, member)没有什么区别:都是得到ptr所指地址的这个结构体的首地址
  1. #define list_entry(ptr, type, member) \
  2.     container_of(ptr, type, member)
(2)这里的ptr是一个链表的头节点,这个宏就是取得这个链表第一元素的所指结构体的首地址。
  1. #define list_first_entry(ptr, type, member) \
  2.     list_entry((ptr)->next, type, member)
(3)这个实际上就是一个for循环,从头到尾遍历链表。prefetch()用于预取以此提高效率。
  1. #define list_for_each(pos, head) \
  2.     for (pos = (head)->next; prefetch(pos->next), pos != (head); \
  3.             pos = pos->next)
(4)这个实际上就是一个for循环,从头到尾遍历链表。和前一个不同的是,这个没有使用prefetch()函数来预取提高效率。
  1. #define __list_for_each(pos, head) \
  2.     for (pos = (head)->next; pos != (head); pos = pos->next)
(5)这个实际上就是一个for循环,从尾到头遍历链表。prefetch()用于预取以此提高效率。
  1. #define list_for_each_prev(pos, head) \
  2.     for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
  3.             pos = pos->prev)
(6)这个实际上就是一个for循环,从头到尾遍历链表。这里使用了n来记录pos的下一个,这样处理完一个流程之后再赋给pos,避免了删除pos结点造成的问题,由它的英文注释我们可以看书,其实这个函数是专门为删除结点是准备的。
  1. #define list_for_each_safe(pos, n, head) \
  2.     for (pos = (head)->next, n = pos->next; pos != (head); \
  3.         pos = n, n = pos->next)
注:list_for_each(pos, head)和list_for_each_safe(pos, n, head)都是从头至尾遍历链表的,但是对于前者来说当操作中没有删除结点的时候使用,但是如果操作中有删除结点 的操作的时候就使用后者,对于后面代safe的一般都是这个目的。
(7)这个实际上就是一个for循环,从尾到头遍历链表。这里使用了n来记录pos的前一个,这样处理完一个流程之后再赋给pos,避免了删除pos结点造成的问题,由它的英文注释我们可以看书,其实这个函数是专门为删除结点是准备的。
  1. #define list_for_each_prev_safe(pos, n, head) \
  2.     for (pos = (head)->prev, n = pos->prev; \
  3.      prefetch(pos->prev), pos != (head); \
  4.      pos = n, n = pos->prev)
(8)第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三个参数为list_head结构在宿主结构中的成员名。
list_entry((head)->next, typeof(*pos), member)用来得到head链表的第一个元素所在结构体的首地址。
这个函数是根据member成员遍历head链表,并且将每个结构体的首地址赋值给pos,这样的话,我们就可以在循环体里面通过pos来访问该结构体变量的其他成员了。而前面的list_for_each(pos, head)中的pos是list_head类型的。
  1. #define list_for_each_entry(pos, head, member)                \
  2.     for (pos = list_entry((head)->next, typeof(*pos), member);    \
  3.      prefetch(pos->member.next), &pos->member != (head);     \
  4.      pos = list_entry(pos->member.next, typeof(*pos), member))
(9)和第(8)个类似,只是遍历的顺序不一样,是从尾到头来遍历。
  1. #define list_for_each_entry_reverse(pos, head, member)            \
  2.     for (pos = list_entry((head)->prev, typeof(*pos), member);    \
  3.      prefetch(pos->member.prev), &pos->member != (head);     \
  4.      pos = list_entry(pos->member.prev, typeof(*pos), member))
(10)pos表示结构体变量;head表示这个链表的开始节点,是list_head类型;member是list_head在结构体当中的变量的名字。
这个函数的功能就是如果pos非空,那么pos的值就为其本身,如果pos为空,那么就从链表头强制扩展一个虚pos指针,这个宏定义是为了在list_for_each_entry_continue()中使用做准备的。
  1. #define list_prepare_entry(pos, head, member) \
  2.     ((pos) ? : list_entry(head, typeof(*pos), member))
(11)pos表示结构体变量;head表示这个链表的开始节点,是list_head类型;member是list_head在结构体当中的变量的名字。
这个函数是根据member成员遍历head链表,并且将每个结构体的首地址赋值给pos,这样的话,我们就可以在循环体里面通过pos来访问该结构体变量的其他成员了。而前面的list_for_each(pos, head)中的pos是list_head类型的。
这个函数得遍历可以不从链表的头开始遍历,可以从一个指定的pos节点遍历。
  1. #define list_for_each_entry_continue(pos, head, member)         \
  2.     for (pos = list_entry(pos->member.next, typeof(*pos), member);    \
  3.      prefetch(pos->member.next), &pos->member != (head);    \
  4.      pos = list_entry(pos->member.next, typeof(*pos), member))
(12)和(11)类似,不同的是遍历的顺序相反。
  1. #define list_for_each_entry_continue_reverse(pos, head, member)        \
  2.     for (pos = list_entry(pos->member.prev, typeof(*pos), member);    \
  3.      prefetch(pos->member.prev), &pos->member != (head);    \
  4.      pos = list_entry(pos->member.prev, typeof(*pos), member))
(13)这个函数的遍历是从当前这个点开始遍历的。
  1. #define list_for_each_entry_from(pos, head, member)             \
  2.     for (; prefetch(pos->member.next), &pos->member != (head);    \
  3.      pos = list_entry(pos->member.next, typeof(*pos), member))
(14)和list_for_each_entry的遍历类似,这个带了safe是为了防止删除节点而造成断链的发生。
  1. #define list_for_each_entry_safe(pos, n, head, member)            \
  2.     for (pos = list_entry((head)->next, typeof(*pos), member),    \
  3.         n = list_entry(pos->member.next, typeof(*pos), member);    \
  4.      &pos->member != (head);                     \
  5.      pos = n, n = list_entry(n->member.next, typeof(*n), member))
(15)和list_for_each_entry_continue()遍历类似,这个带了safe是为了防止删除节点而造成断链的发生。
  1. #define list_for_each_entry_safe_continue(pos, n, head, member)         \
  2.     for (pos = list_entry(pos->member.next, typeof(*pos), member),         \
  3.         n = list_entry(pos->member.next, typeof(*pos), member);        \
  4.      &pos->member != (head);                        \
  5.      pos = n, n = list_entry(n->member.next, typeof(*n), member))
(16)从当前的节点开始遍历。
  1. #define list_for_each_entry_safe_from(pos, n, head, member)             \
  2.     for (n = list_entry(pos->member.next, typeof(*pos), member);        \
  3.      &pos->member != (head);                        \
  4.      pos = n, n = list_entry(n->member.next, typeof(*n), member))
(17)和list_for_each_entry_safe类似,不过遍历的顺序刚好相反。
  1. #define list_for_each_entry_safe_reverse(pos, n, head, member)        \
  2.     for (pos = list_entry((head)->prev, typeof(*pos), member),    \
  3.         n = list_entry(pos->member.prev, typeof(*pos), member);    \
  4.      &pos->member != (head);                     \
  5.      pos = n, n = list_entry(n->member.prev, typeof(*n), member))
(18)list_safe_reset_next is not safe to use in general if the list may be modified concurrently (eg. the lock is dropped in the loop body). An exception to this is if the cursor element (pos) is pinned in the list, and list_safe_reset_next is called after re-taking the lock and before completing the current iteration of the loop body.

  1. #define list_safe_reset_next(pos, n, member)                \
  2.     n = list_entry(pos->member.next, typeof(*pos), member)
上一篇:浅析Makefile不要使用make -e传递变量,直接使用make var1=value传递
下一篇:make的使用和Makefile规则和编程及其基本命令