转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html

  1. linux内核中链表代码分析---list.h头文件分析(二)
  2. 16年2月28日16:59:55
  3. 分析完container_of()宏以后,继续分析list.h文件:
  4. (1)list_entry
  5. 它就是一个container_of宏,都是得到ptr所指地址的这个结构体的首地址
  6. #define list_entry(ptr, type, member) \
  7. container_of(ptr, type, member)
  8. (2) list_first_entry
  9. #define list_first_entry(ptr, type, member) \
  10. list_entry((ptr)->next, type, member)
  11. 这里的ptr是一个链表的头节点,这个宏就是取得这个链表第一元素的所指结构体的首地址。
  12. (3) list_for_each
  13. #define list_for_each(pos, head) \
  14. for (pos = (head)->next; pos != (head); pos = pos->next)
  15. 它实际上就是一个for循环,从头到尾遍历链表,但是看代码发现,遍历链表需要一个head参数即可,这个pos参数好像没什么用啊。。。
  16. 然后就在内核中grep了一番,稍微总结出来一点:
  17. 这是一个for循环,我们通过这个for循环总得做点什么事情吧,以下面这个为例,
  18. 源文件在/driver/input/serio/hil_mlc.c中:
  19. static LIST_HEAD(hil_mlcs);
  20. static void hil_mlcs_process(unsigned long unused)
  21. {
  22. struct list_head *tmp;
  23. read_lock(&hil_mlcs_lock);
  24. list_for_each(tmp, &hil_mlcs) {
  25. struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
  26. ....
  27. }
  28. 可以看到,它通过这个list_for_each函数,将hil_mlcs链表中的每一个成员地址一一赋给了tmp变量,这样,在后面的语句中,就可以通过list_entry这个宏根据tmp变量和list链表头来找到包含他们的整个结构体hil_mlc的首地址,然后就可以对这个结构体里面其他的成员变量进行操作了~~~
  29. 这一块涉及到linux中,比如对于每一个子设备,都建立一个结构体,然后将这些子设备通过一个链表链接起来,操作的时候需要遍历链表中的每一项进行操作,所以就是这个函数存在的意义。
  30. 后面那个 __list_for_each和list_for_each相同,以前这两个函数是不同的,它有一个prefetch预取的操作,我们不再考虑。
  31. (4)list_for_each_prev
  32. #define list_for_each_prev(pos, head) \
  33. for (pos = (head)->prev; pos != (head); pos = pos->prev)
  34. 与list_for_each相似,只是它是从尾到头遍历链表,将链表中的每一项都取出来操作。
  35. (5)list_for_each_safe
  36. #define list_for_each_safe(pos, n, head) \
  37. for (pos = (head)->next, n = pos->next; pos != (head); \
  38. pos = n, n = pos->next)
  39. 这个实际上就是一个for循环,从头到尾遍历链表。这里使用了n来记录pos的下一个,这样处理完一 个流程之后再赋给pos,避免了删除pos结点造成的问题。这个函数是 专门为删除结点是准备的。
  40. 注:list_for_each(pos, head)和list_for_each_safe(pos, n, head)都是从头至尾遍历链表的,但是对于前者 来说当操作中没有删除结点的时候使用,但是如果操作中有删除结点的操作的时候就使用后者,对于后面带safe的一般都是这个目的。
  41. (6) list_for_each_prev_safe
  42. #define list_for_each_prev_safe(pos, n, head) \
  43. for (pos = (head)->prev, n = pos->prev; \
  44. pos != (head); \
  45. pos = n, n = pos->prev)
  46. 同理,这个函数与 list_for_each_prev相似。
  47. /* 注意上面的几个函数,他们的行参里面有pos与下面函数行参里面的pos不同,上面函数的操作都是pos = (head)->next等等的操作,所以pos是list_head类型的,下面函数的操作是pos = list_entry()等等的操作,所以他们是list_head结构体所嵌入的结构体的指针形式的,我们称之为宿主结构体。 */
  48. (7) list_for_each_entry
  49. #define list_for_each_entry(pos, head, member)                \
  50. for (pos = list_entry((head)->next, typeof(*pos), member);    \
  51. &pos->member != (head);     \
  52. pos = list_entry(pos->member.next, typeof(*pos), member))
  53. 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
  54. 这个函数是根据member成员遍历head链表(member成员一般都嵌入在宿主结构体中),并且将每个结构体的首地址赋值给pos,这样的话,我们就 可以在循环体里面通过pos来访问该宿主结构体变量的其他成员了。 下面用最近分析的V4L2里面的一段代码来举例说明,代码位于v4l2-device.c中:
  55. int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
  56. {
  57. struct video_device *vdev;
  58. struct v4l2_subdev *sd;
  59. list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
  60. if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
  61. continue;
  62. 。。。。
  63. }
  64. 可以分析出来, v4l2_device结构体里面含有这个 subdevs链表头,sd是指向v4l2_subdev结构体的指针,list是一个list_head结构,在之前的代码中,肯定有操作已经将各个子设备结构体v4l2_subdev全部链接进 v4l2_device结构体里面的 subdevs链表里面,这时候需要做的就是从这个v4l2_device结构体里面的 subdevs链表里一一取出各个子设备结构体v4l2_subdev,然后操作它的各个成员变量。上述代码就是完成这个工作。
  65. (8) list_for_each_entry_reverse
  66. #define list_for_each_entry_reverse(pos, head, member)            \
  67. for (pos = list_entry((head)->prev, typeof(*pos), member);    \
  68. &pos->member != (head);     \
  69. pos = list_entry(pos->member.prev, typeof(*pos), member))
  70. 与上面那个函数类似,反向遍历链表。
  71. (9) list_prepare_entry
  72. #define list_prepare_entry(pos, head, member) \
  73. ((pos) ? : list_entry(head, typeof(*pos), member))
  74. 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
  75. 这个函数的功能就是如果pos非空,那么pos的值就为其本身,如果pos为空,那么就从链表头强制扩展一
  76. 个虚pos指针,这个宏定义是为了在list_for_each_entry_continue()中使用做准备的。
  77. (10)list_for_each_entry_continue
  78. #define list_for_each_entry_continue(pos, head, member)         \
  79. for (pos = list_entry(pos->member.next, typeof(*pos), member);    \
  80. &pos->member != (head);    \
  81. pos = list_entry(pos->member.next, typeof(*pos), member))
  82. 这个函数与list_for_each_entry很相似,但是肯定是不同的,它是从pos所在的宿主结构体的下一个开始遍历链表,并不是从链表的头部开始遍历,从它的名字可以看出来。主要的区别就是我标红的位置。
  83. (11)list_for_each_entry_continue_reverse
  84. #define list_for_each_entry_continue_reverse(pos, head, member)        \
  85. for (pos = list_entry(pos->member.prev, typeof(*pos), member);    \
  86. &pos->member != (head);    \
  87. pos = list_entry(pos->member.prev, typeof(*pos), member))
  88. 与上一个函数类似,从pos位于的宿主结构体的上一个开始反向变量链表。
  89. (12)list_for_each_entry_from
  90. #define list_for_each_entry_from(pos, head, member)             \
  91. for (; &pos->member != (head);    \
  92. pos = list_entry(pos->member.next, typeof(*pos), member))
  93. 从当前pos所位于的宿主结构体开始遍历链表。
  94. (13)后面的几个函数带safe的函数,他们与上面讲的函数都很相似,只是保存了pos的下一个宿主结构体,这些函数是专门为删除结点是准备的。就不分析他们了。
  95. 至此,对于普通链表的操作我们就分析完了,这个list.h中后面的代码主要是为了哈西表设计的,就暂时先不分析,等到学了哈西表再分析他们^o^~
  1. linux内核中链表代码分析---list.h头文件分析(二)
  2. 16年2月28日16:59:55
  3. 分析完container_of()宏以后,继续分析list.h文件:
  4. (1)list_entry
  5. 它就是一个container_of宏,都是得到ptr所指地址的这个结构体的首地址
  6. #define list_entry(ptr, type, member) \
  7. container_of(ptr, type, member)
  8. (2) list_first_entry
  9. #define list_first_entry(ptr, type, member) \
  10. list_entry((ptr)->next, type, member)
  11. 这里的ptr是一个链表的头节点,这个宏就是取得这个链表第一元素的所指结构体的首地址。
  12. (3) list_for_each
  13. #define list_for_each(pos, head) \
  14. for (pos = (head)->next; pos != (head); pos = pos->next)
  15. 它实际上就是一个for循环,从头到尾遍历链表,但是看代码发现,遍历链表需要一个head参数即可,这个pos参数好像没什么用啊。。。
  16. 然后就在内核中grep了一番,稍微总结出来一点:
  17. 这是一个for循环,我们通过这个for循环总得做点什么事情吧,以下面这个为例,
  18. 源文件在/driver/input/serio/hil_mlc.c中:
  19. static LIST_HEAD(hil_mlcs);
  20. static void hil_mlcs_process(unsigned long unused)
  21. {
  22. struct list_head *tmp;
  23. read_lock(&hil_mlcs_lock);
  24. list_for_each(tmp, &hil_mlcs) {
  25. struct hil_mlc *mlc = list_entry(tmp, hil_mlc, list);
  26. ....
  27. }
  28. 可以看到,它通过这个list_for_each函数,将hil_mlcs链表中的每一个成员地址一一赋给了tmp变量,这样,在后面的语句中,就可以通过list_entry这个宏根据tmp变量和list链表头来找到包含他们的整个结构体hil_mlc的首地址,然后就可以对这个结构体里面其他的成员变量进行操作了~~~
  29. 这一块涉及到linux中,比如对于每一个子设备,都建立一个结构体,然后将这些子设备通过一个链表链接起来,操作的时候需要遍历链表中的每一项进行操作,所以就是这个函数存在的意义。
  30. 后面那个 __list_for_each和list_for_each相同,以前这两个函数是不同的,它有一个prefetch预取的操作,我们不再考虑。
  31. (4)list_for_each_prev
  32. #define list_for_each_prev(pos, head) \
  33. for (pos = (head)->prev; pos != (head); pos = pos->prev)
  34. 与list_for_each相似,只是它是从尾到头遍历链表,将链表中的每一项都取出来操作。
  35. (5)list_for_each_safe
  36. #define list_for_each_safe(pos, n, head) \
  37. for (pos = (head)->next, n = pos->next; pos != (head); \
  38. pos = n, n = pos->next)
  39. 这个实际上就是一个for循环,从头到尾遍历链表。这里使用了n来记录pos的下一个,这样处理完一 个流程之后再赋给pos,避免了删除pos结点造成的问题。这个函数是 专门为删除结点是准备的。
  40. 注:list_for_each(pos, head)和list_for_each_safe(pos, n, head)都是从头至尾遍历链表的,但是对于前者 来说当操作中没有删除结点的时候使用,但是如果操作中有删除结点的操作的时候就使用后者,对于后面带safe的一般都是这个目的。
  41. (6) list_for_each_prev_safe
  42. #define list_for_each_prev_safe(pos, n, head) \
  43. for (pos = (head)->prev, n = pos->prev; \
  44. pos != (head); \
  45. pos = n, n = pos->prev)
  46. 同理,这个函数与 list_for_each_prev相似。
  47. /* 注意上面的几个函数,他们的行参里面有pos与下面函数行参里面的pos不同,上面函数的操作都是pos = (head)->next等等的操作,所以pos是list_head类型的,下面函数的操作是pos = list_entry()等等的操作,所以他们是list_head结构体所嵌入的结构体的指针形式的,我们称之为宿主结构体。 */
  48. (7) list_for_each_entry
  49. #define list_for_each_entry(pos, head, member)                \
  50. for (pos = list_entry((head)->next, typeof(*pos), member);    \
  51. &pos->member != (head);     \
  52. pos = list_entry(pos->member.next, typeof(*pos), member))
  53. 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
  54. 这个函数是根据member成员遍历head链表(member成员一般都嵌入在宿主结构体中),并且将每个结构体的首地址赋值给pos,这样的话,我们就 可以在循环体里面通过pos来访问该宿主结构体变量的其他成员了。 下面用最近分析的V4L2里面的一段代码来举例说明,代码位于v4l2-device.c中:
  55. int v4l2_device_register_subdev_nodes(struct v4l2_device *v4l2_dev)
  56. {
  57. struct video_device *vdev;
  58. struct v4l2_subdev *sd;
  59. list_for_each_entry(sd, &v4l2_dev->subdevs, list) {
  60. if (!(sd->flags & V4L2_SUBDEV_FL_HAS_DEVNODE))
  61. continue;
  62. 。。。。
  63. }
  64. 可以分析出来, v4l2_device结构体里面含有这个 subdevs链表头,sd是指向v4l2_subdev结构体的指针,list是一个list_head结构,在之前的代码中,肯定有操作已经将各个子设备结构体v4l2_subdev全部链接进 v4l2_device结构体里面的 subdevs链表里面,这时候需要做的就是从这个v4l2_device结构体里面的 subdevs链表里一一取出各个子设备结构体v4l2_subdev,然后操作它的各个成员变量。上述代码就是完成这个工作。
  65. (8) list_for_each_entry_reverse
  66. #define list_for_each_entry_reverse(pos, head, member)            \
  67. for (pos = list_entry((head)->prev, typeof(*pos), member);    \
  68. &pos->member != (head);     \
  69. pos = list_entry(pos->member.prev, typeof(*pos), member))
  70. 与上面那个函数类似,反向遍历链表。
  71. (9) list_prepare_entry
  72. #define list_prepare_entry(pos, head, member) \
  73. ((pos) ? : list_entry(head, typeof(*pos), member))
  74. 第一个参数为传入的遍历指针,指向宿主数据结构,第二个参数为链表头,为list_head结构,第三 个参数为list_head结构在宿主结构中的成员名。
  75. 这个函数的功能就是如果pos非空,那么pos的值就为其本身,如果pos为空,那么就从链表头强制扩展一
  76. 个虚pos指针,这个宏定义是为了在list_for_each_entry_continue()中使用做准备的。
  77. (10)list_for_each_entry_continue
  78. #define list_for_each_entry_continue(pos, head, member)         \
  79. for (pos = list_entry(pos->member.next, typeof(*pos), member);    \
  80. &pos->member != (head);    \
  81. pos = list_entry(pos->member.next, typeof(*pos), member))
  82. 这个函数与list_for_each_entry很相似,但是肯定是不同的,它是从pos所在的宿主结构体的下一个开始遍历链表,并不是从链表的头部开始遍历,从它的名字可以看出来。主要的区别就是我标红的位置。
  83. (11)list_for_each_entry_continue_reverse
  84. #define list_for_each_entry_continue_reverse(pos, head, member)        \
  85. for (pos = list_entry(pos->member.prev, typeof(*pos), member);    \
  86. &pos->member != (head);    \
  87. pos = list_entry(pos->member.prev, typeof(*pos), member))
  88. 与上一个函数类似,从pos位于的宿主结构体的上一个开始反向变量链表。
  89. (12)list_for_each_entry_from
  90. #define list_for_each_entry_from(pos, head, member)             \
  91. for (; &pos->member != (head);    \
  92. pos = list_entry(pos->member.next, typeof(*pos), member))
  93. 从当前pos所位于的宿主结构体开始遍历链表。
  94. (13)后面的几个函数带safe的函数,他们与上面讲的函数都很相似,只是保存了pos的下一个宿主结构体,这些函数是专门为删除结点是准备的。就不分析他们了。
  95. 至此,对于普通链表的操作我们就分析完了,这个list.h中后面的代码主要是为了哈西表设计的,就暂时先不分析,等到学了哈西表再分析他们^o^~

linux内核中链表代码分析---list.h头文件分析(二)【转】相关推荐

  1. linux内核中链表代码分析---list.h头文件分析(一)

    linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17:13:14 在学习数据结构时,有一个重要的知识点就是链表.对于链表的一些基本操作,它的最好学习资料就是内核中的li ...

  2. 如下为利用Linux内核链表创建,Linux内核中链表的实现与应用

    链表(循环双向链表)是Linux内核中最简单.最常用的一种数据结构. 1.链表的定义 struct list_head { struct list_head *next, *prev; } 这个不含数 ...

  3. Linux内核中的位操作:ffs.h,fls.h

    今天阅读源码时遇到一个函数:ffs,它时内核中实现的位操作函数,用来查找二进制表示数中第一个为1的位.与ffs对应的还有fls.h,用来查找二进制数中最后一个为1的位. 例如:整数32,对应的二进制为 ...

  4. a.out.h 头文件分析 \linux-1.0\linux\include\linux\a.out.h

    #ifndef __A_OUT_GNU_H__ #define __A_OUT_GNU_H__#define __GNU_EXEC_MACROS__#ifndef __STRUCT_EXEC_OVER ...

  5. 2021-12-10 Linux内核中watchdog,用户层喂狗程序分析

    一.我这里是MTK平台,喂狗的代码在\system\core\watchdogd\,实际测试,如果write(fd, "", 1);注释掉,开机后过段时间会reboot. 1.\s ...

  6. 4.4 ipu_param_mem.h头文件分析

    1.下面这两个结构体是本文件的核心结构体. struct ipu_ch_param_word { uint32_t data[5]; uint32_t res[3]; }; struct ipu_ch ...

  7. HAL层,.sensors.h 头文件分析

    Google为Sensor提供了统一的HAL接口,不同的硬件厂商需要根据该接口来实现并完成具体的硬件抽象层, Android中Sensor的HAL接口定义在:hardware/libhardware/ ...

  8. Linux内核中max()宏的奥妙何在?(一)

    Linux内核中max()宏的奥妙何在?(一) 1.max()宏那点事 在Linux内核中,有这样四个比较大小的函数,如下: max(x,y) //两个数求最大值 min(x,y) //两个数求最小值 ...

  9. c语言中的stdbool.h头文件,【C语言】中的stdbool.h头文件

    C语言中的stdbool.h头文件 一.相关基础知识 二.具体内容 Win7下安装的VS2015中的stdbool.h的位置为: F:\Program Files (x86)\Microsoft Vi ...

最新文章

  1. python namedtuple用法_Python的collections模块中namedtuple结构使用示例
  2. nokia 上的好玩应用(转载)
  3. iOS GCD中级篇 - dispatch_group的理解及使用
  4. bizmsg是什么文件可以删除吗_C盘里的文件夹都是什么?可以删除吗?哪些可以删除?...
  5. shiro修改html不生效,shiro中anon配置不生效
  6. * poj 1062 昂贵的礼物 dijkstra 枚举区间
  7. 1.jenkins持续集成-jenkins安装
  8. typedef int Myfunc(const char *,const struct stat *,int)
  9. Word VBA:批量更改图片的格式
  10. 如何用Java解压缩WAR文件
  11. 计算机科学之父——Alan Turing及相关电影介绍
  12. mac打开nh文件-cajviewer.dmg
  13. Tesla M40 24G 在Win11上的双显卡显示实现、改风冷
  14. 详解交换机端口级联连接方式
  15. MySQL:连接错误
  16. 名帖191 米芾 行书《苕溪诗卷》
  17. 安卓原生系统开发与逆向工程
  18. Lotus Miner和分布式设置
  19. 75寸的电视长宽各是多少厘米
  20. 滑动轨迹 曲线 python_Python 模拟真实运动轨迹,轻松完成长跑和打卡

热门文章

  1. 中国饲用微生态制剂行业十四五研发方向与专项应用调研报告2022年
  2. 全球及中国高压和超高压波纹铝护套交联聚乙烯电缆行业产销现状与投资策略建议报告2021-2027年版
  3. 谋定新型农业主体-农业大健康·万祥军:农业高质量发展规划
  4. laravel 安装配置前准备
  5. 利用反射球实现镜面效果
  6. SQL判断语句用法和多表查询
  7. 使用 Maven 执行 java main class(java应用程序)
  8. .NET三种事务处理详解
  9. Entity Framework 学习中级篇3—存储过程(中)
  10. NavReady 试用小记(2)