Linux设备驱动模型二 kobject

时间:2022-01-22 11:16:47

kobject


1.1 kobject数据结构

kobjectsysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h

struct kobject {   
   /*名称*/
   const char     *name;
   /*与与所属的kset(list成员)组成链表*/
   struct list_head   entry;	
   /*父kobject;此成员未指定时,默认指向所属kset的kobject成员;在/sys文件系统中表示目录的上一层*/
   struct kobject     *parent;	
   /*指向所属的kset,可为NULL*/
   struct kset        *kset;
   /*提供操作kobject属性特征(attribute)的接口*/
   struct kobj_type   *ktype;		
   /*sys文件信息*/
   struct sysfs_dirent    *sd;
   /*kobject的引用计数*/
   struct kref        kref;		
   
   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;
   unsigned int uevent_suppress:1;
};   


kset的定义如下:

struct kset {
	/*与子kobject的entry成员组成链表*/
	struct list_head list;
	/*自旋锁*/
	spinlock_t list_lock;
	/*kobject*/
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
}; 


kobj_type的定义如下:

struct kobj_type {
	/*释放函数*/
	void (*release)(struct kobject *kobj);
	/*sys文件操作函数*/
	const struct sysfs_ops *sysfs_ops;
	/*文件属性*/
	struct attribute **default_attrs;
	const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
	const void *(*namespace)(struct kobject *kobj);
}; 

sysfs_direct被定义在fs/sysfs/sysfs.h,它的定义如下:

struct sysfs_dirent {
	atomic_t		s_count;
	atomic_t		s_active;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
	/*上级目录*/
	struct sysfs_dirent	*s_parent;
	struct sysfs_dirent	*s_sibling;
	/*名称*/
	const char		*s_name;

	const void		*s_ns; /* namespace tag */
	union {
		struct sysfs_elem_dir		s_dir;
		struct sysfs_elem_symlink	s_symlink;
		struct sysfs_elem_attr		s_attr;
		struct sysfs_elem_bin_attr	s_bin_attr;
	};

	unsigned int		s_flags;
	unsigned short		s_mode;
	ino_t			s_ino;
	struct sysfs_inode_attrs *s_iattr;
}; 


1.2 Kobject创建流程


我们看一下kobject的初始化过程。

初始化kobject有两种方式,分别是用kobject_init_and_addkobject_create_and_add函数,他们的区别是:

1)kobject_init_and_add传入一个kobject指针和kobj_type指针,然后进行初始化

2)kobject_create_and_add创建一个kobject变量,并返回其指针,它不用传入kobj_type指针

1.2.1 kobject_init_and_add


下面看kobject_init_and_add函数的实现:

/**
 * 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);

    va_start(args, fmt);
    retval = kobject_add_varg(kobj, parent, fmt, args);
    va_end(args);

    return retval;
} 

它首先调用了kobject_init函数,再调用kobject_add_varg函数。先看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;
    }
    /*ktype不能为NULL*/
    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函数*/
    kobject_init_internal(kobj);
    /*设置ktype*/
    kobj->ktype = ktype;
    return;

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

从上面的代码可以看到,它调用了kobject_init_internal函数,并设置了kobject所指向的ktypektype必须不能为NULL

再看kobject_init_internal函数的实现:

static void kobject_init_internal(struct kobject *kobj)
{
    if (!kobj)
        return;
    /*初始化引用计数器为1*/
    kref_init(&kobj->kref);
    /*初始化entry链表结点,用于与所属的kset的list成员组成链表*/
    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函数初始化引用计数器为1,并初始化entry链表结点。

 

  • 我们再次回到kobject_init_and_add函数中,它接着调用了kobject_add_varg函数:

static int kobject_add_varg(struct kobject *kobj, struct kobject *parent,
                            const char *fmt, va_list vargs)
{
    int retval;
    /*设置kobject的名称*/
    retval = kobject_set_name_vargs(kobj, fmt, vargs);
    if (retval) {
        printk(KERN_ERR "kobject: can not set name properly!\n");
        return retval;
    }
    /*设置父kobject*/
    kobj->parent = parent;
    /*调用kobject_add_internal函数*/
    return kobject_add_internal(kobj);
} 

kobject_add_varg函数主要做了以下3个工作:

1)动过kobject_set_name_vargs设置kobject的名称

