linux内核中链表代码分析---list.h头文件分析(二)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html
- linux内核中链表代码分析---list.h头文件分析(二)
- 16年2月28日16:59:55
- 分析完container_of()宏以后,继续分析list.h文件:
- (1)list_entry
- 它就是一个container_of宏,都是得到ptr所指地址的这个结构体的首地址
- #define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
- (2) list_first_entry
- #define list_first_entry(ptr, type, member) \
- list_entry((ptr)->next, type, member)
- 这里的ptr是一个链表的头节点,这个宏就是取得这个链表第一元素的所指结构体的首地址。
- (3) list_for_each
- #define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
- 它实际上就是一个for循环,从头到尾遍历链表,但是看代码发现,遍历链表需要一个head参数即可,这个pos参数好像没什么用啊。。。
- 然后就在内核中grep了一番,稍微总结出来一点:
- 这是一个for循环,我们通过这个for循环总得做点什么事情吧,以下面这个为例,
- 源文件在/driver/input/serio/hil_mlc.c中:
- static LIST_HEAD(hil_mlcs);
- static void hil_mlcs_process(unsigned long unused)
- {
- struct list_head *tmp;
- read_lock(&hil_mlcs_lock);
- list_for_each(tmp, &hil_mlcs) {
- struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
- ....
- }
- 可以看到,它通过这个list_for_each函数,将hil_mlcs链表中的每一个成员地址一一赋给了tmp变量,这样,在后面的语句中,就可以通过list_entry这个宏根据tmp变量和list链表头来找到包含他们的整个结构体hil_mlc的首地址,然后就可以对这个结构体里面其他的成员变量进行操作了~~~
- 这一块涉及到linux中,比如对于每一个子设备,都建立一个结构体,然后将这些子设备通过一个链表链接起来,操作的时候需要遍历链表中的每一项进行操作,所以就是这个函数存在的意义。
- 后面那个 __list_for_each和list_for_each相同,以前这两个函数是不同的,它有一个prefetch预取的操作,我们不再考虑。
- (4)list_for_each_prev
- #define list_for_each_prev(pos, head) \
- for (pos = (head)->prev; pos != (head); pos = pos->prev)
- 与list_for_each相似,只是它是从尾到头遍历链表,将链表中的每一项都取出来操作。
- (5)list_for_each_safe
- #define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
- 这个实际上就是一个for循环,从头到尾遍历链表。这里使用了n来记录pos的下一个,这样处理完一 个流程之后再赋给pos,避免了删除pos结点造成的问题。这个函数是 专门为删除结点是准备的。
- 注:list_for_each(pos, head)和list_for_each_safe(pos, n, head)都是从头至尾遍历链表的,但是对于前者 来说当操作中没有删除结点的时候使用,但是如果操作中有删除结点的操作的时候就使用后者,对于后面带safe的一般都是这个目的。
- (6) list_for_each_prev_safe
- #define list_for_each_prev_safe(pos, n, head) \
- for (pos = (head)->prev, n = pos->prev; \
- pos != (head); \
- pos = n, n = pos->prev)
- 同理,这个函数与 list_for_each_prev相似。
- /* 注意上面的几个函数,他们的行参里面有pos与下面函数行参里面的pos不同,上面函数的操作都是pos = (head)->next等等的操作,所以pos是list_head类型的,下面函数的操作是pos = list_entry()等等的操作,所以他们是list_head结构体所嵌入的结构体的指针形式的,我们称之为宿主结构体。 */
- (7) list_for_each_entry
- #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_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
- 这个函数是根据member成员遍历head链表(member成员一般都嵌入在宿主结构体中),并且将每个结构体的首地址赋值给pos,这样的话,我们就 可以在循环体里面通过pos来访问该宿主结构体变量的其他成员了。 下面用最近分析的V4L2里面的一段代码来举例说明,代码位于v4l2-device.c中:
- int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
- {
- struct video_device *vdev;
- struct v4l2_subdev *sd;
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- continue;
- 。。。。
- }
- 可以分析出来, v4l2_device结构体里面含有这个
subdevs链表头,sd是指向v4l2_subdev结构体的指针,list是一个list_head结构,在之前的代码中,肯定有操作已经将各个子设备结构体v4l2_subdev全部链接进
v4l2_device结构体里面的 subdevs链表里面,这时候需要做的就是从这个v4l2_device结构体里面的
subdevs链表里一一取出各个子设备结构体v4l2_subdev,然后操作它的各个成员变量。上述代码就是完成这个工作。 - (8) list_for_each_entry_reverse
- #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))
- 与上面那个函数类似,反向遍历链表。
- (9) list_prepare_entry
- #define list_prepare_entry(pos, head, member) \
- ((pos) ? : list_entry(head, typeof(*pos), member))
- 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
- 这个函数的功能就是如果pos非空,那么pos的值就为其本身,如果pos为空,那么就从链表头强制扩展一
- 个虚pos指针,这个宏定义是为了在list_for_each_entry_continue()中使用做准备的。
- (10)list_for_each_entry_continue
- #define list_for_each_entry_continue(pos, head, member) \
- for (pos = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
- 这个函数与list_for_each_entry很相似,但是肯定是不同的,它是从pos所在的宿主结构体的下一个开始遍历链表,并不是从链表的头部开始遍历,从它的名字可以看出来。主要的区别就是我标红的位置。
- (11)list_for_each_entry_continue_reverse
- #define list_for_each_entry_continue_reverse(pos, head, member) \
- for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.prev, typeof(*pos), member))
- 与上一个函数类似,从pos位于的宿主结构体的上一个开始反向变量链表。
- (12)list_for_each_entry_from
- #define list_for_each_entry_from(pos, head, member) \
- for (; &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
- 从当前pos所位于的宿主结构体开始遍历链表。
- (13)后面的几个函数带safe的函数,他们与上面讲的函数都很相似,只是保存了pos的下一个宿主结构体,这些函数是专门为删除结点是准备的。就不分析他们了。
- 至此,对于普通链表的操作我们就分析完了,这个list.h中后面的代码主要是为了哈西表设计的,就暂时先不分析,等到学了哈西表再分析他们^o^~
- linux内核中链表代码分析---list.h头文件分析(二)
- 16年2月28日16:59:55
- 分析完container_of()宏以后,继续分析list.h文件:
- (1)list_entry
- 它就是一个container_of宏,都是得到ptr所指地址的这个结构体的首地址
- #define list_entry(ptr, type, member) \
- container_of(ptr, type, member)
- (2) list_first_entry
- #define list_first_entry(ptr, type, member) \
- list_entry((ptr)->next, type, member)
- 这里的ptr是一个链表的头节点,这个宏就是取得这个链表第一元素的所指结构体的首地址。
- (3) list_for_each
- #define list_for_each(pos, head) \
- for (pos = (head)->next; pos != (head); pos = pos->next)
- 它实际上就是一个for循环,从头到尾遍历链表,但是看代码发现,遍历链表需要一个head参数即可,这个pos参数好像没什么用啊。。。
- 然后就在内核中grep了一番,稍微总结出来一点:
- 这是一个for循环,我们通过这个for循环总得做点什么事情吧,以下面这个为例,
- 源文件在/driver/input/serio/hil_mlc.c中:
- static LIST_HEAD(hil_mlcs);
- static void hil_mlcs_process(unsigned long unused)
- {
- struct list_head *tmp;
- read_lock(&hil_mlcs_lock);
- list_for_each(tmp, &hil_mlcs) {
- struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
- ....
- }
- 可以看到,它通过这个list_for_each函数,将hil_mlcs链表中的每一个成员地址一一赋给了tmp变量,这样,在后面的语句中,就可以通过list_entry这个宏根据tmp变量和list链表头来找到包含他们的整个结构体hil_mlc的首地址,然后就可以对这个结构体里面其他的成员变量进行操作了~~~
- 这一块涉及到linux中,比如对于每一个子设备,都建立一个结构体,然后将这些子设备通过一个链表链接起来,操作的时候需要遍历链表中的每一项进行操作,所以就是这个函数存在的意义。
- 后面那个 __list_for_each和list_for_each相同,以前这两个函数是不同的,它有一个prefetch预取的操作,我们不再考虑。
- (4)list_for_each_prev
- #define list_for_each_prev(pos, head) \
- for (pos = (head)->prev; pos != (head); pos = pos->prev)
- 与list_for_each相似,只是它是从尾到头遍历链表,将链表中的每一项都取出来操作。
- (5)list_for_each_safe
- #define list_for_each_safe(pos, n, head) \
- for (pos = (head)->next, n = pos->next; pos != (head); \
- pos = n, n = pos->next)
- 这个实际上就是一个for循环,从头到尾遍历链表。这里使用了n来记录pos的下一个,这样处理完一 个流程之后再赋给pos,避免了删除pos结点造成的问题。这个函数是 专门为删除结点是准备的。
- 注:list_for_each(pos, head)和list_for_each_safe(pos, n, head)都是从头至尾遍历链表的,但是对于前者 来说当操作中没有删除结点的时候使用,但是如果操作中有删除结点的操作的时候就使用后者,对于后面带safe的一般都是这个目的。
- (6) list_for_each_prev_safe
- #define list_for_each_prev_safe(pos, n, head) \
- for (pos = (head)->prev, n = pos->prev; \
- pos != (head); \
- pos = n, n = pos->prev)
- 同理,这个函数与 list_for_each_prev相似。
- /* 注意上面的几个函数,他们的行参里面有pos与下面函数行参里面的pos不同,上面函数的操作都是pos = (head)->next等等的操作,所以pos是list_head类型的,下面函数的操作是pos = list_entry()等等的操作,所以他们是list_head结构体所嵌入的结构体的指针形式的,我们称之为宿主结构体。 */
- (7) list_for_each_entry
- #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_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
- 这个函数是根据member成员遍历head链表(member成员一般都嵌入在宿主结构体中),并且将每个结构体的首地址赋值给pos,这样的话,我们就 可以在循环体里面通过pos来访问该宿主结构体变量的其他成员了。 下面用最近分析的V4L2里面的一段代码来举例说明,代码位于v4l2-device.c中:
- int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
- {
- struct video_device *vdev;
- struct v4l2_subdev *sd;
- list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
- if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
- continue;
- 。。。。
- }
- 可以分析出来, v4l2_device结构体里面含有这个
subdevs链表头,sd是指向v4l2_subdev结构体的指针,list是一个list_head结构,在之前的代码中,肯定有操作已经将各个子设备结构体v4l2_subdev全部链接进
v4l2_device结构体里面的 subdevs链表里面,这时候需要做的就是从这个v4l2_device结构体里面的
subdevs链表里一一取出各个子设备结构体v4l2_subdev,然后操作它的各个成员变量。上述代码就是完成这个工作。 - (8) list_for_each_entry_reverse
- #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))
- 与上面那个函数类似,反向遍历链表。
- (9) list_prepare_entry
- #define list_prepare_entry(pos, head, member) \
- ((pos) ? : list_entry(head, typeof(*pos), member))
- 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
- 这个函数的功能就是如果pos非空,那么pos的值就为其本身,如果pos为空,那么就从链表头强制扩展一
- 个虚pos指针,这个宏定义是为了在list_for_each_entry_continue()中使用做准备的。
- (10)list_for_each_entry_continue
- #define list_for_each_entry_continue(pos, head, member) \
- for (pos = list_entry(pos->member.next, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
- 这个函数与list_for_each_entry很相似,但是肯定是不同的,它是从pos所在的宿主结构体的下一个开始遍历链表,并不是从链表的头部开始遍历,从它的名字可以看出来。主要的区别就是我标红的位置。
- (11)list_for_each_entry_continue_reverse
- #define list_for_each_entry_continue_reverse(pos, head, member) \
- for (pos = list_entry(pos->member.prev, typeof(*pos), member); \
- &pos->member != (head); \
- pos = list_entry(pos->member.prev, typeof(*pos), member))
- 与上一个函数类似,从pos位于的宿主结构体的上一个开始反向变量链表。
- (12)list_for_each_entry_from
- #define list_for_each_entry_from(pos, head, member) \
- for (; &pos->member != (head); \
- pos = list_entry(pos->member.next, typeof(*pos), member))
- 从当前pos所位于的宿主结构体开始遍历链表。
- (13)后面的几个函数带safe的函数,他们与上面讲的函数都很相似,只是保存了pos的下一个宿主结构体,这些函数是专门为删除结点是准备的。就不分析他们了。
- 至此,对于普通链表的操作我们就分析完了,这个list.h中后面的代码主要是为了哈西表设计的,就暂时先不分析,等到学了哈西表再分析他们^o^~
linux内核中链表代码分析---list.h头文件分析(二)【转】的更多相关文章
- linux内核中链表代码分析---list.h头文件分析(一)【转】
转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...
- Linux内核中链表的实现与应用【转】
转自:http://blog.chinaunix.net/uid-27037833-id-3237153.html 链表(循环双向链表)是Linux内核中最简单.最常用的一种数据结构. ...
- Linux内核中链表实现
关于双链表实现,一般教科书上定义一个双向链表节点的方法如下: struct list_node{ stuct list_node *pre; stuct list_node *next; ElemTy ...
- Linux内核中链表的学习
一.自己学习链表 数组的缺点:(1)数据类型一致:(2)数组的长度事先定好,不能灵活更改. 从而引入了链表来解决数组的这些缺点:(1)结构体解决多数据类型(2)链表的组合使得链表的长度可以灵活设置. ...
- Linux内核中Makefile、Kconfig和.config的关系(转)
我们在编译Linux内核时,往往在Linux内核的顶层目录会执行一些命令,这里我以RK3288举例,比如:make firefly-rk3288-linux_defconfig.make menuco ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析
一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道,pin control subsystem也不例外,被它驱动的硬件叫做pin controller(一般ARM soc的datash ...
- Linux内核中的GPIO系统之(3):pin controller driver代码分析--devm_kzalloc使用【转】
转自:http://www.wowotech.net/linux_kenrel/pin-controller-driver.html 一.前言 对于一个嵌入式软件工程师,我们的软件模块经常和硬件打交道 ...
- CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天
CVE-2019-11477:Linux 内核中TCP协议栈整数溢出漏洞详细分析 代码卫士 今天
- Linux内核中SPI总线驱动分析
本文主要有两个大的模块:一个是SPI总线驱动的分析 (研究了具体实现的过程): 另一个是SPI总线驱动的编写(不用研究具体的实现过程). 1 SPI概述 SPI是英语Serial Peripheral ...
随机推荐
- Android Service服务的生命周期
与activity类似,服务也存在生命周期回调方法,你可以实现这些方法来监控服务的状态变化,并在适当的时机执行一些操作. 以下代码提纲展示了服务的每个生命周期回调方法: public class Ex ...
- 【BZOJ2159】Crash的文明世界(第二类斯特林数,动态规划)
[BZOJ2159]Crash的文明世界(第二类斯特林数,动态规划) 题面 BZOJ 洛谷 题解 看到\(k\)次方的式子就可以往二项式的展开上面考,但是显然这样子的复杂度会有一个\(O(k^2)\) ...
- 【BZOJ4813】[CQOI2017]小Q的棋盘(贪心)
[BZOJ4813][CQOI2017]小Q的棋盘(贪心) 题面 BZOJ 洛谷 题解 果然是老年选手了,这种题都不会做了.... 先想想一个点如果被访问过只有两种情况,第一种是进入了这个点所在的子树 ...
- python测试webservice接口
1.下载库:https://pypi.python.org/pypi/suds-jurko 2.解压后,进入到解压目录,安装库:python3 setup.py install 3.测试获取手机归属地 ...
- luogu2542 航线规划 (树链剖分)
不会lct,所以只能树剖乱搞 一般这种删边的题都是离线倒着做,变成加边 他要求的结果其实就是缩点以后两点间的距离. 然后先根据最后剩下的边随便做出一个生成树,然后假装把剩下的边当成加边操作以后处理 这 ...
- mysql 日志清理
1.查看binlog日志 show binary logs; 2.删除某个日志文件之前的所有日志文件purge binary logs to 'bin.000106'; 3.再看show binary ...
- java配置、IntelliJ IDEA Ultimate激活、
1.下载并安装 Java地址:http://www.oracle.com/technetwork/java/javase/downloads/index.html IntelliJ IDEA地址:ht ...
- Shell变量的取用、删除、取代与替换
<<鸟哥的私房菜>> 注意: 通配符适用的地方:shell命令行或者shell脚本中 正则表达式适用的地方:字符串处理时,一般有一般正则和Perl正则. 在文本过滤工具里,都是 ...
- (stringstream toupper 空格) 词组缩写 hdu2564
词组缩写 Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submi ...
- GTF文件
一.GTF文件格式 Fields must be tab-separated. Also, all but the final field in each feature line must cont ...