设备驱动中的kobject(kernel-4.7)

本文深入剖析Linux设备模型中的kobject数据结构,探讨kobject的初始化过程,如何建立层次结构并将其添加到sysfs文件系统。分析了kobject的引用计数、属性及其在sysfs中的表示,详细解释了kobject如何与sysfs交互,创建对应的目录和属性文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

kobject是Linux设备模型的最底层数据结构,它代表一个内核对象。
kobject结构体定义在include/linux/kobject.h文件中:


struct kobject {
    const char      *name; //这个内核对象的名字,在sysfs文件系统中,name将以一个目录的形式出现
    struct list_head    entry; //用于将该内核对象链接进其所属的kset的内核对象链表
    struct kobject      *parent;//代表该内核对象的父对象,用于构建内核对象的层次结构
    struct kset     *kset; //kset是该内核对象所属的“内核对象集合”
    struct kobj_type    *ktype; //ktype是该内核对象的sysfs文件系统相关的操作函数和属性。
    struct kernfs_node  *sd; /* sysfs directory entrysd表示该内核对象对应的sysfs目录项。 */
    struct kref     kref;//kref的核心数据是一个原子型变量,用于表示该内核对象的引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
    struct delayed_work release;
#endif
    unsigned int state_initialized:1;//表示该内核对象是否已经进行过了初始化,1表示已经初始化过了
    unsigned int state_in_sysfs:1;//表示该内核对象是否已经在sysfs文件系统中建立一个入口点
    unsigned int state_add_uevent_sent:1;//表示加入内核对象时是否发送uevent事件
    unsigned int state_remove_uevent_sent:1;//表示删除内核对象时是否发送uevent事件
    unsigned int uevent_suppress:1;//表示内核对象状态发生变化时,是否发送uevent事件
};

kobject初始化分析,从kobject_init_and_add函数开始看,该函数完成对kobject的初始化,建立kobject的层次结构,并将kobject添加到sysfs文件系统中,该函数定义在lib/kobject.c文件中,其内容如下:


/**
 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 * @parent: pointer to the parent of this kobject.
 * @fmt: the name of the kobject.
 *
 * This function combines the call to kobject_init() and
 * kobject_add().  The same type of error handling after a call to
 * kobject_add() and kobject lifetime rules are the same here.
 */
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
             struct kobject *parent, const char *fmt, ...)
{
    va_list args;
    int retval;

    kobject_init(kobj, ktype); //对kobject进行初始化

    va_start(args, fmt); //va_start用于处理可变参数
    retval = kobject_add_varg(kobj, parent, fmt, args); //将kobject添加到kobject层次结构中
    va_end(args);

    return retval;
}

其中,kobject_init函数,其定义如下:

/**
 * kobject_init - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
    char *err_str;

    if (!kobj) {
        err_str = "invalid kobject pointer!";
        goto error;
    }
    if (!ktype) {            //如果ktype为空,则返回一个错误
        err_str = "must have a ktype to be initialized properly!\n";
        goto error;
    }
    if (kobj->state_initialized) {   //如果kobject已经被初始化过,则打印错误信息
        /* do not error out as sometimes we can recover */
        printk(KERN_ERR "kobject (%p): tried to init an initialized "
               "object, something is seriously wrong.\n", kobj);
        dump_stack();
    }

    kobject_init_internal(kobj); //调用kobject_init_internal函数
    kobj->ktype = ktype;  //将传递过来的ktype赋值给kobj->ktype
    return;

error:
    printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
    dump_stack();
}

其中调用了kobject_init_internal函数,该函数定义在lib/kobject.c文件中,其定义如下:


static void kobject_init_internal(struct kobject *kobj)
{
    if (!kobj)
        return;
    kref_init(&kobj->kref);
    INIT_LIST_HEAD(&kobj->entry);
    kobj->state_in_sysfs = 0;
    kobj->state_add_uevent_sent = 0;
    kobj->state_remove_uevent_sent = 0;
    kobj->state_initialized = 1;
}

kobject_init_internal函数完成对kobject成员变量的初始化,其中对kobj->kref即引用计数的初始化,这是通过kref_init函数完成的,该函数定义在include/linux/kref.h文件中,其定义如下:

    struct kref {
        atomic_t refcount;
    };

    /**
     * kref_init - initialize object.
     * @kref: object in question.
     */
    static inline void kref_init(struct kref *kref)
    {
        atomic_set(&kref->refcount, 1);
    }

引用计数的核心变量是一个原子变量,初始化为1。


回到kobject_init_and_add 函数中,调用kobject_add_varg函数,该函数定义在lib/kobject.c文件中,其内容如下:


static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
                       struct kobject *parent,
                       const char *fmt, va_list vargs)
{
    int retval;

    retval = kobject_set_name_vargs(kobj, fmt, vargs);//解析可变参数,设置kobject.name
    if (retval) {
        printk(KERN_ERR "kobject: can not set name properly!\n");
        return retval;
    }
    kobj->parent = parent; //kobject.parent为parent
    return kobject_add_internal(kobj);
}

其中调用kobject_add_internal函数,该函数定义在lib/kobject.c文件中,其内容如下:



