Linux设备模型基础---操作函数

本文详细介绍了Linux内核中Kobject与Kset的基本操作,包括初始化、添加、删除等函数的实现原理及应用场景。重点讲解了这些操作如何帮助管理内核中的对象。

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

关于kref的操作函数

初始化

?
void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount, 1);
mp_mb();
}

注意smp_mb以及smp_mb_*的目的是保证在多核SMP机器上的一致性,初始化后引用计数为1。

递增引用计数

?
void kref_get(struct kref *kref)
{
        WARN_ON(!atomic_read(&kref->refcount));
        atomic_inc(&kref->refcount);
        smp_mb__after_atomic_inc();
}

递减引用计数

?
int kref_put(struct kref *kref, void(*release)(structkref *kref))
{
        WARN_ON(release == NULL);
        WARN_ON(release == (void(*)(structkref *))kfree);
        if(atomic_dec_and_test(&kref->refcount)) {
                release(kref);
                return1;
        }
        return0;
}

kref_put使用了回调函数机制。如果引用计数递减后为0,将调用函数release来执行一些释放操作。函数kref_sub与本函数类似,只是kref_sub中引用计数可以减小指定的值,不再局限于1。

?
int kref_sub(struct kref *kref, unsigned intcount,
             void(*release)(structkref *kref))
{
        WARN_ON(release == NULL);
        WARN_ON(release == (void(*)(structkref *))kfree);
        if(atomic_sub_and_test((int) count, &kref->refcount)) {
                release(kref);
                return1;
        }
        return0;
}



关于kobject的函数

初始化

  1. ?
    voidkobject_init(structkobject *kobj, structkobj_type *ktype)
    {
            char*err_str;
     
            if(!kobj) {
                    err_str = "invalid kobject pointer!";
                    gotoerror;
            }
            if(!ktype) {
                    err_str = "must have a ktype to be initialized properly!\n";
                    gotoerror;
            }
            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;
    //内核中经常使用goto语句进行错误处理
    error:
            printk(KERN_ERR"kobject (%p): %s\n", kobj, err_str);
            dump_stack();
    }
    EXPORT_SYMBOL(kobject_init);
        该函数首先检测参数的有效性,然后检测该kobject是否已经初始化,然后调用函数kobject_init_internal,最后设置

kobj和ktype之间的映射关系。

?
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加入驱动模型系统中进行管理

?
int kobject_add(structkobject *kobj, structkobject *parent,
                constchar*fmt, ...)
{
        va_listargs;
        intretval;
        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);
        returnretval;
}
EXPORT_SYMBOL(kobject_add);

该函数也仅是进行了参数验证之后,就调用函数kobject_add_varg执行真正的具体工作。

?
staticintkobject_add_varg(structkobject *kobj, structkobject *parent,
                            constchar*fmt, va_listvargs)
{
        intretval;
        retval = kobject_set_name_vargs(kobj, fmt, vargs);
        if(retval) {
                printk(KERN_ERR"kobject: can not set name properly!\n");
                returnretval;
        }
        kobj->parent = parent;
        returnkobject_add_internal(kobj);
}

这个函数先是设置了kobject的名字,最后建立kobj及其父节点之间的映射关系。

?
intkobject_set_name_vargs(structkobject *kobj, constchar*fmt,
                                  va_listvargs)
{
        constchar*old_name = kobj->name;
        char*s;
        if(kobj->name && !fmt)
               return0;
        kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);
        if(!kobj->name)
               return-ENOMEM;
        /* ewww... some of these buggers have '/' in the name ... */
        /* 因为kobject如果通过sysfs导出到用户空间都对应一个目录,所以名称中不能包含‘/’字符 */
        while((s = strchr(kobj->name,'/')))
                s[0] = '!';
        kfree(old_name);
        return0;
}

删除,即从驱动设备模型系统中去掉本kobject。

?
voidkobject_del(structkobject *kobj)
{
        if(!kobj)
                return;
        sysfs_remove_dir(kobj);
        kobj->state_in_sysfs = 0;
        kobj_kset_leave(kobj);
        kobject_put(kobj->parent);
        kobj->parent = NULL;
}

该函数首先从sysfs中去掉响应的目录项,然后从所属kset中去掉本kobject,并修改父节点引用计数。

?
staticvoidkobj_kset_leave(structkobject *kobj)
{
       if(!kobj->kset)
                return;
        spin_lock(&kobj->kset->list_lock);
        list_del_init(&kobj->entry);
        spin_unlock(&kobj->kset->list_lock);
        kset_put(kobj->kset);
}

