ldd随笔(1)-linux设备模型

一下只是个人学习后的理解,可能有很多不对的地方。

要学习linux的设备驱动模型,首先必须要知道kobject和kset的概念,下面是kobject在2.6.38的源码中的实现。

struct kobject {
    const char      *name;      //名称,可能在sysfs中创建对应的目录
    struct list_head    entry;    //标准链表,用于被kset连接起来
    struct kobject      *parent; //指向父kobject的指针
    struct kset     *kset;         //指向所属的kset的指针
    struct kobj_type    *ktype; //包含的kobj_type, 用指向不同的ktype来表示不同的object
    struct sysfs_dirent *sd;      //在sysfs中创建目录时用到的结构
    struct kref     kref;           //引用计数
    unsigned int state_initialized:1;
    unsigned int state_in_sysfs:1;
    unsigned int state_add_uevent_sent:1;
    unsigned int state_remove_uevent_sent:1;
    unsigned int uevent_suppress:1;
};

kobject里面比较重要的一个是kobj_type结构的指针:

struct kobj_type {
    void (*release)(struct kobject *kobj);  //当此kobj被完全释放(引用为0)时调用
    const struct sysfs_ops *sysfs_ops;  //与sysfs相关的操作
    struct attribute **default_attrs;  //代表了一系列属性。
    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
    const void *(*namespace)(struct kobject *kobj);
};

kobject中的ktype指向不同的kobj_type可以代表着不同的类型,以及所执行的不同操作。kobject可能在sysfs中表现为一个目录,而它指向的kobj_type中包含的一系列struct attribute则代表着这个目录下的文件。每一个属性对应了一个文件,可以设置它的读写权限,名称(文件名)还有就是当读或者写这些文件时,调用的show和store函数。可以利用这些属性文件,进行信息的展示和配置的修改等。

kobject只是代表着某个结点,并不能构成特定的结构,所以还存在一个kset,使得它们可以构成一个树状结构。kset的实现如下:

struct kset {
    struct list_head list;  //链表头,用以将下层的kobject串起来
    spinlock_t list_lock;  //链表的锁,只能串行操作
    struct kobject kobj;  //kset包含一个kobject
    const struct kset_uevent_ops *uevent_ops;  //为了支持热插拔所提供的结构
};

kset里面包含了一个kobject,而不止是一个指针。所以可以把kset看成是一个kobject的扩展,所有对kobject的操作,都适用与kset(操作其中的kobject,但是内核为kobject和kset分别提供了一套类似的操作)。如果用C++表述,kobject就是一个基类,而kset是它的派生类。kset可以通过它的list包含住很多子kobject,而这些子kobject又可以通过parent和kset指针指向它的父kobject(kset);并且kset也是(包含了)一个kobject,所以它还可以被其它的kset当做子kobject。这样就构成了一个树状结构,其中kobject是叶子结点,而kset是非叶子结点或者根结点,正好和目录与文件的关系类似。目录本身是一个文件,但是它可以包含其它文件或目录,同时又被其它目录含(根目录除外)。所以sysfs中的文件结构通常对应了内核中的kobject结构。每一个目录代表一个kobject(或者kset),而此目录里面的子目录又代表了这个kobject下面的子kobject,目录里的文件代表了kobject的一系列属性。

而kset_uevent_ops(对应老版内核中的hotplug,热插拔)指向了一组操作,包括过滤,热插拔等,当往一个kset中添加新的kobject时,便会触发这些操作,首先会调用filter表示是否过滤这个事件,然后在调用热插拔处理函数来向环境变量里添加值,从而达到通知用户空间的目的。假如次kset没有设置这个uevent_ops指针,便会往它的父kobject迭代查找,直到找到为止(若到根kobject都没有,就忽略)。再进行调用。

kobject通常不单独使用,而是嵌入到其他结构,代表就是pci core和usb core,它们是内核提供的,利用kobject来构造的树状结构驱动框架。

linux内核利用以上的框架,包装出了三个概念,那就是总线,设备,和驱动。总线可以看成是树状结构中的非叶子结点(包括根结点),设备和驱动可以看成其中的叶子结点;当有设备插入,便可利用上面uevent_ops的机制,添加环境变量,然后调用udev(hotplug)来判断插入设备的类型,然后根据类型查找modules.***map表,找到合适的驱动程序,加载其入内核;当驱动程序加载到内核的时候,同样会想总线注册,也会激活uevent_ops的操作,这时系统会对设备和驱动进行匹配,若匹配正确,就会调用驱动程序里的probe函数。当设备取下时,系统会调用驱动程序的remove函数。这样就可以支持热插拔。

linux内核还提供了usb和pci等总线的抽象,它们是在bus,device和driver之上进行进一步的包装。内核已经把总线和设备的部分完成了,驱动作者只需要关注驱动程序的实现即可。例如,linux内核提供的usb core已经完成了usb的大部分工作,包括总线驱动,以及usb框架,还有设备的识别和初始化等,以及设备和驱动之间进行通信的urb方式,甚至还包含了大部分设备的驱动程序,但是有的设备驱动内核并没有。我们会在windows经常看到一种情况,某个设备被识别了,但是没有驱动程序而不能被使用,其中识别出设备就是内核所完成的,而这样的驱动往往就必须由设备提供者自己实现了。总之在内核抽象出一套驱动框架之上,驱动作者可以免去很多的工作。

posted @ 2013-02-03 00:13  xien7  阅读(405)  评论(0编辑  收藏  举报