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

输入子系统匹配过程之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

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值