2)设置kobjectparent成员,即所指向的父kobject,可以为NULL

3)调用kobject_add_internal函数


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,这里调用了kobject_get,结束的时候必须用kobject_put*/
    parent = kobject_get(kobj->parent);

    /* join kset if set, use it as parent if we do not already have one */
    if (kobj->kset) {
        /*如果parent没有设置,把parent指向所属ket的kobj成员*/
        if (!parent)
            parent = kobject_get(&kobj->kset->kobj);
        /*把kobject的entry成员添加到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>");
    /*创建sys目录*/
    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;
} 

kobject_add_internal函数有几个关键点:

1)如果kobjectkset成员不为NULL,它会调用kobj_kset_join函数把kobjectentry成员添加到ksetlist链表中

2)如果kobjectparent成员为NULL,则把它指向ksetkobject成员。

3)最后调用create_dir函数创建sys目录,关于create_dir,在上一章中有描述。

 

接着看kobj_kset_join函数的实现:

/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *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);
} 

很清楚地看到,此段代码把kobjectentry成员添加到以ksetlist成员为头结点的链表中。


1.2.2 创建流程图


Linux设备驱动模型二 kobject


1.2.3 kobject_create_and_add

kobject_create_and_add的实现如下:

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
	struct kobject *kobj;
	int retval;

	kobj = kobject_create();
	if (!kobj)
		return NULL;

	retval = kobject_add(kobj, parent, "%s", name);
	if (retval) {
		printk(KERN_WARNING "%s: kobject_add error: %d\n",
		       __func__, retval);
		kobject_put(kobj);
		kobj = NULL;
	}
	return kobj;
} 

它调用了kobject_createkobject_add函数。

 

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_and_add函数一样,它也调用了kobject_init函数来初始化kobject,但此时传入的kobj_type指针是系统定义的kobj_type变量:

static struct kobj_type dynamic_kobj_ktype = {
	.release	= dynamic_kobj_release,
	.sysfs_ops	= &kobj_sysfs_ops,
}; 

所以,我们应该明白:通过kobject_init_and_add初始化的kobject,其ktype成员是外部指定的,而通过kobject_create_and_add初始化的kobject,其ktype成员是系统定义的。

  • 再看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函数,此函数的实现在之前已描述过。


1.2.4 创建流程图


Linux设备驱动模型二 kobject


1.2.5 代码示例1

  •  文件kobject_demo1.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

/*kobject变量*/
static struct kobject *mp_kobj;

/*
struct attribute {
	const char		*name;
	mode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};
*/

/*struct attribute变量*/
static struct attribute m_attr = {
    .name = "name",
    .mode = S_IRWXUGO,
};

/*struct attribute数组*/
static struct attribute *m_attrs[] = {
    &m_attr,
    NULL,/*末尾必须为NULL*/

};

/**********************************************************/
//sysfs_ops

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

/*sysfs_ops的show函数实现*/
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
{
    ssize_t count = 0;

    printk("%s\n", __FUNCTION__);
    count = sprintf(buf, "%s\n", kobject_name(kobj) );

    return count;
}

/*sysfs_ops的store函数实现*/
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
                               const char *buf, size_t count)
{
    printk("%s\n", __FUNCTION__);
    return 0;
}

/*struct sysfs_ops变量 */
static struct sysfs_ops m_sys_ops = {
    .show 	= kobj_attr_show,
    .store 	= kobj_attr_store,
};


/**********************************************************/
/*模块加载函数*/
static int __init kobj_init(void)
{
    int error = 0;
    /*struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
    if use kobject_create_and_add, it will auto bind ktype to dynamic_kobj_ktype
    so we can not set ktype member*/
    mp_kobj = kobject_create_and_add("kobj_demo1_1", NULL);
    if (!mp_kobj) {
        goto out;
    }

    printk("%s success.\n", __FUNCTION__);

    return 0;

out:
    printk("%s failed!\n", __FUNCTION__);
    return error;
}

