输入子系统匹配过程之list_for_each_entry()函数分析

来源:互联网 发布:宁波淘宝商城外包 编辑:程序博客网 时间:2024/05/17 08:10

输入子系统匹配过程之list_for_each_entry()函数分析

list_for_each_entry()函数在内核中应用广泛,本文结合input_subsystem详细分析了list_for_each_entry()函数,希望对读者理解其实现过程能有所帮助,我水平有限,难免有错,欢迎批评指正。

  • 分析前的准备
  • list_for_each_entry(handler, &input_handler_list, node)的参数分析
  • offsetof (type,member)宏函数分析
  • container_of(ptr, type, member)宏函数分析
  • list_entry(ptr, type, member)宏函数分析
  • list_for_each_entry()宏函数分析*

分析前的准备

根据上一篇“Linux输入子系统过程分析笔记”的分析,系统已经自动构造好了input_table[]数组,现在我们自己写的驱动已经分配和设置input_dev结构体,并调用input_register_device()注册,注册过程调用list_add_tail(&dev->node, &input_dev_list);,把dev->node添加到input_dev_list链表,以后就可以通过链表里的node 找到node所在的dev.接下来就是调用
list_for_each_entry(handler, &input_handler_list, node)
input_attach_handler(dev, handler);
使input_dev 与合适的 handler 建立连接。

源码中list_for_each_entry(handler, &input_handler_list, node)
后面是没有分号的,这是一个宏函数,展开后是一个for循环结构,对每一次循环,执行input_attach_handler(dev,handler).对于list_for_each_entry()函数的具体实现,下面会详细说一说,这里先只说它的功能,方便建立连接的过程就好。

list_for_each_entry(handler, &input_handler_list, node)的参数分析

首先要搞清楚这三个参数:
第二个参数:
input_handler_list是已经初始化好了的链表,在哪里被初始化?具体是在 driver/input 目录下的evdev.c keyboard.c mousedev.c 等文件里 通过调用
input_register_handler()==>list_add_tail(&handler->node, &input_handler_list);构造。
&input_handler_list 的内容是 handler 的 node 成员,不是 handler 本身。handler->node 与第三个参数node是相同的数据类型。这一过程与构建input_dev_list过程相似:
input_register_device()==>list_add_tail(&dev->node,
&input_dev_list);
第一个参数:
handler 是input_register_device()函数 里面声明的一个空指针,用来指向第三个参数node所在的handler结构体。
第三个参数:
node 来自于哪里,我自己也不是很清楚,就不误导大家了。

list_for_each_entry(handler, &input_handler_list, node)的作用是把第一个参数handler指向第三个参数node 所在的input_handler结构体的地址,然后调用input_attach_handler(dev,handler),input_attach_handler里面
再调用input_match_device(handler->id_table, dev);如果match成功则调用 handler->connect(handler, dev, id);建立dev与handler的连接,一个dev可能与几个handler建立连接,handler用于接收和处理dev发生的事件dev也可以响应由handler发来的动作命令。

offsetof (type,member)宏函数分析

实现:
#define offsetof(struct_t,member) ((size_t)(char *)&((struct_t *)0)->member)

参数说明:member 是type 类型结构体的成员
(struct_t *)0 把0强制转换为 struct_t 类型的地址,然后就可以通过-> 操作符找到首地址为零的struct_t 类型的结构体的member 成员,即(struct_t *)0)->member,
&((struct_t *)0)->member)即表示member 的地址,由于这个member 所在结构体的首地址为零,则这个member的地址值相当于 任何一个具体的member的地址在结构体地址中的偏移量。
总之一句话,offsetof(type,member)返回的是 member地址-member所在结构体首地址,即偏移量。

container_of(ptr, type, member)宏函数分析

container_of源码:

        * container_of - cast a member of a structure out to the containing structure        * @ptr:        the pointer to the member.        * @type:       the type of the container struct this is embedded in.        * @member:     the name of the member within the struct.        *        */        #define container_of(ptr, type, member)({ \             const typeof(((type *)0)->member)*__mptr = (ptr); \             (type *)((char *)__mptr - offsetof(type,member));}) 

第二个参数 type 是结构体的类型,(type *)0 的作用是把0 强制转换为 type 类型的地址,例如若type是 struct input_handler,则把0强制转换为 struct input_handler 类型的地址。经过(type *)0后,就可以使用-> 操作符找到type类型结构体的member成员了,即(type *)0)->member,与offsetof()的分析一样,只是offsetof必须用零地址才能求出偏移量,而这里的0地址没有什么特别含义。

typeof( ((type *)0)->member )作用是取member 类型,若member是int类型,则typeof( ((type *)0)->member ) 整句话相当于int,
那么 const typeof( ((type *)0)->member ) *__mptr = (ptr);
相当于 const int *__mptr = (ptr);

所以第一句话const typeof( ((type *)0)->member ) *__mptr = (ptr)的作用就是 声明一个与member相同类型的指针__mptr =(ptr),ptr的类型与member 的类型也是一样的,通常ptr是由member类型对象构成的链表头。

那么经过const typeof( ((type *)0)->member ) *__mptr = (ptr),__mptr就指向了某个具体的member对象,那么( (char *)__mptr - offsetof(type,member)就是这个具体的member对象所在的type类型结构体的首地址了,最后把这个地址强制转换为type类型(type *)( (char *)__mptr - offsetof(type,member) );

一句话:container_of(ptr, type, member)返回的是第一个参数ptr指向的某个具体的member( 不是第三个参数,第三个参数member用于offsetof函数。)所在的type类型结构体的首地址。第三个参数member的作用是提供一个数据类型。

list_entry(ptr, type, member)宏函数分析

list_entry(ptr, type, member)源码:

   /**     * list_entry - get the struct for this entry     * @ptr:    the &struct list_head pointer.     * @type:   the type of the struct this is embedded in.     * @member: the name of the list_struct within the struct.     */    #define list_entry(ptr, type, member) \        container_of(ptr, type, member)

一句话,就是container_of()。

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))

时刻注意这是一个宏函数,第三个参数member也是替换字符串用的,是结构体成员的名称,它的作用是提供一个数据类型,宏展开成一个函数后,也并没有引用member传来的地址,pos->member不是第三个参数member。

第一行pos = list_entry((head)->next, typeof(*pos), member)的作用就是返回
head->next所指向的member 所在的pos类型结构体的首地址。

第二句prefetch(pos->member.next), &pos->member != (head)是循环条件, 先不管,不影响分析。

第三句pos = list_entry(pos->member.next, typeof(*pos), member))和第一句一样, pos指向的是下一个member所在的pos类型结构体的首地址,这样pos就历遍了一个由pos类型结构体组成的链表 一样。

list_for_each_entry()函数在内核中大量应用在各种情形中,具体在input_register_device()函数里面有

list_for_each_entry(handler, &input_handler_list, node)            input_attach_handler(dev, handler);

上面说了,这两行代码是要一起分析的,对于每一个handler 调用input_attach_handler(dev, handler),可能有多次都匹配成功的情况,对于input_attach_handler()的实现,由于我理解不足和表达能力极差,就不分析误导大家了。

Linux输入子系统过程分析笔记:
http://blog.csdn.net/qq_22863733/article/details/78153266

阅读全文
0 0
原创粉丝点击