关于内核中此宏list_for_each_entry的说明

转载 2012年03月23日 10:02:27
转载自 ever__love
最终编辑 pcwung
#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))

说白了define list_for_each_entry 就是一个for循环。。。。




在 [net/core/netfilter.c] 的 nf_register_sockopt() 函数中有这么一段话:

…… struct list_head *i; …… list_for_each(i, &nf_sockopts) { struct nf_sockopt_ops *ops = (struct nf_sockopt_ops *)i; …… } ……


函数首先定义一个 (struct list_head *) 指针变量i,然后调用 list_for_each(i,&nf_sockopts) 进行遍历。在 [include/linux/list.h] 中, list_for_each() 宏是这么定义的:

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

它实际上是一个 for 循环,利用传入的 pos 作为循环变量,从表头 head 开始,逐项向后(next 方向)移动 pos,直至又回到 head(prefetch() 可以不考虑,用于预取以提高遍历速度 )。

那么在 nf_register_sockopt() 中实际上就是遍历 nf_sockopts 链表。为什么能直接将获得的 list_head 成员变量地址当成 struct nf_sockopt_ops 数据项变量的地址呢?我们注意到在 struct nf_sockopt_ops 结构中,list是其中的第一项成员,因此,它的地址也就是结构变量的地址。更规范的获得数据变量地址的用法应该是:

struct nf_sockopt_ops *ops = list_entry(i, struct nf_sockopt_ops, list);


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

#define list_for_each_entry(pos, head, member) ……


与 list_for_each() 不同,这里的pos是数据项结构指针类型,而不是 (struct list_head *)。nf_register_sockopt() 函数可以利用这个宏而设计得更简单:

…… struct nf_sockopt_ops *ops; list_for_each_entry(ops,&nf_sockopts,list){ …… } ……


某些应用需要反向遍历链表,Linux 提供了 list_for_each_prev() 和 list_for_each_entry_reverse() 来完成这一操作,使用方法和上面介绍的 list_for_each()、list_for_each_entry() 完全相同。

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

4. 安全性考虑
在并发执行的环境下,链表操作通常都应该考虑同步安全性问题,为了方便,Linux 将这一操作留给应用自己处理。Linux 链表自己考虑的安全性主要有两个方面:

a) list_empty() 判断

基本的 list_empty() 仅以头指针的 next 是否指向自己来判断链表是否为空,Linux 链表另行提供了一个 list_empty_careful() 宏,它同时判断头指针的 next 和 prev,仅当两者都指向自己时才返回真。这主要是为了应付另一个 cpu 正在处理同一个链表而造成 next、prev 不一致的情况。但代码注释也承认,这一安全保障能力有限:除非其他 cpu 的链表操作只有 list_del_init(),否则仍然不能保证安全,也就是说,还是需要加锁保护。

b) 遍历时节点删除

前面介绍了用于链表遍历的几个宏,它们都是通过移动 pos 指针来达到遍历的目的。但如果遍历的操作中包含删除 pos 指针所指向的节点,pos 指针的移动就会被中断,因为 list_del(pos) 将把 pos 的 next、prev 置成 LIST_POSITION2 和 LIST_POSITION1 的特殊值。

当然,调用者完全可以自己缓存 next 指针使遍历操作能够连贯起来,但为了编程的一致性,Linux 链表仍然提供了两个对应于基本遍历操作的 "_safe" 接口:list_for_each_safe(pos, n, head)、list_for_each_entry_safe(pos, n, head, member),它们要求调用者另外提供一个与 pos 同类型的指针n,在 for 循环中暂存 pos 下一个节点的地址,避免因 pos 节点被释放而造成的断链

相关文章推荐

linux内核学习笔记之——list_for_each_entry

在Linux内核源码中,经常要对链表进行操作,其中一个很重要的宏是list_for_each_entry: 意思大体如下: 假设只有两个结点,则第一个member代表head, list_f...

list_for_each_entry宏函数解析(上)

在内核中,经常用到list_for_each_entry函数, container_of

list_for_each_entry深入理解

list_for_each_entry

Linux container_of 以及list_for_each_entry 的拆分理解

在阅读Linux 内核以及驱动的时候,经常会见到这么一个宏"list_for_each_entry()",或者"list_for_each_entry_xxx()",如果不理解就验证影响代码的理解。拖...

list_for_each_entry

在Linux内核源码中,经常要对链表进行操作,其中一个很重要的宏是list_for_each_entry: 意思大体如下:          假设下面几个结点,则第一个member代表he...

list_for_each_entry

参考: http://blog.sina.com.cn/s/blog_5e99b41e0100rxgf.html http://hi.baidu.com/shiftedmind/blog/...

list_for_each_entry

参考: http://blog.sina.com.cn/s/blog_5e99b41e0100rxgf.html http://hi.baidu.com/shiftedmind/blog/item...

[转载] 终于理解list_entry和list_for_each_entry

原文地址:http://blog.sina.com.cn/s/blog_533074eb0101do71.html 内核中经常采用链表来管理对象,先看一下内核中对链表的定义     struc...

list_for_each与list_for_each_entry

1.list_for_each和list_for_each_entry都是遍历链表的两个宏,本质上都是for循环。 2.他们做的事情本质上都一样,A.获取链表头,B.判断链表项是不是链表头,C.指向...

Linux内核中list_head、list_for_each、list_entry、container_of之间的关系

原文:http://blog.csdn.net/hongchangfirst/article/details/7076225 作者:hongchangfirst Linux内核需要...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)