/*模块退出函数*/
static void __exit kobj_exit(void)
{
    //删除kobject
    kobject_del(mp_kobj);
    //使引用计数减1并调用kobj_type的release函数
    kobject_put(mp_kobj);
    printk("%s\n", __FUNCTION__);
}


module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL"); 

 

  • 文件Makefile

FILE=kobject_demo1
obj-m:=$(FILE).o
KERNELBUILD :=/lib/modules/$(shell uname -r)/build
default:
	make -C $(KERNELBUILD) M=$(shell pwd) modules
	echo insmod/rmmod ./$(FILE).ko to load or uninstall
clean:
	rm -rf *.o *.ko *.mod.c .*.cmd *.markers *.order *.symvers .tmp_versions 

  • 编译及运行:

$ make					#编译模块
$ sudo dmesg -c            		#清除内核日志
$ sudo insmod ./kobject_demo1.ko   	#加载内核模块
$ sudo dmesg                		#察看内核日志
[ 1954.839828] kobj_init success.

$ cd /sys/kobj_demo1/           		#进入/sys目录
$ pwd
/sys/kobj_demo1
$ ls
name
$ cat name              			#察看kobject属性
kobj_demo1
$ sudo rmmod ./kobject_demo1.ko        #卸载内核模块
$ sudo dmesg
[ 3609.041498] kobj_release
[ 3609.041501] kobj_exit 

  • kobject_demo1.c的关键地方:

1)通过kobject_init_and_add函数初始化kobject,同时传入了ktype变量。

2)因为没有设置父kobjectkset,所以kobject_demo1出现在/sys顶层目录下

3)在删除kobject的时候,必须调用kobject_put函数,以便清除kobject所有相关的内存,kobject_put函数的实现如下:

/**
 * kobject_put - decrement refcount for object.
 * @kobj: object.
 *
 * Decrement the refcount, and if 0, call kobject_cleanup().
 */
void kobject_put(struct kobject *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);
    }
} 

1.2.6 代码示例2

通过kobject_create_and_add函数创建kobject

  •  文件kobject_demo1_1.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

/*kobject变量*/
static struct kobject *mp_kobj;

/*
struct attribute {
	const char		*name;
	mode_t			mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lock_class_key	*key;
	struct lock_class_key	skey;
#endif
};
*/

/*struct attribute变量*/
static struct attribute m_attr = {
    .name = "name",
    .mode = S_IRWXUGO,
};

/*struct attribute数组*/
static struct attribute *m_attrs[] = {
    &m_attr,
    NULL,/*末尾必须为NULL*/

};

/**********************************************************/
//sysfs_ops

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

/*sysfs_ops的show函数实现*/
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
{
    ssize_t count = 0;

    printk("%s\n", __FUNCTION__);
    count = sprintf(buf, "%s\n", kobject_name(kobj) );

    return count;
}

/*sysfs_ops的store函数实现*/
static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
                               const char *buf, size_t count)
{
    printk("%s\n", __FUNCTION__);
    return 0;
}

/*struct sysfs_ops变量 */
static struct sysfs_ops m_sys_ops = {
    .show 	= kobj_attr_show,
    .store 	= kobj_attr_store,
};


/**********************************************************/
/*模块加载函数*/
static int __init kobj_init(void)
{
    int error = 0;
    /*struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
    if use kobject_create_and_add, it will auto bind ktype to dynamic_kobj_ktype
    so we can not set ktype member*/
    mp_kobj = kobject_create_and_add("kobj_demo1_1", NULL);
    if (!mp_kobj) {
        goto out;
    }

    printk("%s success.\n", __FUNCTION__);

    return 0;

out:
    printk("%s failed!\n", __FUNCTION__);
    return error;
}

/*模块退出函数*/
static void __exit kobj_exit(void)
{
    //删除kobject
    kobject_del(mp_kobj);
    //使引用计数减1并调用kobj_type的release函数
    kobject_put(mp_kobj);
    printk("%s\n", __FUNCTION__);
}


module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL"); 

kobject_demo1.c不同的是,kobject_demo1_1.c并没有创建kobj_type变量

1.2.7 代码示例3


