Linux component 组件系统架构分析

原创文章,转载请标明出处。

背景介绍

任何架构的提出都是有其现实应用背景的,都是为了解决一些现实问题。而component系统架构的提出就是出于这样一种现实需求:构建功能系统!

系统就是各种功能单元(组件)的有序结合。一个系统,只有集齐了它的所有组件才能正常工作。
可以打个比方:一辆车就是一个系统,它由各种组件–发动机,变速箱,中控台,车轮等构成。

一辆车,只装上发动机,或变速箱。是不能工作的,必须安装了所有的组件,才能开始发动。

而发动的过程,也是有顺序要求的,如先采离合,再挂当(变速箱初始化),再踩油门(发动机初始化),车轮再开始转动。(有序)

所以component架构构建功能系统就包括两方面的作用:

  1. 保证系统安装了所有的组件。
  2. 规定了系统各组件初始化的顺序。

component架构在linux内核中现在主要的应用是用来构建display-subsystem,一个显示子系统,由LCD控制器(vop),接口控制器(mipi,lvds,hdmi),液晶背光,电源等多个独立的功能单元构成。而把这些功能单元构成一个系统,就需要这些功能单元间有一定的协同配合。

如在扫描设备树时,每一个设备节点依次被注册到系统中(一般用platform_device_register)。而只有当所有显示子系统相关的设备节点都注册到系统中时,整个显示系统才能正常工作。

如果没有component架构的参与,在用platform_device_register函数注册每个设备节点时,在其驱动的probe函数中。就已经执行了一系列的初始化工作,而此时系统相关的其他设备节点可能还没有注册到内核中,这样可能就会导致一些问题出现。

在component架构的参下,可以保证在显示子系统的所有功能单元都注册后,才按照一定顺序执行初始化操作。

架构分析

component架构的实现位于drivers/base/componet.c
对系统组件架构有了概念上的理解后,分析架构就比较简单了。

数据结构

component系统组件架构涉及到如下几个数据结构:
struct component用来表示系统组件。
struct master表示需要构建的系统。
struct component_match用来匹配系统需要的组件。并规定了组件的初始化顺序。
component 架构上述结构可以用拼乐高积木的过程来理解:
一个一个形态各异的积木就用struct component来表示,而struct master就是
要拼接成的东西(系统)(例如想拼成一辆车,一只恐龙,或一个房子)。而struct component_match就是你手里的图纸。根据图纸,你就可以在一个个积木(component)中,找出需要的积木,然后拼成一个想要的作品(master)。
根据上面的理解我们在详细的看一下这些结构体中的内容就比较好理解了.

  1. component
struct component {struct list_head node;        struct list_head master_node;struct master *master;bool bound;const struct component_ops *ops;struct device *dev;
};

node:用于加入到component_list链表,该链表保存了注册到组件系统架构中的所有component
master_node:该组件如果最终被某个master选中,则通过此成员用于加入到该master的components链表中,便于后续该master来遍历其组件。
master: 组件所属的master
bound: 表示组件是否已经执行了bind操作
ops: 组件可执行的初始化操作。

  1. master
struct master {struct list_head node;struct list_head components;bool bound;const struct component_master_ops *ops;struct device *dev;struct component_match *match;
};

node:同compnent中node作用一样,用于向组件系统架构中注册master。
components:用于保存该系统(master)下的组件。
ops:master可执行的初始化和退出操作。
match:该master用到的compnent_match,master 应用该match在组件系统架构component_list链表中找到适配于自己的component组件。

3.component_match

我们之前说过组件系统的两方面作用,即保证系统安装了所有组件和保证组件的初始化顺序。compnent_match是实现这两个作用的核心数据结构! component_match中用compare结构体数组表示一个系统(master)中需要的所有组件。每个数组条目代表了一个需要安装的组件。而一个系统中所需的组件数量是不定的,所以采用了零长数组的方式,用于动态扩展组件数量。 而master又是如何通过compnent_match匹配到component的呢?答案就是通过每个compare[]数组元素中的fn函数,int (*fn)(struct device *, void *)函数指针指定了匹配的规则(方法)*data 中保存了fn用到的匹配数据。

