• 慢慢学Linux驱动开发,第五篇,初探设备模型概念


    试着总结一下,学习一下,至少现在的我对于设备模型这个概念,几乎完全不懂。

         Linux设备模型中三个很重要的概念就是总线、设备和驱动,即bus、device、driver,而实际上内核中也定义了这么一些数据结构,分别为struct bus_type,struct device,struct device_driver,
    原型定义均在include/linux/device.h中。而struct bus_type结构中两个非常重要的成员就是kset和kobject这两个结构体。

    于是乎,Linux设备模型概念中重要的5个概念都引出来了,即设备模型
    在具体实现方面分两个层次:
    一是底层数据结构来实现基本对象及其层次关系:kobjects和ksets。
    二是基于这两个底层数据结构上实现的设备模型:总线,设备,驱动。

    一、底层数据结构:kobject,kset

    kobject 


    结合面向对象的思维。这个kobject属于最基础的结构,也就是最高抽象层(有点像java中的Cobject类)。任何一个设备模型如总线,设备,驱动都属于一个kobject 。在实现上这种派生关系就是在结构体中包含一个kobject的变量。

    这个在层次上处理最顶层的kobject结构提供了所有模型需要的最基本的功能:
    1 引用计数  用于内核维护其存在与消亡
    2 sysfs表示  每个sys/下的对象对应着一个kobject。
    3 热拔插事件处理  处理设备的热拔插事件。

    Kobjects 在内核中对应有一套申请,初始化,添加,注册,计数操作,释放等函数

    //2.6.10
    struct kobject {
    char * k_name; //名称
    char name[KOBJ_NAME_LEN];
    struct kref kref; //计数
    struct list_head entry;//用于连接到同类kobjects的链表
    struct kobjectn * parent;//用于实现层次,指向其父对象
    struct kset * kset; //用于实现层次,所属的集合
    struct kobj_typen * ktype;//指向对象的类型
    struct dentry * dentry; //指示在sysfs中的目录项
    };


    Kset和kobj_type

         Kset在概念上是一个集合或者叫容器。实现了对象的层次。所有属于一个ksets的对象(kobject)的parent都指向该ksets的kobj.同时这个对象都连接到kset 的list表上。同时位于ksets层次之上的是subsys,在最新的内核中已经取消subsys,因为它本质上也就是一个ksets。Kset有一套类似kobject的操作,实现上只是进一步调用其自身kobj的相应操作,毕竟ksets本质上也是一个kobject。

    //2.6.10
    struct kset {
    struct subsystem * subsys; //在新内核中已经没有subsys概念了,统一用kests
    struct kobj_type * ktype; //类型
    struct list_head list; //同一kset的链表
    struct kobject kobj; //自身的kobjects
    struct kset_hotplug_ops * hotplug_ops;
    };

    最后 属于同一个集合的对象可以拥有共同的属性:ktype 。

    struct kobj_type {
    void (*release)(struct kobject *);
    struct sysfs_ops * sysfs_ops;
    struct attribute ** default_attrs;
    };

    所谓的属性更具体一点说就是一些键值对。并且在sysfs_ops中的show函数被文件系统调用来显示sys/下面对应入口各属性的值。

    如此 ,kobjects与ksets实现层次树的底层骨架。
    进一步地,通过封装这些底层结构来实现上层的设备驱动模型。

    二、总线 设备 驱动

    关于bus,关于device,关于driver,他们是如何建立联系的呢?

    基本关系简要的概括如下:
    驱动核心可以注册多种类型的总线。
    每种总线下面可以挂载许多设备。(通过kset devices)
    每种总线下可以用很多设备驱动。(通过包含一个kset drivers)}
    每个驱动可以处理一组设备。
    这种基本关系的建立源于实际系统中各种总线,设备,驱动结构的抽象。

        具体到usb设备就是usb core的代码会进行整个usb系统的初始化,比如申请struct bus_type usb_bus_type,然后扫描usb总线,看线上连接了哪些usb设备,或者说root hub上连了哪些usb设备
    ,比如说连了一个usb键盘,那么就为它准备一个struct device,根据它的实际情况,为这个struct device赋值,并插入devices链表中来。又比如root hub上连了一个普通的hub,那么除了要为这个hub本身准备一个struct device以外,还得继续扫描这个hub上是否又连了别的设备,有的话继续重复之前的事情,这样一直进行下去,直到完成整个扫描,最终就把usb_bus_type中的devices链表给建立了起来。

         那么drivers链表呢?这个就不用bus方面主动了,而该由每一个driver本身去bus上面登记,或者说挂牌。具体到usb系统,每一个usb设备的驱动程序都会有一个struct usb_driver结构体,其代
    码如下,来自include/linux/usb.h

    //2.6.10
    struct usb_driver {
    struct module *owner;
    const char *name;
    int (*probe) (struct usb_interface *intf,
    const struct usb_device_id *id);
    void (*disconnect) (struct usb_interface *intf);
    int (*ioctl) (struct usb_interface *intf, unsigned int code, void *buf);
    int (*suspend) (struct usb_interface *intf, u32 state);
    int (*resume) (struct usb_interface *intf);
    const struct usb_device_id *id_table;
    struct device_driver driver;
    };

    bus和device之间是如何建立联系的?

         看似很长一段,实际上也就是注释为主.而此刻我们只需注意到其中的struct device_driver driver这个成员,usb core为每一个设备驱动准备了一个函数,让它把自己的这个
    struct device_driver driver插入到usb_bus_type中的drivers链表中去.而这个函数正是我们此前看到的usb_register.而与之对应的 usb_deregister所从事的正是与之相反的工作,把这个结构
    体从drivers链表中删除.可以说,usb core的确是用心良苦,为每一个usb设备驱动做足了功课,正因为如此,作为一个实际的usb设备驱动,它在初始化阶段所要做的事情就很少,很简单了, 直接调用
    usb_register即可.事实上,没有人是理所当然应该为你做什么的,但usb core这么做了.所以每一个写usb设备驱动的人应该铭记,usb device driver绝不是一个人在工作,在他身后,是usb core所提
    供的默默无闻又不可或缺的支持。

    device和driver之间是如何建立联系的呢?

         因为有了热插拔,device可以在计算机启动以后在插入或者拔出计算机了.因此,很难再说是先有device还是先有driver了.因为都有可能.device可以在任何时刻出现,而driver也可以在任何时刻被加载,所以,出现的情况就是,每当一个struct device诞生,它就会去bus的drivers链表中寻找自己的另一半,反之,每当一个一个struct device_driver诞生,它就去bus的devices链表中寻找它的那些设备.如果找到了合适的,那么ok,调用 device_bind_driver绑定好.如果找不到,没有关系,等待吧。

    还记得初始化的那几行代码吗?回到usb_register中来,看一下传给他的参数是什么?

    我们注意到,那句调用是这样子的,

    /* register the driver, return usb_register return code if error */
    retval = usb_register(&usb_storage_driver);

    是的,传递了一个叫做usb_storage_driver的家伙,这是什么?同一文件中,drivers/usb/storage/usb.c:

    struct usb_driver usb_storage_driver = {
    .owner = THIS_MODULE,
    .name = "usb-storage",
    .probe = storage_probe,
    .disconnect = storage_disconnect,
    .id_table = storage_usb_ids,
    };

         可以看到这里定义了一个struct usb_driver的结构体变量,usb_storage_driver,关于usb_driver我们上节已经说过了,当时主要说的是其中的成员driver,而眼下要讲的则是另外几个成员.首先,.owner
    和.name这两个没啥好多说的,owner这玩艺是用来给模块计数的,每个模块都这么用,赋值总是THIS_MODULE,而name就是这个模块的名字,usb core会处理它,所以如果这个模块正常被加载了的话,使用lsmod命令能看到一个叫做usb-storage的模块名。

    ------------------------------------------------------------------------------------------------------------------

    作者:庞辉

    出处:http://www.cnblogs.com/pang123hui/

    本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名庞辉(包含链接).


  • 相关阅读:
    Java 异步编程
    对@repository,@Service, @Compent,@Controller注解的理解
    分布式锁的解决方案
    JVM垃圾收集器
    java死锁
    CountDownLatch和CylicBarrier以及Semaphare你使用过吗
    必懂知识——HashMap的实现原理
    重写equals为啥需要重写hashCode
    mysql数据库的索引
    mysql常见的优化策略
  • 原文地址:https://www.cnblogs.com/pang123hui/p/2309906.html
Copyright © 2020-2023  润新知