在本函数中,将本kobject从kset的链表中摘下,调用kset_put函数(下面会有介绍)。

函数kobject_get用于递增kobject引用计数。

?
structkobject *kobject_get(structkobject *kobj)
{
        if(kobj)
                kref_get(&kobj->kref);
        returnkobj;
}

对应的,函数kobject_put用于递增kobject引用计数,如果kobject引用计数为0,则调用kobject_release。

?
voidkobject_put(structkobject *kobj)
{
if(kobj) {
if(!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_put() is being "
"called.\n", kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release);
}
}

以下函数用于删除时的清理工作。

?
staticvoid kobject_release(structkref *kref)
{
        kobject_cleanup(container_of(kref,structkobject, kref));
}
 
staticvoidkobject_cleanup(structkobject *kobj)
{
        structkobj_type *t = get_ktype(kobj);
        constchar*name = kobj->name;
        pr_debug("kobject: '%s' (%p): %s\n",
                 kobject_name(kobj), kobj, __func__);
        if(t && !t->release)
                pr_debug("kobject: '%s' (%p): does not have a release() "
                         "function, it is broken and must be fixed.\n",
                         kobject_name(kobj), kobj);
        /* send "remove" if the caller did not do it but sent "add" */
        if(kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
                pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
                         kobject_name(kobj), kobj);
                kobject_uevent(kobj, KOBJ_REMOVE);
        }
        /* remove from sysfs if the caller did not do it */
        if(kobj->state_in_sysfs) {
                pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
                         kobject_name(kobj), kobj);
                kobject_del(kobj);
        }
        if(t && t->release) {
                pr_debug("kobject: '%s' (%p): calling ktype release\n",
                         kobject_name(kobj), kobj);
                t->release(kobj);
        }
        /* free name if we allocated it */
        if(name) {
                pr_debug("kobject: '%s': free name\n", name);
                kfree(name);
        }
}


有关kset的函数操作

初始化

?
voidkset_init(structkset *k)
{
        kobject_init_internal(&k->kobj);
        INIT_LIST_HEAD(&k->list);
        spin_lock_init(&k->list_lock);
}

本函数中初始化kset结构体内嵌的kobject,然后初始化链表以及自旋锁。由此可以看出,通过内嵌方式,可以实现面向对象中的继承机制。

注册

?
intkset_register(structkset *k)
{
        interr;
        if(!k)
                return-EINVAL;
        kset_init(k);
        err = kobject_add_internal(&k->kobj);
        if(err)
                returnerr;
        kobject_uevent(&k->kobj, KOBJ_ADD);
        return0;
}

所谓注册,无非是将内嵌的kobject加入设备模型系统中,当然,还要想用户空间到处uevent事件。此处的kobject_add_internal值得一看,从这个函数中可以看出kobject父子节点以及所属kset之间的关系规则。

?
static int kobject_add_internal(structkobject *kobj)
{
        interror = 0;
        structkobject *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;
        returnerror;
}

由函数中可以看出,如果kobject的所属kset设定,而其父节点没有设定,则将本kobject加入kset,并将kset作为作为本kobject的父节点。

注销

?
voidkset_unregister(structkset *k)
{
        if(!k)
                return;
        kobject_put(&k->kobj);
}

仅仅是减少内嵌kobject对象的引用计数

kobject和kset

将某一kobject加入kset

?
staticvoidkobj_kset_join(structkobject *kobj)
{
        if(!kobj->kset)
                return;
        kset_get(kobj->kset);
        spin_lock(&kobj->kset->list_lock);
        list_add_tail(&kobj->entry, &kobj->kset->list);
        spin_unlock(&kobj->kset->list_lock);
}


将某一Kobject从kset中移除

?
staticvoidkobj_kset_leave(structkobject *kobj)
{
        if(!kobj->kset)
                return;
        spin_lock(&kobj->kset->list_lock);
        list_del_init(&kobj->entry);
        spin_unlock(&kobj->kset->list_lock);
        kset_put(kobj->kset);
}



此处仅仅讲解了一些静态数据的操作函数,相应的动态数据操作类似,仅仅是数据不再是静态生命,而是动态创建。

注:在内核中经常可以看到某些函数参数和返回值相同,不光类型,也包括值,这样的做法是为了保证代码调用上的一致性,并没有什么特别作用。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值