struct component_match {size_t alloc;size_t num;struct {void *data;int (*fn)(struct device *, void *);} compare[0];
};

alloc:用于记录分配的compare数组大小。
num:用于记录系统实际需要的组件(compnents)数量。当num等于alloc时,会引发compare数组的空间扩展分配。

我们再回到最初的问题:

如何保证系统安装了所有组件?

答案即是通过component_match的compare数组。master只有在匹配到了compare数组中指定的所有组件后才会执行初始化函数component_master_ops->bind

而又是如何指定各个组件的执行顺序呢?

同样也是通过compare数组,compare数组元素的顺序即指定了加入master 的component链表的顺序,进而决定了component组件初始化的顺序。

4.component_master_ops master执行的操作。

struct component_master_ops {int (*add_components)(struct device *, struct master *);int (*bind)(struct device *);void (*unbind)(struct device *);
};

5.component_ops 组件可以执行的操作。

struct component_ops {int (*bind)(struct device *, struct device *, void *);void (*unbind)(struct device *, struct device *, void *);
};

组件系统工作流程

组件系统大致包括如下操作:

  1. 向组件系统注册component
    注册的component被保存到static LIST_HEAD(component_list);静态链表中。此操作通过int component_add(struct device *dev, const struct component_ops *ops)函数实现。每次向系统注册一个组件时,都会遍历系统的master链表,尝试把该组件绑定到合适的master中去。这个过程是通过master扫描其component_mach,并遍历系统component_list,找出符合mach匹配函数的组件进行绑定。
    当一个master找到了其所有的组件后会执行其初始化函数。
    ret = master->ops->bind(master->dev);

  2. 构建component_match
    component_match作为master查找component的地图,需要在master注册到系统前来构建。系统通过component_match_add函数逐一添加compare条目,用于匹配每一个comonent,这个过程也确定了后续每个component的初始化过程。

  3. 向组件系统注册master。
    注册的master被保存到组件系统架构的static LIST_HEAD(masters)静态链表中。一般通过component_master_add_with_match函数实现。此函数同时会遍历系统component_list链表,找出符合match的组件进行绑定。
    当一个master找到了其所有的组件后会执行其初始化函数。
    ret = master->ops->bind(master->dev);

  4. bind执行
    系统和系统组件初始化函数执行的时机是什么呢?
    无论是通过component_add函数向组件系统中注册component,还是通过component_master_add_with_match函数向系统中注册master时,当mastr安装了其所有的componet后都会执行master->ops->bind(master->dev);操作,在此操作中一般会执行component_bind_all函数,开始执行各组件的初始化:component->ops->bind

代码分析

