个人对kobject的一点研究

    技术2025-03-28  13

    在LINUX中最让人不解的大概就是/sys下面的内容了 下面首先让我们来创建一个简单的platform设备,并从这个设备的视角进行深入,在此篇文章的深入过程中,我们只看kobeject的模型 我所使用的内核版本号为2.6.26,操作系统的内核版本号为2.6.27-7,暂未发现2.6.27-7与2.6.26的重大不同 首先写一个简单的模块 #include <linux/platform_device.h> #include <linux/init.h> #include <linux/module.h> static int __init test_probe(struct platform_device *pdev) {         int err = 0;         return err; } static int test_remove(struct platform_device *pdev) {         return 0; } static struct platform_device test_device = {         .name = "test_ts",         .id = -1, }; static struct platform_driver test_driver = {         .probe                = test_probe,         .remove                = test_remove,         .driver                = {                 .name        = "test_ts",                 .owner        = THIS_MODULE,         }, }; static int __devinit test_init(void) {         platform_device_register(&test_device);                return platform_driver_register(&test_driver); } static void __exit test_exit(void) {         platform_device_unregister(&test_device);         platform_driver_unregister(&test_driver); } module_init(test_init); module_exit(test_exit); MODULE_AUTHOR("zwolf"); MODULE_DESCRIPTION("Module test"); MODULE_LICENSE("GPL"); MODULE_ALIAS("test"); 接下来是makefile #Makefile obj-m:=test.o KDIR:=/lib/modules/2.6.27-7-generic/build PWD:=$(shell pwd) default:         $(MAKE) -C $(KDIR) M=$(PWD) modules KDIR中的目录请改为各位实际运行中的内核目录 make之后进行模块的加载 sudo insmod ./test.ko 现在到sys目录中查看我们的设备是否已经加载上了 首先是/sys/bus/platform/devices/ 在devices下,每一个连接文件都代表了一个设备 ls可看见test_ts,进入test_ts,ls可发现driver这个链接文件,ls-l查看,发现这个文件是连到/sys/bus/platform/drivers/test_ts的 这里需要说明的是连接的含义,并不是driver驱动存在于test_ts这个设备中,而是test_ts使用的驱动为/sys/bus/platform/drivers/test_ts 现在换到/sys/bus/platform/drivers这个目录下 ls查看会发现这里的文件都为目录,而非连接文件,说明这是驱动真正放置的位置 现在进入test_ts目录,然后ls,发现有一个test_ts的连接文件,ls –l查看可发现该文件连接到/sys/devices/platform/test_ts下 回到/sys/bus/platform/devices/下ls –l也会发现test_ts连接到/sys/devices/platform/test_ts 为什么test_ts这个设备放置于/sys/devices/platform下,而不是/sys/bus/platform/devices下呢 我认为和直观性有关,在sys下有这么几个目录block  bus  class  dev  devices  firmware  kernel  module  fs power devices很直观的说明了设备在这个目录下,便于大家查找 而/sys/bus/platform/devices下的连接是为了分类查找 画了张目录图,如下,绿色框的为连接文件,绿色线条为连接的对象       题外话:我自身对于这样的分类不是很喜欢,臃肿 重复  而且信息也不好规划,希望在以后的版本能对sys进行大的改造 现在来看另两个图,也就是构成sys的核心kobject,首先第一个是我去掉了连接部分的内容  也就是绿色线条的目录图   第二个是组成这个目录图的核心,kobject图,我也叫他层次图   不看大号绿色箭头右边的内容的话是不是发现两个架构相同? 对的,kobject的层次决定了目录的结构 kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype 在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices 首先是bus bus的注册在/drivers/base/bus.c里 int __init buses_ini   t(void) {         bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);         if (!bus_kset)                 return -ENOMEM;         return 0; } 先看bus_uevent_ops,这是一个uevent的操作集(我也还没清楚uevent的用途,所以uevent的内容先放着) 然后到kset_create_and_add struct kset *kset_create_and_add(const char *name,                                  struct kset_uevent_ops *uevent_ops,                                  struct kobject *parent_kobj) //传递进来的参数为("bus", &bus_uevent_ops, NULL) {         struct kset *kset;         int error;         //创建一个kset容器         kset = kset_create(name, uevent_ops, parent_kobj);         if (!kset)                 return NULL;         //注册创建的kset容器         error = kset_register(kset);         if (error) {                 kfree(kset);                 return NULL;         }         return kset; } 首先需要创建一个kset容器 static struct kset *kset_create(const char *name,                                 struct kset_uevent_ops *uevent_ops,                                 struct kobject *parent_kobj) //传递进来的参数为("bus", &bus_uevent_ops, NULL) {         struct kset *kset;         //为kset分配内存         kset = kzalloc(sizeof(*kset), GFP_KERNEL);         if (!kset)                 return NULL;         //设置kset中kobject的名字,这里为bus         kobject_set_name(&kset->kobj, name);         //设置uevent操作集,这里为bus_uevent_ops         kset->uevent_ops = uevent_ops;         //设置父对象,这里为NULL         kset->kobj.parent = parent_kobj;         //设置容器操作集         kset->kobj.ktype = &kset_ktype;         //设置父容器         kset->kobj.kset = NULL;         return kset; } 这里的ktype,也就是kset_ktype是一个操作集,用于为sys下文件的实时反馈做服务,例如我们cat name的时候就要通过ktype提供的show函数,具体什么怎么运用,将在后面讲解 现在回到kset_create_and_add中的kset_register,将建立好的kset添加进sys里 int kset_register(struct kset *k) {         int err;         if (!k)                 return -EINVAL;         //初始化         kset_init(k);         //添加该容器         err = kobject_add_internal(&k->kobj);         if (err)                 return err;         kobject_uevent(&k->kobj, KOBJ_ADD);         return 0; } kset_init进行一些固定的初始化操作,里面没有我们需要关心的内容 kobject_add_internal为重要的一个函数,他对kset里kobj的从属关系进行解析,搭建正确的架构 static int kobject_add_internal(struct kobject *kobj) {         int error = 0;         struct kobject *parent;         //检测kobj是否为空         if (!kobj)                 return -ENOENT;         //检测kobj名字是否为空         if (!kobj->name || !kobj->name[0]) {                 pr_debug("kobject: (%p): attempted to be registered with empty "                          "name!/n", kobj);                 WARN_ON(1);                 return -EINVAL;         }         //提取父对象         parent = kobject_get(kobj->parent);         /* join kset if set, use it as parent if we do not already have one */         //父容器存在则设置父对象         if (kobj->kset) {//在bus的kset中为空,所以不会进入到下面的代码                 //检测是否已经设置父对象                 if (!parent)                         //无则使用父容器为父对象                         parent = kobject_get(&kobj->kset->kobj);                 //添加该kobj到父容器的链表中                 kobj_kset_join(kobj);                 //设置父对象                 kobj->parent = parent;         }         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",                  kobject_name(kobj), kobj, __func__,                  parent ? kobject_name(parent) : "<NULL>",                  kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");         //建立相应的目录         error = create_dir(kobj);         if (error) {                 kobj_kset_leave(kobj);                 kobject_put(parent);                 kobj->parent = NULL;                 if (error == -EEXIST)                         printk(KERN_ERR "%s failed for %s with "                                "-EEXIST, don't try to register things with "                                "the same name in the same directory./n",                                __func__, kobject_name(kobj));                 else                         printk(KERN_ERR "%s failed for %s (%d)/n",                                __func__, kobject_name(kobj), error);                 dump_stack();         } else                 kobj->state_in_sysfs = 1;         return error; } 至此bus的目录就建立起来了 模型如下     接下来是devices,在/drivers/base/core.c里 int __init devices_init(void) {         devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);         if (!devices_kset)                 return -ENOMEM;         return 0; } 过程和bus的注册一致,我就不复述了~ 模型如下     然后是platform的注册 在platform的注册中,分为两个部分,一部分是注册到devices中,另一部分是注册到bus中,代码在/drivers/base/platform.c中 int __init platform_bus_init(void) {         int error;                 //注册到devices目录中         error = device_register(&platform_bus);         if (error)                 return error;         //注册到bus目录中         error =  bus_register(&platform_bus_type);         if (error)                 device_unregister(&platform_bus);         return error; } 首先是device_register,注册的参数为platform_bus,如下所示 struct device platform_bus = {         .bus_id                = "platform", }; 很简单,只有一个参数,表明了目录名 int device_register(struct device *dev) {         //初始化dev结构         device_initialize(dev);         //添加dev至目录         return device_add(dev); } void device_initialize(struct device *dev) {         //重要的一步,指明了父容器为devices_kset,而devices_kset的注册在前面已经介绍过了         dev->kobj.kset = devices_kset;         //初始化kobj的ktype为device_ktype         kobject_init(&dev->kobj, &device_ktype);         klist_init(&dev->klist_children, klist_children_get,                    klist_children_put);         INIT_LIST_HEAD(&dev->dma_pools);         INIT_LIST_HEAD(&dev->node);         init_MUTEX(&dev->sem);         spin_lock_init(&dev->devres_lock);         INIT_LIST_HEAD(&dev->devres_head);         device_init_wakeup(dev, 0);         set_dev_node(dev, -1); } int device_add(struct device *dev) {         struct device *parent = NULL;         struct class_interface *class_intf;         int error;         dev = get_device(dev);         if (!dev || !strlen(dev->bus_id)) {                 error = -EINVAL;                 goto Done;         }         pr_debug("device: '%s': %s/n", dev->bus_id, __func__);         parent = get_device(dev->parent);         setup_parent(dev, parent);         if (parent)                 set_dev_node(dev, dev_to_node(parent));         //设置dev->kobj的名字和父对象,并建立相应的目录         error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);         if (error)                 goto Error;         if (platform_notify)                 platform_notify(dev);         if (dev->bus)                 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,                                              BUS_NOTIFY_ADD_DEVICE, dev);         //建立uevent文件         error = device_create_file(dev, &uevent_attr);         if (error)                 goto attrError;         if (MAJOR(dev->devt)) {                 error = device_create_file(dev, &devt_attr);                 if (error)                         goto ueventattrError;         }         //建立subsystem连接文件连接到所属class,这里没有设置class对象所以不会建立         error = device_add_class_symlinks(dev);         if (error)                 goto SymlinkError;         //建立dev的描述文件,这里没有设置描述文件所以不会建立         error = device_add_attrs(dev);         if (error)                 goto AttrsError;         //建立链接文件至所属bus,这里没有设置所属bus所以不会建立         error = bus_add_device(dev);         if (error)                 goto BusError;         //添加power文件,因为platform不属于设备,所以不会建立power文件         error = device_pm_add(dev);         if (error)                 goto PMError;         kobject_uevent(&dev->kobj, KOBJ_ADD);         //检测驱动中有无适合的设备进行匹配,但没有设置bus,所以不会进行匹配         bus_attach_device(dev);         if (parent)                 klist_add_tail(&dev->knode_parent, &parent->klist_children);         if (dev->class) {                 down(&dev->class->sem);                 list_add_tail(&dev->node, &dev->class->devices);                 list_for_each_entry(class_intf, &dev->class->interfaces, node)                         if (class_intf->add_dev)                                 class_intf->add_dev(dev, class_intf);                 up(&dev->class->sem);         } Done:         put_device(dev);         return error; PMError:         bus_remove_device(dev); BusError:         if (dev->bus)                 blocking_notifier_call_chain(&dev->bus->p->bus_notifier,                                              BUS_NOTIFY_DEL_DEVICE, dev);         device_remove_attrs(dev); AttrsError:         device_remove_class_symlinks(dev); SymlinkError:         if (MAJOR(dev->devt))                 device_remove_file(dev, &devt_attr); ueventattrError:         device_remove_file(dev, &uevent_attr); attrError:         kobject_uevent(&dev->kobj, KOBJ_REMOVE);         kobject_del(&dev->kobj); Error:         cleanup_device_parent(dev);         if (parent)                 put_device(parent);         goto Done; } 在kobject_add-> kobject_add_varg-> kobject_add_internal中 //提取父对象,因为没有设置,所以为空 parent = kobject_get(kobj->parent); //父容器存在则设置父对象,在前面的dev->kobj.kset = devices_kset中设为了devices_kset if (kobj->kset) { //检测是否已经设置父对象         if (!parent)                 //无则使用父容器为父对象                 parent = kobject_get(&kobj->kset->kobj); //添加该kobj到父容器的链表中         kobj_kset_join(kobj);         //设置父对象         kobj->parent = parent; } 现在devices下的platform目录建立好了,模型如下,其中红线描绘了目录关系   现在到bus_register了注册的参数platform_bus_type如下所示struct bus_type platform_bus_type = {        .name                = "platform",        .dev_attrs        = platform_dev_attrs,        .match                = platform_match,        .uevent                = platform_uevent,        .suspend                = platform_suspend,        .suspend_late        = platform_suspend_late,        .resume_early        = platform_resume_early,        .resume                = platform_resume,};int bus_register(struct bus_type *bus){        int retval;        //声明一个总线私有数据并分配空间        struct bus_type_private *priv;        priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL);        if (!priv)                return -ENOMEM;        //互相关联        priv->bus = bus;        bus->p = priv;        BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);        //设置私有数据中kobj对象的名字        retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);        if (retval)                goto out;        //设置父容器为bus_kset,操作集为bus_ktype        priv->subsys.kobj.kset = bus_kset;        priv->subsys.kobj.ktype = &bus_ktype;        priv->drivers_autoprobe = 1;        //注册bus容器        retval = kset_register(&priv->subsys);        if (retval)                goto out;        //建立uevent属性文件        retval = bus_create_file(bus, &bus_attr_uevent);        if (retval)                goto bus_uevent_fail;        //建立devices目录        priv->devices_kset = kset_create_and_add("devices", NULL,                                                 &priv->subsys.kobj);        if (!priv->devices_kset) {                retval = -ENOMEM;                goto bus_devices_fail;        }        //建立drivers目录        priv->drivers_kset = kset_create_and_add("drivers", NULL,                                                 &priv->subsys.kobj);        if (!priv->drivers_kset) {                retval = -ENOMEM;                goto bus_drivers_fail;        }        //初始化klist_devices和klist_drivers链表        klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);        klist_init(&priv->klist_drivers, NULL, NULL);        //增加probe属性文件        retval = add_probe_files(bus);        if (retval)                goto bus_probe_files_fail;        //增加总线的属性文件        retval = bus_add_attrs(bus);        if (retval)                goto bus_attrs_fail;        pr_debug("bus: '%s': registered/n", bus->name);        return 0;bus_attrs_fail:        remove_probe_files(bus);bus_probe_files_fail:        kset_unregister(bus->p->drivers_kset);bus_drivers_fail:        kset_unregister(bus->p->devices_kset);bus_devices_fail:        bus_remove_file(bus, &bus_attr_uevent);bus_uevent_fail:        kset_unregister(&bus->p->subsys);        kfree(bus->p);out:        return retval;}在kset_register-> kobject_add_internal中//提取父对象,因为没有设置父对象,所以为空parent = kobject_get(kobj->parent);//父容器存在则设置父对象,在上文中设置了父容器priv->subsys.kobj.kset = bus_ksetif (kobj->kset) {        //检测是否已经设置父对象        if (!parent)                //无则使用父容器为父对象                parent = kobject_get(&kobj->kset->kobj);        //添加该kobj到父容器的链表中        kobj_kset_join(kobj);        //设置父对象        kobj->parent = parent;}在retval = kset_register(&priv->subsys)完成之后platform在bus下的模型如下图       有印象的话大家还记得在platform下面有两个目录devices和drivers吧~现在就到这两个目录的注册了priv->devices_kset = kset_create_and_add("devices", NULL,&priv->subsys.kobj);priv->drivers_kset = kset_create_and_add("drivers", NULL, &priv->subsys.kobj);注意这两条语句的头部priv->devices_kset = kset_create_and_addpriv->drivers_kset = kset_create_and_add可以清楚的看到bus_type_private下的devices_kset, drivers_kset分别连接到了devices,drivers的kset上现在来看kset_create_and_add("devices", NULL,&priv->subsys.kobj);struct kset *kset_create_and_add(const char *name,                                 struct kset_uevent_ops *uevent_ops,                                 struct kobject *parent_kobj)//参数为"devices", NULL,&priv->subsys.kobj{        struct kset *kset;        int error;        //创建一个kset容器        kset = kset_create(name, uevent_ops, parent_kobj);        if (!kset)                return NULL;        //注册创建的kset容器        error = kset_register(kset);        if (error) {                kfree(kset);                return NULL;        }        return kset;}在kset_create 中比较重要的操作为kset->kobj.ktype = &kset_ktype //设置了ktype,为kset_ktypekset->kobj.parent = parent_kobj; //设置了父对象,为priv->subsys.kobj,也就是platform_bus_type->p->subsys.kobjkset->kobj.kset = NULL;    //设置父容器为空在kset_register中//提取父对象parent = kobject_get(kobj->parent); //在之前设置为了//父容器存在则设置父对象,由于父容器为空,不执行以下代码if (kobj->kset) {        //检测是否已经设置父对象        if (!parent)                //无则使用父容器为父对象                parent = kobject_get(&kobj->kset->kobj);        //添加该kobj到父容器的链表中        kobj_kset_join(kobj);        //设置父对象        kobj->parent = parent;}至此, devices的模型就建立好了,drivers模型的建立和devices是一致的,只是名字不同而已,我就不复述了,建立好的模型如下         好了~  到了这里,bus,devices和platform的基础模型就就建立好了,就等设备来注册了在platform模型设备的建立中,需要2个部分的注册,驱动的注册和设备的注册platform_device_register(&test_device);        platform_driver_register(&test_driver);首先看platform_device_register注册参数为test_device,结构如下static struct platform_device test_device = {        .name = "test_ts",        .id = -1,        //. resource        //.dev};这个结构主要描述了设备的名字,ID和资源和私有数据,其中资源和私有数据我们在这里不使用,将在别的文章中进行讲解int platform_device_register(struct platform_device *pdev){        //设备属性的初始化        device_initialize(&pdev->dev);        //将设备添加进platform里        return platform_device_add(pdev);}void device_initialize(struct device *dev){        dev->kobj.kset = devices_kset;                   //设置kset为devices_kset,则将设备挂接上了devices目录        kobject_init(&dev->kobj, &device_ktype);                    //初始化kobeject,置ktype为device_ktype        klist_init(&dev->klist_children, klist_children_get,                   klist_children_put);        INIT_LIST_HEAD(&dev->dma_pools);        INIT_LIST_HEAD(&dev->node);        init_MUTEX(&dev->sem);        spin_lock_init(&dev->devres_lock);        INIT_LIST_HEAD(&dev->devres_head);        device_init_wakeup(dev, 0);        set_dev_node(dev, -1);}int platform_device_add(struct platform_device *pdev){        int i, ret = 0;        if (!pdev)                return -EINVAL;        //检测是否设置了dev中的parent,无则赋为platform_bus        if (!pdev->dev.parent)                pdev->dev.parent = &platform_bus;        //设置dev中的bus为platform_bus_type        pdev->dev.bus = &platform_bus_type;        //检测id,id为-1表明该设备只有一个,用设备名为bus_id        //不为1则表明该设备有数个,需要用序号标明bus_id        if (pdev->id != -1)                snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,                         pdev->id);        else                strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);        //增加资源到资源树中        for (i = 0; i < pdev->num_resources; i++) {                struct resource *p, *r = &pdev->resource ;                if (r->name == NULL)                        r->name = pdev->dev.bus_id;                p = r->parent;                if (!p) {                        if (r->flags & IORESOURCE_MEM)                                p = &iomem_resource;                        else if (r->flags & IORESOURCE_IO)                                p = &ioport_resource;                }                if (p && insert_resource(p, r)) {                        printk(KERN_ERR "%s: failed to claim resource %d/n",pdev->dev.bus_id, i);                        ret = -EBUSY;                        goto failed;                }        }        pr_debug("Registering platform device '%s'. Parent at %s/n",pdev->dev.bus_id, pdev->dev.parent->bus_id);        //添加设备到设备层次中        ret = device_add(&pdev->dev);        if (ret == 0)                return ret;failed:        while (--i >= 0)                if (pdev->resource .flags & (IORESOURCE_MEM|IORESOURCE_IO))                        release_resource(&pdev->resource );        return ret;}int device_add(struct device *dev){        struct device *parent = NULL;        struct class_interface *class_intf;        int error;        dev = get_device(dev);        if (!dev || !strlen(dev->bus_id)) {                error = -EINVAL;                goto Done;        }        pr_debug("device: '%s': %s/n", dev->bus_id, __func__);        //取得上层device,而dev->parent的赋值是在platform_device_add中的pdev->dev.parent = &platform_bus完成的        parent = get_device(dev->parent);        //以上层devices为准重设dev->kobj.parent        setup_parent(dev, parent);        if (parent)                set_dev_node(dev, dev_to_node(parent));        //设置dev->kobj的名字和父对象,并建立相应目录        error = kobject_add(&dev->kobj, dev->kobj.parent, "%s", dev->bus_id);        if (error)                goto Error;        if (platform_notify)                platform_notify(dev);        //一种新型的通知机制,但是platform中没有设置相应的结构,所以在这里跳过        /* notify clients of device entry (new way) */        if (dev->bus)                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_ADD_DEVICE, dev);        //建立uevent文件        error = device_create_file(dev, &uevent_attr);        if (error)                goto attrError;        //设备有设备号则建立dev文件        if (MAJOR(dev->devt)) {                error = device_create_file(dev, &devt_attr);                if (error)                        goto ueventattrError;        }        //建立subsystem连接文件连接到所属class        error = device_add_class_symlinks(dev);        if (error)                goto SymlinkError;        //添加dev的描述文件        error = device_add_attrs(dev);        if (error)                goto AttrsError;        //添加链接文件至所属bus        error = bus_add_device(dev);        if (error)                goto BusError;        //添加power文件        error = device_pm_add(dev);        if (error)                goto PMError;        kobject_uevent(&dev->kobj, KOBJ_ADD);        //检测驱动中有无适合的设备进行匹配,现在只添加了设备,还没有加载驱动,所以不会进行匹配        bus_attach_device(dev);        if (parent)                klist_add_tail(&dev->knode_parent, &parent->klist_children);        if (dev->class) {                down(&dev->class->sem);                list_add_tail(&dev->node, &dev->class->devices);                list_for_each_entry(class_intf, &dev->class->interfaces, node)                        if (class_intf->add_dev)                                class_intf->add_dev(dev, class_intf);                up(&dev->class->sem);        }Done:        put_device(dev);        return error;PMError:        bus_remove_device(dev);BusError:        if (dev->bus)                blocking_notifier_call_chain(&dev->bus->p->bus_notifier,BUS_NOTIFY_DEL_DEVICE, dev);        device_remove_attrs(dev);AttrsError:        device_remove_class_symlinks(dev);SymlinkError:        if (MAJOR(dev->devt))                device_remove_file(dev, &devt_attr);ueventattrError:        device_remove_file(dev, &uevent_attr);attrError:        kobject_uevent(&dev->kobj, KOBJ_REMOVE);        kobject_del(&dev->kobj);Error:        cleanup_device_parent(dev);        if (parent)                put_device(parent);        goto Done;}static void setup_parent(struct device *dev, struct device *parent){        struct kobject *kobj;        //取得上层device的kobj        kobj = get_device_parent(dev, parent);        //kobj不为空则重设dev->kobj.parent        if (kobj)                dev->kobj.parent = kobj;}static struct kobject *get_device_parent(struct device *dev,                                         struct device *parent){        int retval;        //因为dev->class为空,所以跳过这段代码        if (dev->class) {                struct kobject *kobj = NULL;                struct kobject *parent_kobj;                struct kobject *k;                if (parent == NULL)                        parent_kobj = virtual_device_parent(dev);                else if (parent->class)                        return &parent->kobj;                else                        parent_kobj = &parent->kobj;                spin_lock(&dev->class->class_dirs.list_lock);                list_for_each_entry(k, &dev->class->class_dirs.list, entry)                        if (k->parent == parent_kobj) {                                kobj = kobject_get(k);                                break;                        }                spin_unlock(&dev->class->class_dirs.list_lock);                if (kobj)                        return kobj;                k = kobject_create();                if (!k)                        return NULL;                k->kset = &dev->class->class_dirs;                retval = kobject_add(k, parent_kobj, "%s", dev->class->name);                if (retval < 0) {                        kobject_put(k);                        return NULL;                }                return k;        }        if (parent)                //返回上层device的kobj                return &parent->kobj;        return NULL;}在bus_attach_device中虽然没有成功进行匹配,但是有很重要的一步为之后正确的匹配打下基础void bus_attach_device(struct device *dev){        struct bus_type *bus = dev->bus;        int ret = 0;        if (bus) {                if (bus->p->drivers_autoprobe)                        ret = device_attach(dev);                WARN_ON(ret < 0);                if (ret >= 0)                        klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);        }}klist_add_tail(&dev->knode_bus, &bus->p->klist_devices)就是这一行在这一行代码中将设备挂载到了bus下的devices链表下,这样,当驱动请求匹配的时候,platform总线就会历遍devices链表为驱动寻找合适的设备现在来看一下test_device的模型       然后platform_driver_unregister,他的参数 test_driver的结构如下static struct platform_driver test_driver = {        .probe                = test_probe,        .remove                = test_remove,        .driver                = {                .name        = "test_ts",                .owner        = THIS_MODULE,        },};int platform_driver_register(struct platform_driver *drv){        drv->driver.bus = &platform_bus_type;        if (drv->probe)                drv->driver.probe = platform_drv_probe;        if (drv->remove)                drv->driver.remove = platform_drv_remove;        if (drv->shutdown)                drv->driver.shutdown = platform_drv_shutdown;        if (drv->suspend)                drv->driver.suspend = platform_drv_suspend;        if (drv->resume)                drv->driver.resume = platform_drv_resume;        return driver_register(&drv->driver);}从上面代码可以看出,在platform_driver中设置了probe, remove, shutdown, suspend或resume函数的话则drv->driver也会设置成platform对应的函数int driver_register(struct device_driver *drv){        int ret;        struct device_driver *other;                //检测总线的操作函数和驱动的操作函数是否同时存在,同时存在则提示使用总线提供的操作函数        if ((drv->bus->probe && drv->probe) ||            (drv->bus->remove && drv->remove) ||            (drv->bus->shutdown && drv->shutdown))                printk(KERN_WARNING "Driver '%s' needs updating - please use ""bus_type methods/n", drv->name);        //检测是否已经注册过        other = driver_find(drv->name, drv->bus);        if (other) {                put_driver(other);                printk(KERN_ERR "Error: Driver '%s' is already registered, “"aborting.../n", drv->name);                return -EEXIST;        }        //添加驱动到总线上        ret = bus_add_driver(drv);        if (ret)                return ret;                ret = driver_add_groups(drv, drv->groups);        if (ret)                bus_remove_driver(drv);        return ret;}int bus_add_driver(struct device_driver *drv){        struct bus_type *bus;        struct driver_private *priv;        int error = 0;        //取bus结构        bus = bus_get(drv->bus);        if (!bus)                return -EINVAL;        pr_debug("bus: '%s': add driver %s/n", bus->name, drv->name);        //分配驱动私有数据        priv = kzalloc(sizeof(*priv), GFP_KERNEL);        if (!priv) {                error = -ENOMEM;                goto out_put_bus;        }        //初始化klist_devices链表        klist_init(&priv->klist_devices, NULL, NULL);        //互相关联        priv->driver = drv;        drv->p = priv;        //设置私有数据的父容器,在这一步中,设置了kset为platform下的drivers_kset结构,也就是drivers呢个目录        priv->kobj.kset = bus->p->drivers_kset;        //初始化kobj对象,设置容器操作集并建立相应的目录,这里由于没有提供parent,所以会使用父容器中的kobj为父对象        error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,                                     "%s", drv->name);        if (error)                goto out_unregister;        //检测所属总线的drivers_autoprobe属性是否为真        //为真则进行与设备的匹配,到这里,就会与我们之前注册的test_device连接上了,至于如何连接,进行了什么操作,将在别的文章中详细描述        if (drv->bus->p->drivers_autoprobe) {                error = driver_attach(drv);                if (error)                        goto out_unregister;        }        //挂载到所属总线驱动链表上        klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);        module_add_driver(drv->owner, drv);        //建立uevent属性文件        error = driver_create_file(drv, &driver_attr_uevent);        if (error) {                printk(KERN_ERR "%s: uevent attr (%s) failed/n",                        __func__, drv->name);        }        //建立设备属性文件        error = driver_add_attrs(bus, drv);        if (error) {                printk(KERN_ERR "%s: driver_add_attrs(%s) failed/n",__func__, drv->name);        }        error = add_bind_files(drv);        if (error) {                printk(KERN_ERR "%s: add_bind_files(%s) failed/n",__func__, drv->name);        }        kobject_uevent(&priv->kobj, KOBJ_ADD);        return error;out_unregister:        kobject_put(&priv->kobj);out_put_bus:        bus_put(bus);        return error;}到这里test_driver的模型就建立好了,图就是最上面的层次图,我就不再贴了到这里一个基本的框架就建立起来了~   下面,我开始对kobject kset和ktype做分析先说说关系,ktype与kobject和kset这两者之前的关系较少,让我画一个图,是这样的

     

      ktype依赖于kobject,kset也依赖于kobject,而kobject有时需要kset(所以用了一个白箭头),不一定需要ktype(真可怜,连白箭头都没有) 首先先说一下这个可有可无的ktype 到/sys/bus/platform下面可以看见一个drivers_autoprobe的文件 cat drivers_autoprobe可以查看这个文件的值 echo 0 > drivers_autoprobe则可以改变这个文件的值 drivers_autoprobe这个文件表示的是是否自动进行初始化 在 void bus_attach_device(struct device *dev) {         struct bus_type *bus = dev->bus;         int ret = 0;         if (bus) {                 if (bus->p->drivers_autoprobe)                         ret = device_attach(dev);                 WARN_ON(ret < 0);                 if (ret >= 0)                         klist_add_tail(&dev->knode_bus, &bus->p->klist_devices);         } } 中可以看见这么一段代码 if (bus->p->drivers_autoprobe)         ret = device_attach(dev); bus->p->drivers_autoprobe的值为真则进行匹配 而drivers_autoprobe这个文件则可以动态的修改这个值选择是否进行匹配 使用外部文件修改内核参数,ktype就是提供了这么一种方法 现在让我们看看ktype是怎么通过kobject进行运作的 首先是ktype及通过ktype进行运作的drivers_autoprobe的注册 ktype的挂载十分简单,因为他是和kobject是一体的 只有这么下面一句        priv->subsys.kobj.ktype = &bus_ktype; 这样就将bus_ktype挂载到了platform_bus_type的kobject上 drivers_autoprobe的注册如下 retval = bus_create_file(bus, &bus_attr_drivers_autoprobe); bus_attr_drivers_autoprobe这个结构由一系列的宏进行组装 static BUS_ATTR(drivers_autoprobe, S_IWUSR | S_IRUGO,                 show_drivers_autoprobe, store_drivers_autoprobe); #define BUS_ATTR(_name, _mode, _show, _store)        / struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store) #define __ATTR(_name,_mode,_show,_store) { /         .attr = {.name = __stringify(_name), .mode = _mode },        /         .show        = _show,                                        /         .store        = _store,                                        / } 最后bus_attr_drivers_autoprobe的模型如下 struct bus_attribute  bus_attr_drivers_autoprobe {         .attr = { .name = “drivers_autoprobe”, .mode = S_IWUSR | S_IRUGO },                .show        = show_drivers_autoprobe,                                                .store        = store_drivers_autoprobe,                                        } 进入到bus_create_file中 int bus_create_file(struct bus_type *bus, struct bus_attribute *attr) //参数为(bus, &bus_attr_drivers_autoprobe) {         int error;         if (bus_get(bus)) {                 error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);                 bus_put(bus);         } else                 error = -EINVAL;         return error; } int sysfs_create_file(struct kobject * kobj, const struct attribute * attr) //参数为(&bus->p->subsys.kobj, &attr->attr) {         BUG_ON(!kobj || !kobj->sd || !attr);         return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR); } int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,int type) //参数为(&bus->p->subsys.kobj ->sd, &attr->attr, SYSFS_KOBJ_ATTR) {         return sysfs_add_file_mode(dir_sd, attr, type, attr->mode); } int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,                         const struct attribute *attr, int type, mode_t amode) //整理一下参数,现在应该为 //(&platform_bus_type->p->subsys.kobj ->sd, &bus_attr_drivers_autoprobe->attr, SYSFS_KOBJ_ATTR, &bus_attr_drivers_autoprobe->attr->mode) {         umode_t mode = (amode & S_IALLUGO) | S_IFREG;         struct sysfs_addrm_cxt acxt;         struct sysfs_dirent *sd;         int rc;         //在这一步中可以看出新建了一个节点         sd = sysfs_new_dirent(attr->name, mode, type);         if (!sd)                 return -ENOMEM;                 //这一步挂载了&bus_attr_drivers_autoprobe->attr到节点中,为以后提取attr及上层结构做准备         sd->s_attr.attr = (void *)attr;         // dir_sd也就是上层目录,在这里为platform_bus_type->p->subsys.kobj ->sd         //也就是/sys/bus/platform这个目录         sysfs_addrm_start(&acxt, dir_sd);         rc = sysfs_add_one(&acxt, sd);         sysfs_addrm_finish(&acxt);         if (rc)                 sysfs_put(sd);         return rc; } struct sysfs_dirent *sysfs_new_dirent(const char *name, umode_t mode, int type) {         char *dup_name = NULL;         struct sysfs_dirent *sd;         if (type & SYSFS_COPY_NAME) {                 name = dup_name = kstrdup(name, GFP_KERNEL);                 if (!name)                         return NULL;         }         sd = kmem_cache_zalloc(sysfs_dir_cachep, GFP_KERNEL);         if (!sd)                 goto err_out1;         if (sysfs_alloc_ino(&sd->s_ino))                 goto err_out2;         atomic_set(&sd->s_count, 1);         atomic_set(&sd->s_active, 0);         sd->s_name = name;   //节点的名字为&bus_attr_drivers_autoprobe->attr->name  也就是drivers_autoprobe         sd->s_mode = mode; sd->s_flags = type;   //节点的type为SYSFS_KOBJ_ATTR         return sd; err_out2:         kmem_cache_free(sysfs_dir_cachep, sd); err_out1:         kfree(dup_name);         return NULL; } 现在一切准备就绪,来看看怎么读取吧 首先是open,大概流程可以看我的另一篇文章<从文件到设备>,一直看到ext3_lookup 这里和ext3_lookup不同的是,sys的文件系统是sysfs文件系统,所以应该使用的lookup函数为sysfs_lookup(/fs/sysfs/dir.c) static struct dentry * sysfs_lookup(struct inode *dir, struct dentry *dentry,                                 struct nameidata *nd) {         struct dentry *ret = NULL;         struct sysfs_dirent *parent_sd = dentry->d_parent->d_fsdata;         struct sysfs_dirent *sd;         struct inode *inode;         mutex_lock(&sysfs_mutex);         sd = sysfs_find_dirent(parent_sd, dentry->d_name.name);         if (!sd) {                 ret = ERR_PTR(-ENOENT);                 goto out_unlock;         }         //节点的初始化在这里         inode = sysfs_get_inode(sd);         if (!inode) {                 ret = ERR_PTR(-ENOMEM);                 goto out_unlock;         }         dentry->d_op = &sysfs_dentry_ops;         dentry->d_fsdata = sysfs_get(sd);         d_instantiate(dentry, inode);         d_rehash(dentry); out_unlock:         mutex_unlock(&sysfs_mutex);         return ret; } struct inode * sysfs_get_inode(struct sysfs_dirent *sd) {         struct inode *inode;         inode = iget_locked(sysfs_sb, sd->s_ino);         if (inode && (inode->i_state & I_NEW))                 //为节点赋值                 sysfs_init_inode(sd, inode);         return inode; } static void sysfs_init_inode(struct sysfs_dirent *sd, struct inode *inode) {         struct bin_attribute *bin_attr;         inode->i_blocks = 0;         inode->i_mapping->a_ops = &sysfs_aops;         inode->i_mapping->backing_dev_info = &sysfs_backing_dev_info;         inode->i_op = &sysfs_inode_operations;         inode->i_ino = sd->s_ino;         lockdep_set_class(&inode->i_mutex, &sysfs_inode_imutex_key);         if (sd->s_iattr) {                 set_inode_attr(inode, sd->s_iattr);         } else                 set_default_inode_attr(inode, sd->s_mode);         //判断类型         switch (sysfs_type(sd)) {         case SYSFS_DIR:                 inode->i_op = &sysfs_dir_inode_operations;                 inode->i_fop = &sysfs_dir_operations;                 inode->i_nlink = sysfs_count_nlink(sd);                 break;         //还记得在注册的时候有一个参数为SYSFS_KOBJ_ATTR赋到了sd->s_flags上面吧         case SYSFS_KOBJ_ATTR:                 inode->i_size = PAGE_SIZE;                 inode->i_fop = &sysfs_file_operations;                 break;         case SYSFS_KOBJ_BIN_ATTR:                 bin_attr = sd->s_bin_attr.bin_attr;                 inode->i_size = bin_attr->size;                 inode->i_fop = &bin_fops;                 break;         case SYSFS_KOBJ_LINK:                 inode->i_op = &sysfs_symlink_inode_operations;                 break;         default:                 BUG();         }         unlock_new_inode(inode); } sysfs_file_operations的结构如下,之后open和read,write都明了了 const struct file_operations sysfs_file_operations = {         .read                = sysfs_read_file,         .write                = sysfs_write_file,         .llseek                = generic_file_llseek,         .open                = sysfs_open_file,         .release        = sysfs_release,         .poll                = sysfs_poll, }; 有关在哪调用open还是请查阅我的另一篇文章<从文件到设备>中 nameidata_to_filp之后的操作 好的~  现在进入到了sysfs_open_file中 static int sysfs_open_file(struct inode *inode, struct file *file) {         struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata;         //要重的取值,在这里取得了drivers_autoprobe的目录platform的kproject         struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;         struct sysfs_buffer *buffer;         struct sysfs_ops *ops;         int error = -EACCES;         if (!sysfs_get_active_two(attr_sd))                 return -ENODEV;         if (kobj->ktype && kobj->ktype->sysfs_ops)                 //这里可谓是ktype实现中的核心,在这里ops设置成了platform_bus_type中kobject->ktype的sysfs_ops                 ops = kobj->ktype->sysfs_ops;         else {                 printk(KERN_ERR "missing sysfs attribute operations for ""kobject: %s/n", kobject_name(kobj));                 WARN_ON(1);                 goto err_out;         }         if (file->f_mode & FMODE_WRITE) {                 if (!(inode->i_mode & S_IWUGO) || !ops->store)                         goto err_out;         }         if (file->f_mode & FMODE_READ) {                 if (!(inode->i_mode & S_IRUGO) || !ops->show)                         goto err_out;         }         error = -ENOMEM;         buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL);         if (!buffer)                 goto err_out;         mutex_init(&buffer->mutex);         buffer->needs_read_fill = 1;         //然后将设置好的ops挂载到buffer上         buffer->ops = ops;         //再将buffer挂载到file->private_data中         file->private_data = buffer;         error = sysfs_get_open_dirent(attr_sd, buffer);         if (error)                 goto err_free;         sysfs_put_active_two(attr_sd);         return 0; err_free:         kfree(buffer); err_out:         sysfs_put_active_two(attr_sd);         return error; } 现在已经为read和write操作准备好了 马上进入到read操作中   整个流程如上图所示,如何进入到sysfs_read_file在上面open的操作中已经说明了 我们就从sysfs_read_file开始分析(该文件在/fs/sysfs/file.c中) sysfs_read_file(struct file *file, char __user *buf, size_t count, loff_t *ppos) {         struct sysfs_buffer * buffer = file->private_data;         ssize_t retval = 0;         mutex_lock(&buffer->mutex);         if (buffer->needs_read_fill || *ppos == 0) {                 //主要操作在fill_read_buffer中                 retval = fill_read_buffer(file->f_path.dentry,buffer);                 if (retval)                         goto out;         }         pr_debug("%s: count = %zd, ppos = %lld, buf = %s/n",__func__, count, *ppos, buffer->page);         retval = simple_read_from_buffer(buf, count, ppos, buffer->page,                                          buffer->count); out:         mutex_unlock(&buffer->mutex);         return retval; } static int fill_read_buffer(struct dentry * dentry, struct sysfs_buffer * buffer) {         struct sysfs_dirent *attr_sd = dentry->d_fsdata;         //取得父目录的kobject,也就是platform的kobject         struct kobject *kobj = attr_sd->s_parent->s_dir.kobj;         //还记得这个buffer->ops在什么时候进行赋值的么?         struct sysfs_ops * ops = buffer->ops;         int ret = 0;         ssize_t count;         if (!buffer->page)                 buffer->page = (char *) get_zeroed_page(GFP_KERNEL);         if (!buffer->page)                 return -ENOMEM;         if (!sysfs_get_active_two(attr_sd))                 return -ENODEV;         buffer->event = atomic_read(&attr_sd->s_attr.open->event);         //调用ops->show  也就是bus_sysfs_ops->show    具体就是bus_attr_show了         //参数为父目录的kobject, bus_attr_drivers_autoprobe->attr,和一段char信息         count = ops->show(kobj, attr_sd->s_attr.attr, buffer->page);         sysfs_put_active_two(attr_sd);         if (count >= (ssize_t)PAGE_SIZE) {                 print_symbol("fill_read_buffer: %s returned bad count/n",                         (unsigned long)ops->show);                 /* Try to struggle along */                 count = PAGE_SIZE - 1;         }         if (count >= 0) {                 buffer->needs_read_fill = 0;                 buffer->count = count;         } else {                 ret = count;         }         return ret; } 现在进入bus_attr_show中 static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,char *buf) {         //提取attr的上层结构,也就是bus_attr_drivers_autoprobe         struct bus_attribute *bus_attr = to_bus_attr(attr);         //提取kobj的上上层结构,也就是bus_type_private         struct bus_type_private *bus_priv = to_bus(kobj);         ssize_t ret = 0;         if (bus_attr->show)                 //终于到了这里,最后的调用,调用bus_attr_drivers_autoprobe.show ,也就是show_drivers_autoprobe                 //参数为bus_priv->bus,也就是platform_bus_type , 及一段char信息                 ret = bus_attr->show(bus_priv->bus, buf);         return ret; } static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf) {         return sprintf(buf, "%d/n", bus->p->drivers_autoprobe); } 没什么好介绍了就是打印 buf + bus->p->drivers_autoprobe   从结果来看~ buf是空的 到这里,终于把内核的信息给打印出来了,千辛万苦,层层调用,就是为了取得上层kobject结构,逆运算再取得kobject的上层结构 大家是否对kobject有所了解了呢?~   在对kobject进行介绍之前  还是先把write操作讲完吧 哈哈~ write操作和read操作重要的步骤基本是一致的,只不过在最后的调用中 static ssize_t store_drivers_autoprobe(struct bus_type *bus,                                        const char *buf, size_t count) {         if (buf[0] == '0')                 bus->p->drivers_autoprobe = 0;         else                 bus->p->drivers_autoprobe = 1;         return count; } 不进行打印而对内核的参数进行了修改而已 好~ 现在让我们来看看kobject吧 kobject的结构如下 struct kobject {         const char                *name;          //kobject的名字         struct kref                kref;                                //kobject的原子操作         struct list_head        entry;         struct kobject                *parent;                        //父对象         struct kset                *kset;                        //父容器         struct kobj_type        *ktype;                        //ktype         struct sysfs_dirent        *sd;                                //文件节点         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; }; kobject描述的是较具体的对象,一个设备,一个驱动,一个总线,一类设备 在层次图上可以看出,每个存在于层次图中的设备,驱动,总线,类别都有自己的kobject kobject与kobject之间的层次由kobject中的parent指针决定 而kset指针则表明了kobject的容器 像platform_bus 和test_device的kset都是devices_kset 呢parent和kset有什么不同呢 我认为是人工和默认的区别,看下面这张图 ,蓝框为kset,红框为kobject     容器提供了一种默认的层次~  但也可以人工设置层次 对于kobject现在我只理解了这么多,欢迎大家指出有疑问的地方 最后是kset,kset比较简单,看下面的结构 struct kset {         struct list_head list;         spinlock_t list_lock;         struct kobject kobj;         struct kset_uevent_ops *uevent_ops; }; 对于kset的描述,文档里也有介绍 /** * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. * * A kset defines a group of kobjects.  They can be individually * different "types" but overall these kobjects all want to be grouped * together and operated on in the same manner.  ksets are used to * define the attribute callbacks and other common events that happen to * a kobject. 翻译过来大概就是 结构kset,一个指定类型的kobject的集合,属于某一个指定的子系统 kset定义了一组kobject,它们可以是不同类型组成但却希望捆在一起有一个统一的操作 kset通常被定义为回调属性和其他通用的事件发生在kobject上 可能翻译的不是很好,望大家见谅 从结构中能看出kset比kobject多了3个属性 list_head                                //列表 spinlock_t                        //共享锁 kset_uevent_ops                //uevent操作集 list_head        连接了所有kobject中kset属性指向自己的kobject 而kset_uevent_ops则用于通知机制,由于uevent的作用我也没接触过,所以暂不解析uevent的机制了 写到这里,不知道大家对内核驱动架构中的注册和对kobject的了解有无加深呢?   欢迎转载 = 3= 转载请标上转自BLOG : zwolf.cublog.cn
    最新回复(0)