现在的位置: 首页 > 综合 > 正文

kobject的一点研究

2013年07月25日 ⁄ 综合 ⁄ 共 37873字 ⁄ 字号 评论关闭

在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下的连接是为了分类查找 

画了张目录图,如下,绿色框的为连接文件,绿色线条为连接的对象 
[attach]228852[/attach] 

题外话:我自身对于这样的分类不是很喜欢,臃肿 重复  而且信息也不好规划,希望在以后的版本能对sys进行大的改造 

现在来看另两个图,也就是构成sys的核心kobject,首先第一个是我去掉了连接部分的内容  也就是绿色线条的目录图 
[attach]228853[/attach] 

第二个是组成这个目录图的核心,kobject图,我也叫他层次图 
[attach]228854[/attach] 

不看大号绿色箭头右边的内容的话是不是发现两个架构相同? 
对的,kobject的层次决定了目录的结构 
kobeject图很大,但也不要担心,里面的内容其实不多,基础框架涉及3个主要结构kset kobject和ktype 
在说明test_ts的注册之前,先让我们看一下sys下的两个基础目录bus,devices 

首先是bus 
bus的注册在/drivers/base/bus.c里 
int __init buses_init(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的目录就建立起来了 

模型如下 
[attach]228855[/attach] 

接下来是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的注册一致,我就不复述了~ 
模型如下 
[attach]228856[/attach] 

然后是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目录建立好了,模型如下,其中红线描绘了目录关系 
[attach]228857[/attach]


 superfight 回复于:2009-01-19 01:28:29

[font=宋体]现在到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_kset 
if (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下的模型如下图 

[attach]228858[/attach] 

有印象的话大家还记得在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_add 
priv->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_ktype 
kset->kobj.parent = parent_kobj; //设置了父对象,为priv->subsys.kobj,也就是platform_bus_type->p->subsys.kobj 
kset->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是一致的,只是名字不同而已,我就不复述了,建立好的模型如下 

[attach]228859[/attach] 

好了~  到了这里,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的模型 
[attach]228860[/attach] 

然后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的模型就建立好了,图就是最上面的层次图,我就不再贴了 

到这里一个基本的框架就建立起来了~[/font]

[ 本帖最后由 superfight 于 2009-1-19 01:59 编辑 ]


 superfight 回复于:2009-01-19 01:31:48

下面,我开始对kobject kset和ktype做分析 

先说说关系,ktype与kobject和kset这两者之前的关系较少,让我画一个图,是这样的 

[attach]228861[/attach] 

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操作中 
[attach]228862[/attach] 

整个流程如上图所示,如何进入到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; 
}; 

抱歉!评论已关闭.