好久都没来过啦.最近又在重看linux device driver这本书.之前看的时候忽略掉了linux设备模型这个章节,这次仔细看了一下,可看的相当头痛,迷迷糊糊...话说源代码时最好的工具书,于是查阅了一下源代码,也大致阅读了一些,没太仔细的句句分析,只是关注了与设备模型相关的。
linux设备模型的抽象是总线、设备、模型。按照这个顺序来分析就可以勾勒出linux设备模型。
先来几句废话.好久没有看过内核的源码了.感觉都生疏了好多...最近又在重新看linux device driver这本书,把14章 linux设备模型好好看了一下,之前没认真看.主要是没耐心.认真看过之后发现好迷糊.不知所云.于是乎,只好翻看源码来找答案.源码是最好的工具书嘛... 只不过需要花些时间来研究一下源代码.
关于linux 设备模型的结构网上有许多的文章.我也不想再重复这些东西.只是我看过一些仍然觉得脑袋一片浆糊.不知道是怎么回事.
首先要理解模型里的基石kobject以及kset的作用.网上许多介绍它们在设备模型中的地位,就不重复了。想深究的童鞋可以看看lib/kobject.c文件当中的一些函数.如kobject_add,kset_register等函数。可以帮助理解,而且在以后分析时也用得着.
设备模型主要理清了总线、设备和驱动三者之间的关系,fudan_abc分析的相当有趣.可感觉不太深入。感情去可以去其blog上找。
采用up-bottom方式.从总线开始分析,入口就在drivers/base/bus.c文件中的
int bus_register(struct bus_type *bus)函数当中.认真阅读该函数,并理解其调用函数.就可以更加深入的理解kobject以及
kset在设备模型中的作用.差不多设备模型的骨架就可以摸清楚了.我从该函数中画出了一张结构图作为总结.
从图中就可以知道.内核所知道的所有总线都在bus_kset容器当中.而从总线中又可以找到连接到该总线设备容器以及驱动容器.当然这种说法是不完全正确的.可以这样理解,这都是通过kobject以及kset来到达的一种层次式的结构.
连接到某总线的设备以及驱动之间也有关系,但还没看到这里,就此作罢。有空再看。
通过bus_register()函数可以弄清楚整个设备模型的骨架。大致的架构弄清楚之后就可以继续填充这个模型了。根据总线、设备、驱动由高层到底层的顺序,可以以设备为入口点来继续探索。
设备的入口在device_register()函数当中。该函数相当简单,仅包含两个函数调用,第一个是device_initialize(),第二个是device_add()函数。根据调用顺序依次阅读其源码。抛开与设备模型无关的代码不谈,虽然sysfs也属于设备模型,但可以忽略它。没有显示的说明,所列出的代码都来自
drivers/base/core.c文件。
首先第一个函数device_initialize()。
570 dev->kobj.kset = devices_kset;
由此可见,所有的设备都在这个名叫devices_kset的容器当中,可以称其为设备容器。
571 kobject_init(&dev->kobj, &device_ktype);
显然,所有的设备都属于device_ktype类型的。
在设备模型当中,该函数的作用就是将该设备放入设备容器当中,并设置设备类型。
第二个函数device_add()。
971 parent = get_device(dev->parent);
972 setup_parent(dev, parent);
这两条语句设置该设备的内嵌kobject的父kobject为其父设备的kobject。当然,该设备可能没有父设备。再次体现出kobject在设备模型中对理清各个设备层次关系的重要性。
980 error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
将该设备添加到上层设备的内嵌kobject链表当中。即dev->kobj.parent.list链表。dev->kobj.parent可能为空,由kobject_add函数可知,会将dev->kobj.kset.kobj设置为dev->kobj.parent。
而dev->kobj.kset就是devices_kset。这样对于没有父设备的设备就直接在devices_kset的下层。而devices_kset只是一个容器.所以该设备就属于顶层设备。
1010 error = bus_add_device(dev);
这里在设备层次上暴露出与总线的交互关系.跟踪其函数调用。只有一条语句与设备模型相关。
在drivers/base/bus.c文件当中:
467 klist_add_tail(&dev->p->knode_bus, &bus->p->klist_devices);
该语句就是将该设备添加到其所连接到的总线上设备链表当中。由此可知,通过总线的设备链表可以获知所有连接到该总线的设备。相当明了了... (有兴趣的童鞋可以浏览一下klist的接口)
1026 bus_probe_device(dev);
设备在驱动模型自设备往上的逻辑结构已经完成了.现在就该给device找个能够驱动它的驱动了。不在此做延伸.这里涉及到了总线、设备、驱动三者之间的关系.而这里仅以设备位主线来考察。留待驱动这个层次分析清楚之后再作展开。
1027 if (parent)
1028 klist_add_tail(&dev->p->knode_parent,
1029 &parent->p->klist_children);
如果该设备有父设备.就将其添加到父设备的子设备链表当中。
以上就是device_register所做的所有事情.其中主要就是理清该设备在设备模型中与总线、父设备以及驱动之间的所有复杂的关系.
根据设备、总线、驱动这条线,已经分析过总线以及设备。现在来看一下设备模型中驱动这个层次。
和总线、设备的分析方法一样,分析驱动的入口在driver_register()函数当中.定义在drivers/base/driver.c文件中。根据源代码来分析:
235 other = driver_find(drv->name, drv->bus);
在driver_find函数当中
282 struct kobject *k = kset_find_obj(bus->p->drivers_kset, name);
bus->p->drivers_kset是该总线上的设备所对应的所有驱动的容器。在其中查找驱动名与name匹配的"驱动",实际上只是获取了驱动结构指向的私有结构里内嵌的kobject,在通过container_of就可以获取这个私有结构priv.而priv->driver就是所属驱动结构了。从这条语句可以窥出一些设备模型的结构组织。
243 ret = bus_add_driver(drv);
bus_add_driver()定义在drivers/base/bus.c文件中。
668 priv->kobj.kset = bus->p->drivers_kset;
priv是该驱动结构所拥有的私有结构信息。这条语句就是将该驱动结构放入其所将驱动的设备所在总线的驱动容器当中。当然这个说法不严谨,但按照面向对象的思想来思考。
669 error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
670 "%s", drv->name);
在调用kobject_init_and_add函数时将其父结构参数设置为NULL,故将把其父结构设置为
bus->p->drivers_kset.kobj,并添加到链表当中。
679 klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
将该驱动加入对应设备连接的总线的驱动链表上。由此可知,只要有总线结构,就可以通过遍历总线结构中的驱动链表访问连接到该总线的所有设备对应的驱动。
从以上可知.driver_register()函数主要设置驱动结构所属容器,以及加入到对应设备所连接的总线的驱动链表上。一个是设置在驱动模型中的层次,一个方便从总线这个层次上管理所有的驱动。
在该函数中遗漏了将驱动与设备绑定的过程.这与设备模型似乎没多大关系。但其中的一个函数调用
bus_for_each_dev():
klist_iter_init_node(&bus->p->klist_devices, &i,
(start ? &start->p->knode_bus : NULL));
遍历驱动所将驱动的设备所在总线上的所有设备,并条用一个指定函数。bus->p->klist_devices就是链接在该总线上的所有设备。在驱动与设备绑定的过程当中,若发现匹配的设备以及能够驱动的设备.通过调用bus的probe方法或者驱动自身的probe方法所确定。则会将能驱动的设备加入到驱动结构的devices字段构成的链表当中。而在设备结构中会将driver字段指向能驱动该设备的驱动结构。需要注意的是一个设备只对应一个驱动,而驱动却能够驱动多个设备。