/** Add a component to be matched.** The match array is first created or extended if necessary.*/
void component_match_add(struct device *dev, struct component_match **matchptr,int (*compare)(struct device *, void *), void *compare_data)
{struct component_match *match = *matchptr;if (IS_ERR(match))return;/*当第一次分配match或mactch中compare数组已经满了时,进行match的空间分配*/if (!match || match->num == match->alloc) {size_t new_size = match ? match->alloc + 16 : 15;//如果是第一次分配compare数组size 为15,如果不是第一次分配,会用原来大小alloc+16match = component_match_realloc(dev, match, new_size);//new_size表示的是分配的compare数组的大小。*matchptr = match;if (IS_ERR(match))return;}/*下面初始化了一个compare数组元素,表示增加了一个组件的匹配项*/match->compare[match->num].fn = compare;match->compare[match->num].data = compare_data;match->num++;//可以看到num记录了match中增加的组件数。最终作为master需要的组件数。
}static struct component_match *component_match_realloc(struct device *dev,struct component_match *match, size_t num)
{struct component_match *new;if (match && match->alloc == num)return match;/*分配component_match空间,num指定了compare结构体数组的大小*/new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL);if (!new)return ERR_PTR(-ENOMEM);if (match) {memcpy(new, match, component_match_size(min(match->num, num)));devm_kfree(dev, match);//扩展match时执行的是重新分配。释放了原来的空间。} else {new->num = 0;}new->alloc = num;//记录了分配的compare数组sizereturn new;
}/*向comonent架构系统中注册一个组件*/
int component_add(struct device *dev, const struct component_ops *ops)
{struct component *component;int ret;component = kzalloc(sizeof(*component), GFP_KERNEL);//分配componentif (!component)return -ENOMEM;component->ops = ops;//对compnent进行初始化,指定了组件的操作函数component->dev = dev;dev_dbg(dev, "adding component (ops %ps)\n", ops);mutex_lock(&component_mutex);list_add_tail(&component->node, &component_list);//把组件加入链表component_listret = try_to_bring_up_masters(component);//遍历注册的每个master,并尝试把compnent绑定之。if (ret < 0) {list_del(&component->node);kfree(component);}mutex_unlock(&component_mutex);return ret < 0 ? ret : 0;
}int component_master_add_with_match(struct device *dev,const struct component_master_ops *ops,struct component_match *match)
{struct master *master;int ret;if (ops->add_components && match)return -EINVAL;if (match) {//根据compare数组的大小重新分配match空间/* Reallocate the match array for its true size */match = component_match_realloc(dev, match, match->num);if (IS_ERR(match))return PTR_ERR(match);}/*master初始化并注册*/master = kzalloc(sizeof(*master), GFP_KERNEL);if (!master)return -ENOMEM;master->dev = dev;master->ops = ops;master->match = match;INIT_LIST_HEAD(&master->components);/* Add to the list of available masters. */mutex_lock(&component_mutex);list_add(&master->node, &masters);//加入链表ret = try_to_bring_up_master(master, NULL);//开始查找组件,进而启动系统。if (ret < 0) {/* Delete off the list if we weren't successful */list_del(&master->node);kfree(master);}mutex_unlock(&component_mutex);return ret < 0 ? ret : 0;
}static int try_to_bring_up_masters(struct component *component)
{struct master *m;int ret = 0;list_for_each_entry(m, &masters, node) {ret = try_to_bring_up_master(m, component);if (ret != 0)break;}return ret;
}/** Try to bring up a master.  If component is NULL, we're interested in* this master, otherwise it's a component which must be present to try* and bring up the master.** Returns 1 for successful bringup, 0 if not ready, or -ve errno.*/
static int try_to_bring_up_master(struct master *master,struct component *component)
{int ret;if (master->bound)return 0;/** Search the list of components, looking for components that* belong to this master, and attach them to the master.*/if (find_components(master)) {/* Failed to find all components */ret = 0;goto out;}if (component && component->master != master) {//如果component !=NULL,component必须属于master。才会执行后面的系统初始化。ret = 0;goto out;}if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) {//设备模型中的设备资源管理相关操作,用于管理设备的资源组。这里不具体介绍。ret = -ENOMEM;goto out;}/* Found all components */ret = master->ops->bind(master->dev);//如果绑定了所有的组件,开始执行系统初始化。一般在此函数中会继续调用component_bind_all函数执行组件的初始化。if (ret < 0) {devres_release_group(master->dev, NULL);dev_info(master->dev, "master bind failed: %d\n", ret);goto out;}master->bound = true;//表示系统已经绑定了所有组件,并执行了初始化。return 1;out:master_remove_components(master);return ret;
}static int find_components(struct master *master)
{struct component_match *match = master->match;size_t i;int ret = 0;if (!match) {/** Search the list of components, looking for components that* belong to this master, and attach them to the master.*/return master->ops->add_components(master->dev, master);}/** Scan the array of match functions and attach* any components which are found to this master.*/for (i = 0; i < match->num; i++) {//遍历了component_match中的compare数组。对比每一个数组项,绑定component到masterret = component_master_add_child(master,match->compare[i].fn,match->compare[i].data);if (ret)break;}return ret;
}

linux component组件架构分析相关推荐

  1. Linux设备驱动程序架构分析之I2C架构(基于3.10.1内核)

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 I2C体系架构的硬件实体包括两部分: 硬件I2C Adapter:硬件I2C Adapter ...

  2. Linux设备驱动程序架构分析之一个I2C驱动实例

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 内核版本:3.10.1 编写一个I2C设备驱动程序的工作可分为两部分,一是定义和注册I2C设备,即i2c_clien ...

  3. linux驱动架构变化,Linux网卡驱动架构分析

    一.网卡驱动架构 由上到下层次依次为:应用程序→系统调用接口→协议无关接口→网络协议栈→设备无关接口→设备驱动. 二.重要数据结构 1.Linux内核中每一个网卡由一个net_device结构来描述. ...

  4. linux的mtd架构分析【转】

    转自:http://blog.csdn.net/column/details/xgbing-linux-mtd.html linux mtd 嵌入式系统的存储有很多不可靠之处.随着使用容量的增大,现在 ...

  5. linux tty结构体,linux tty驱动架构分析

    再看Linux tty驱动过程中发现linux的驱动构架中,面向对象的思想已经根深蒂固.就比如这串口驱动,代码中经常有一些貌似和串口无关的代码,比如,tty_register_driver等.但我们却 ...

  6. Linux设备驱动程序架构分析之SD Spec摘要

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 本文是对SDSpecifications Part 1 Physical Layer Simplified Spec ...

  7. linux内核源码分析笔记

    一.内核源码目录结构 1.Linux 内核源代码包括三个主要部分 1)内核核心代码:包括linux内核整体架构分析笔记描述的各子系统和子模块,以及其他支撑子系统,如:电源管理.linux初始化等. 2 ...

  8. Linux PCI驱动框架分析:(Peripheral Component Interconnect,外部设备互联)

    <DPDK 20.05 | rte_pci_bus思维导图 | 第一版> <linux系统下:IO端口,内存,PCI总线 的 读写(I/O)操作> <Linux指令:ls ...

  9. 【嵌入式Linux学习七步曲之第五篇 Linux内核及驱动编程】PowerPC + Linux2.6.25平台下的I2C驱动架构分析

    PowerPC + Linux2.6.25平台下的I2C驱动架构分析 Sailor_forever  sailing_9806#163.com (本原创文章发表于Sailor_forever 的个人b ...

最新文章

  1. WINDOWS下的各类HOOK
  2. mysql pom依赖关系_常用的POM依赖
  3. 神经网络的核裂变模型
  4. 有关数据结构基础知识(数据结构 严蔚敏版)
  5. 嵌入式小白到大神学习全攻略(学习路线+课程+学习书籍+练习项目)
  6. tomcat不能解析php,tomcat不支持php怎么办
  7. java 注解: Annotation
  8. 自我投资,最好的方式就是写作
  9. 笔记四:onsubmit和onclick的区别
  10. java俄罗斯方块设计报告_俄罗斯方块JAVA课程设计实验报告毕业设计
  11. python实现txt内容合并
  12. perl脚本基础总结(转)
  13. 2010.5.7 MFC中弹对话框以跟踪和程序运行中修改Static Text的Caption属性
  14. 80后的北漂IT人:你的未来在哪?该做好打算了!
  15. 借WS2812 PWM DMA驱动调试浅谈STM32调试思路
  16. linux历史版本内核下载方法。
  17. 矩阵与矩阵转置的加法
  18. 【nodeJS】从nodejs原生的博客网站搭建到 koa框架实现个人博客网站搭建
  19. 内部总线、系统总线、外部总线
  20. 计算机保研面试基础知识,华科计算机保研复试机试题目

热门文章

  1. AndroidTV开发教程(3)
  2. extern “C“详解
  3. C#编写一个扫雷游戏
  4. 力扣739. 每日温度
  5. 隐秘的角落:张东升的人生,给所有职场人提了个醒
  6. tomcat配置https证书
  7. python股票分析系列_Python股票分析系列——基础股票数据操作(二).p4
  8. 大学生求职企业招聘APP(服务端采用jsp+mysql,手机端采用android开发)
  9. Linux向日葵同步剪贴板,远程桌面可双向复制粘贴图片 向日葵客户端9.0.3更新
  10. 未来计算机设想图片,未来学校设想图作文