Linux kernel -- 内核对象kobject

内核对象和集合

Linux驱动模型的基础是内核对象。它将总线类型、设备、驱动等看作是内核对象。内核对象的结构为kobject,相当于其它对象的基类。结构kobject的定义如下:

struct kobject {
    const char      *name;    /*kobject的名字*/
    struct list_head    entry;    /*将kobject链接到kset的连接件*/
    struct kobject      *parent;  /*指向kobject的父对象的指针*/
    struct kset     *kset;    /*如果kobject已链接到kset,则指向它*/
    struct kobj_type    *ktype;   /*kobject的类型*/
    struct sysfs_dirent *sd;      /*指向kobject在sysfs内部树中的节点*/
    struct kref     kref;    /*kobject的引用计数*/
    unsigned int state_initialized:1; /*如果为1,表示kobject已被经过初始化*/
    unsigned int state_in_sysfs:1;  /*如果为1,表示kobject已被添加到内核关系树中*/
    unsigned int state_add_uevent_sent:1; /*如果为1,表示kobject已发送过添加事件到用户空间*/
    unsigned int state_remove_uevent_sent:1; /*如果为1,表示kobject已发送过删除事件到用户空间*/
    unsigned int uevent_suppress:1; /*如果为1,表示抑制发送事件到用户空间*/
};

说明:本文所有的源代码均取自内核2.6.32。


对于内核对象kobject,有些成员和方法是内核对象类型特定的。即对该类型的所有内核对象,这些成员和方法是相同的。

struct kobj_type {
    void (*release)(struct kobject *kobj); /*该类型的内核对象的释放方法*/
    struct sysfs_ops *sysfs_ops; /*指向操作表结构的指针,其中实现该类型内核对象的属性读/写方法*/
    struct attribute **default_attrs; /*该类型的内核对象的默认属性*/
};

kset表示内核对象的集合。结构kset的定义如下:

struct kset {
    struct list_head list; /*该kset中的所有kobjetc的链表*/
    spinlock_t list_lock; /*用于遍历该kset的所有kobject的自旋锁*/
    struct kobject kobj; /*该kset的内嵌kobject(递归)*/
    struct kset_uevent_ops *uevent_ops; /*该kset的uevent操作集。相应的函数在kobject发生某种事件时调用,kset可以添加新的环境变量,或者过滤一些uevent*/
};

创建或初始化内核对象

在使用内核对象之前,必须创建或初始化kobject结构体。对应的函数分别是kobject_create或kobject_init。如果用户已为kobject分配了空间,则只需要调用kobject_init。否则,需要调用kobject_create,该函数先为kobject分配空间,接着调用kobject_init。

函数kobject_create定义如下:

struct kobject *kobject_create(void)
{
    struct kobject *kobj;

    kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
    if (!kobj)
        return NULL;

    kobject_init(kobj, &dynamic_kobj_ktype);
    return kobj;
}

函数kobject_init定义如下:

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
    char *err_str;

    if (!kobj) {
        err_str = "invalid kobject pointer!";
        goto error;
    }
    if (!ktype) {
        err_str = "must have a ktype to be initialized properly!\n";
        goto error;
    }
    if (kobj->state_initialized) {
        /* 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);
    kobj->ktype = ktype;
    return;

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

函数首先进行了一些必要的检查。内核对象原则上不应该被重复初始化,但是如果出现这种情况,程序也不退出,而是打印错误信息后继续执行。然后调用函数kobject_init_internal初始化内核对象中的部分变量。


函数kobject_init_internal定义如下:

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初始化了内核对象中的部分变量,主要包括:

  • 初始化内核对象的内嵌引用计数;
  • 初始化内核对象用于链接到kset的连接件;
  • 当前内核对象还没被添加到sysfs文件系统,此外,也没有向用户空间发送任何uevent事件,因此对象的变量都设为0;
  • 该函数执行结束时,意味着内核对象已初始化完成,设置state_initialized为1。

将内核对像添加到sysfs文件系统

在调用kobject_init后,要向sysfs注册该kobject,所以必须调用kobject_add函数。如果kobject结构体不准备在sysfs层次使用,就不用调用kobject_add函数。

函数kobject_add定义如下:

int kobject_add(struct kobject *kobj, struct kobject *parent,
        const char *fmt, ...)
{
    va_list args;
    int retval;

    if (!kobj)
        return -EINVAL;

    if (!kobj->state_initialized) {
        printk(KERN_ERR "kobject '%s' (%p): tried to add an "
               "uninitialized object, something is seriously wrong.\n", kobject_name(kobj), kobj);
        dump_stack();
        return -EINVAL;
    }
    va_start(args, fmt);
    retval = kobject_add_varg(kobj, parent, fmt, args);
    va_end(args);

    return retval;
}

函数kobject_add_varg定义:

static 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);
    if (retval) {
        printk(KERN_ERR "kobject: can not set name properly!\n");
        return retval;
    }
    kobj->parent = parent;
    return kobject_add_internal(kobj);
}

调用kobject_set_name_vargs函数设置内核对象kobject的名字,并设置了父对象。


函数kobject_add_internal定义:

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

    if (!kobj)
        return -ENOENT;

    if (!kobj->name || !kobj->name[0]) {
        WARN(1, "kobject: (%p): attempted to be registered with empty "
             "name!\n", kobj);
        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) {
        if (!parent)
            parent = kobject_get(&kobj->kset->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;

        /* be noisy on error issues */
        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;
}

如果设置了内核对象的kset变量,并且内核对象的parent变量为空,则将它重新设置为kset变量的内嵌内核对象。内核对象的parent变量将决定内核对象在sysfs树中的位置。因此,在该函数调用之前,存在以下三种可能:

  • 如果内核对象的parent和kset变量都已设置,则内核对象在sysfs树中的位置由其parent决定,它将被放置在内核对象的父对象所对应的目录;
  • 如果内核对象的parent为NULL,而kset已设置,在此会把kset变量的内嵌内核对象值赋值给内核对象的parent变量,因此kset决定了内核对象在sysfs树中的位置,它将放置在kset的内嵌内核对象所对应的目录下;
  • 如果内核对象的parent和kset均为NULL,则内核对象将被放置在sysfs文件系统树的根目录下。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值