static int kobject_add_internal(struct kobject *kobj)
{
    int error = 0;
    struct kobject *parent;

    if (!kobj)
        return -ENOENT;

    if (!kobj->name || !kobj->name[0]) { //检查是否已经设置kobject.name,如果没有设置则返回错误
        WARN(1, "kobject: (%p): attempted to be registered with empty "
             "name!\n", kobj);
        return -EINVAL;
    }

    parent = kobject_get(kobj->parent); //kobject_get函数取得kobject.parent

    /* join kset if set, use it as parent if we do not already have one */
    if (kobj->kset) {
        if (!parent) 
    //如果parent为空,则将kset.kobject设置为kobject.parent
            parent = kobject_get(&kobj->kset->kobj); 
    //如果kobject.kset不为空,则调用kobj_kset_join函数,将kobject.entry链接进kobject.kset.list中
        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);//创建kobject在sysfs文件系统中对应的目录结构
    if (error) {
        kobj_kset_leave(kobj);
        kobject_put(parent);
        kobj->parent = NULL;

        /* be noisy on error issues */
        if (error == -EEXIST)
            WARN(1, "%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
            WARN(1, "%s failed for %s (error: %d parent: %s)\n",
                 __func__, kobject_name(kobj), error,
                 parent ? kobject_name(parent) : "'none'");
    } else
        kobj->state_in_sysfs = 1; //在sysfs文件系统中创建目录结构成功,则将 kobj->state_in_sysfs设置为1
    return error;
}

其中,kobject_get函数取得kobject.parent,该函数定义在lib/kobject.c文件中,其内容如下:


/**
 * kobject_get - increment refcount for object.
 * @kobj: object.
 */
struct kobject *kobject_get(struct kobject *kobj)
{
    if (kobj) {
        if (!kobj->state_initialized)
            WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
                   "initialized, yet kobject_get() is being "
                   "called.\n", kobject_name(kobj), kobj);
        kref_get(&kobj->kref);
    }
    return kobj;
}

该函数除了返回对应的kobject,还通过调用kref_get函数,增加该kobject的引用计数。

kobject_add_internal 函数中,如果kobject.kset不为空,则调用kobj_kset_join函数,将kobject.entry链接进kobject.kset.list


kobject_add_internal函数中调用create_dir函数创建kobject在sysfs文件系统中对应的目录结构,该函数定义在lib/kobject.c文件中,其内容如下:


static int create_dir(struct kobject *kobj)
{
    const struct kobj_ns_type_operations *ops;
    int error;

    error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj)); 
    if (error)
        return error;

    error = populate_dir(kobj);
    if (error) {
        sysfs_remove_dir(kobj);
        return error;
    }

    /*
     * @kobj->sd may be deleted by an ancestor going away.  Hold an
     * extra reference so that it stays until @kobj is gone.
     */
    sysfs_get(kobj->sd);

    /*
     * If @kobj has ns_ops, its children need to be filtered based on
     * their namespace tags.  Enable namespace support on @kobj->sd.
     */
    ops = kobj_child_ns_ops(kobj);
    if (ops) {
        BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
        BUG_ON(ops->type >= KOBJ_NS_TYPES);
        BUG_ON(!kobj_ns_type_registered(ops->type));

        sysfs_enable_ns(kobj->sd);
    }

    return 0;
}

create_dir()在sysfs中创建kobj对应的目录,populate_dir()创建kobj中默认属性对应的文件。create_dir()正是调用populate_dir()实现的。该函数定义在lib/kobject.c文件中,其内容如下:


/*
 * populate_dir - populate directory with attributes.
 * @kobj: object we're working on.
 *
 * Most subsystems have a set of default attributes that are associated
 * with an object that registers with them.  This is a helper called during
 * object registration that loops through the default attributes of the
 * subsystem and creates attributes files for them in sysfs.
 */
static int populate_dir(struct kobject *kobj)
{
    struct kobj_type *t = get_ktype(kobj);
    struct attribute *attr;
    int error = 0;
    int i;

    if (t && t->default_attrs) {
        for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
            error = sysfs_create_file(kobj, attr);
            if (error)
                break;
        }
    }
    return error;
}

至此,先后分析了kobject_add_internal函数把kobj加入已有的结构;相应的kobject_add_varg函数;相应的kobject_init_and_add函数。可以看到,kobject_init_and_add函数完成了对kobject初始化,建立了kobject的层次结构,并将kobject添加到sysfs文件系统中。


kobject的属性
kobject.ktype代表这个kobject的类型属性,它是struct kobj_type类型,该结构体定义在include/linux/kobject.h文件中,如下

struct kobj_type {
    void (*release)(struct kobject *kobj);//release函数用于在释放kobject对象时进行一些清理工作
    const struct sysfs_ops *sysfs_ops; //struct sysfs_ops类型的指针
    struct attribute **default_attrs;//struct attribute数组,其中保存了kobject的默认attribute
    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
    const void *(*namespace)(struct kobject *kobj);
};

其中 sysfs_opsstruct sysfs_ops类型的指针,该结构体定义在include/linux/sysfs.h文件中,其定义如下:

struct sysfs_ops {
    ssize_t (*show)(struct kobject *, struct attribute *, char *);
    ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

sysfs_ops定义了对struct attribute进行操作的函数,其中,show用于将要显示的内容填充到第三个参数指定的内存空间中,并最终显示给用户空间。store用于设置attribute内容。
kobj_type 结构体中的default_attrs是一个struct attribute数组,其中保存了kobject的默认attribute。struct attribute定义在include/linux/sysfs.h文件中,其内容如下:

struct attribute {
    const char      *name;
    umode_t         mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
    bool            ignore_lockdep:1;
    struct lock_class_key   *key;
    struct lock_class_key   skey;
#endif
};

在将kobject加入到sysfs文件系统后,在该kobject对应的目录下,会创建kobject的属性文件,default_attrs数组中指定了几个属性,就会创建几个对应的属性文件,那么sysfs_ops->show在读属性文件时被调用,sysfs_ops->store在写属性文件时被调用。

未完待续。。。。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值