前面两个示例并没有指定父kobjectparent成员,下面指定其parent成员

  • kobject_demo2.c

#include <linux/device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/sysfs.h>
#include <linux/kernel.h>
#include <linux/stat.h>
#include <linux/slab.h>
#include <linux/string.h>

#define ATTR_NAME "name"
#define ATTR_VALUE	"value"

struct my_object {
    struct kobject kobj;
    int value;
};

static struct my_object *m_obj1;
static struct my_object *m_obj2;


/**********************************************************/
//attrubute

static struct attribute m_attr_name = {
    .name = ATTR_NAME,
    .mode = S_IRWXUGO,
};

static struct attribute m_attr_value = {
    .name = ATTR_VALUE,
    .mode = S_IRWXUGO,
};


static struct attribute *m_attrs[] = {
    &m_attr_name,
    &m_attr_value,
    NULL,

};

/**********************************************************/
//sysfs_ops

static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
{
    ssize_t count = 0;
    struct my_object *mobj;

    printk("%s\n", __FUNCTION__);

    if (!strcmp(attr->name, ATTR_NAME)) {
        count = sprintf(buf, "%s\n", kobject_name(kobj) );
    } else if (!strcmp(attr->name, ATTR_VALUE)) {
        mobj = container_of(kobj, struct my_object, kobj);
        count = sprintf(buf, "%d\n", mobj->value);
    }


    return count;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
                               const char *buf, size_t count)
{
    struct my_object *mobj;

    printk("%s\n", __FUNCTION__);

    if (!strcmp(attr->name, ATTR_VALUE)) {
        mobj = container_of(kobj, struct my_object, kobj);
        sscanf(buf, "%d", &mobj->value);
    }

    return count;
}


static struct sysfs_ops m_sys_ops = {
    .show 	= kobj_attr_show,
    .store 	= kobj_attr_store,
};

/**********************************************************/
//kobj_type

void kobj_release(struct kobject *kobj)
{
    struct my_object *mobj = container_of(kobj, struct my_object, kobj);
    if (mobj) {
        //释放my_object内存
        kfree(mobj);
    }
    printk("%s\n", __FUNCTION__);

}

static struct kobj_type m_ktype = {
    .release = kobj_release,
    .sysfs_ops = &m_sys_ops,
    .default_attrs = m_attrs,
};

/**********************************************************/
static int __init kobj_init(void)
{
    int error = 0;
    //申请m_obj1内存
    m_obj1 = (struct my_object *)kzalloc(sizeof(struct my_object), GFP_KERNEL);

    if (!m_obj1) {
        error = -ENOMEM;
        goto out;
    }
    //申请m_obj2内存
    m_obj2 = (struct my_object *)kzalloc(sizeof(struct my_object), GFP_KERNEL);

    if (!m_obj2) {
        error = -ENOMEM;
        goto out1;
    }

    //初始化m_obj1的kobject
    error = kobject_init_and_add(&m_obj1->kobj, &m_ktype, NULL, "kobj_demo1");
    if (error) {
        goto out2;
    }

    ////初始化m_obj2的kobject,并指定其parent为m_obj1->kobj
    error = kobject_init_and_add(&m_obj2->kobj, &m_ktype, &m_obj1->kobj, "kobj_demo2");
    if (error) {
        goto out2;
    }
    //初始化value值
    m_obj1->value = 1;
    m_obj2->value = 2;

    printk("%s success.\n", __FUNCTION__);

    return 0;
out2:
    kfree(m_obj2);
out1:
    kfree(m_obj1);
out:
    printk("%s failed!\n", __FUNCTION__);
    return error;
}

static void __exit kobj_exit(void)
{
    //删除kobject
    kobject_del(&m_obj2->kobj);
    //使引用计数减1并调用kobj_type的release函数
    kobject_put(&m_obj2->kobj);

    kobject_del(&m_obj1->kobj);
    kobject_put(&m_obj1->kobj);

    printk("%s\n", __FUNCTION__);
}


module_init(kobj_init);
module_exit(kobj_exit);

MODULE_AUTHOR("tonny");
MODULE_DESCRIPTION("kobject demo");
MODULE_LICENSE("GPL");