我是靠谱客的博主 顺利皮皮虾,最近开发中收集的这篇文章主要介绍设备模型管理对象kobject分析,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一. kobject介绍

        kobject结构首先是在2.5.45内核版本引入的,开始设计的目的是用于管理对象的引用计数,
现在主要用来关联设备模型和设备模型的sysfs接口。因为kobject通常隐藏在更高层的结构体中,
开发驱动的人员很少直接接触kobject,但是理解了kobject,对于设备模型的框架理解是非常有帮助的。本文使用的内核是3.18.45。

二. kobject相关结构体介绍

1. kobject介绍

struct kobject {
    const char        *name;
    struct list_head    entry;
    struct kobject        *parent;
    struct kset        *kset;
    struct kobj_type    *ktype;
    struct kernfs_node    *sd;
    struct kref        kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
    struct delayed_work    release;
#endif
    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;
};

name :kobject的名称

entry :kobject可以内组织成一个链表形式

parent :kobject是以分级结构排列的,parent指针指向该kobject的上一级的kobject

kset :表示该kobject属于哪个kset

ktype :该kobject不在被引用的处理方式,在后面介绍struct kobj_type时介绍

sd :后面研究到了再补充。

kref :kobject的引用计数。

state_initialized :kobject是否初始化了

state_add_uevent_sent,state_remove_uevent_sent :和uevent相关

2. kset介绍

struct kset {
    struct list_head list;
    spinlock_t list_lock;
    struct kobject kobj;
    const struct kset_uevent_ops *uevent_ops;
};

list :表示kset可以组织成一个链表形式

list_lock :操作kset链表时的需要自旋锁保证互斥

kobject :表示kset结构体中包含kobject结构

uevent_ops :表示kset的uevent的处理函数

3. ktype介绍

struct kobj_type {
    void (*release)(struct kobject *kobj);
    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);
};

release :表示kobject不再引用时,调用的释放资源的函数

sysfs_ops : 表示kobject在sysfs虚拟文件系统中的操作函数

default_attrs :后面研究到了再补充

kobj_ns_type_operations :后面研究到了再补充。namespace :后面研究到了再补充

总结:

        1. kobject有一个name和kref字段,并且有一个父指针,用于kobject分级结构管理。

        2. ktype用于当kobject的kref为0时,对kobject的处理方式。

        3. kset字面意思是kobject的组合,list用于将一组kobject形成链表,便于管理。
    
kobject的结构图大致如下:

三. kset相关函数介绍

1. kset_create_and_add函数



static struct kset *kset_create(const char *name,
				const struct kset_uevent_ops *uevent_ops,
				struct kobject *parent_kobj)
{
	struct kset *kset;
	int retval;

	kset = kzalloc(sizeof(*kset), GFP_KERNEL); /* 创建内存空间,存储kset结构 */
	if (!kset)
		return NULL;
	retval = kobject_set_name(&kset->kobj, "%s", name); /* 设置kset->kobj的name */
	if (retval) {
		kfree(kset);
		return NULL;
	}
	kset->uevent_ops = uevent_ops;    /* 设置uevent处理函数 */
	kset->kobj.parent = parent_kobj;  /* 指定kset->kobj的父kobj */

	/*
	 * The kobject of this kset will have a type of kset_ktype and belong to
	 * no kset itself.  That way we can properly free it when it is
	 * finished being used.
	 */
	kset->kobj.ktype = &kset_ktype;  /* 设置ktype */
	kset->kobj.kset = NULL;          /* 指定kobj属于哪个kset,这里是NULL */

	return kset;
}

static void kobject_init_internal(struct kobject *kobj)
{
	if (!kobj)
		return;
	kref_init(&kobj->kref);             /* kobj->kref初始化为1 */
	INIT_LIST_HEAD(&kobj->entry);       /* 初始化kobj->entry链表 */
	kobj->state_in_sysfs = 0;
	kobj->state_add_uevent_sent = 0;
	kobj->state_remove_uevent_sent = 0;
	kobj->state_initialized = 1;        /* kobj的状态已初始化的标识  */
}

void kset_init(struct kset *k)
{
	kobject_init_internal(&k->kobj);
	INIT_LIST_HEAD(&k->list);           /* 初始化kset的链表 */
	spin_lock_init(&k->list_lock);      /* 初始化链表自旋锁 */
}

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);  /* kobj创建,则kobj的父kobj的kref要加1 */

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {    /* 判断kobj是否属于某个kset */
		if (!parent)     /* 如果kobj没有父kobj */
			parent = kobject_get(&kobj->kset->kobj);  /* kobj指向的kset的kobj作为父kobj,并且kref加1 */
		kobj_kset_join(kobj);
		kobj->parent = parent;    /* kobj指向的kset的kobj作为kobj的父kobj */
	}

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

	return error;
}

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); /* kset->kobj注册会产生一个KOBJ_ADD的uevent事件 */
	return 0;
}

struct kset *kset_create_and_add(const char *name,
				 const struct kset_uevent_ops *uevent_ops,
				 struct kobject *parent_kobj)
{
	struct kset *kset;
	int error;

	kset = kset_create(name, uevent_ops, parent_kobj); /*  */
	if (!kset)
		return NULL;
	error = kset_register(kset);
	if (error) {
		kfree(kset);
		return NULL;
	}
	return kset;
}

        kset_create_and_add函数主要用来创建一个kset结构并且初始化结构体的参数,并关联到它的父结构体中。大致示意图如下

         这里强调一些kobject_add_internal函数的作用,它用于将创建的kset的kobj与其指向的kset建立关联,如果创建的kset的kobj没有父kobj,但是指向一个kset,则将创建的kset的kobj加入到指向的kset的链表中,并且将指向的kset的kobj作为父kobj。示意图如下:

 

 2. kset_release函数

static void kset_release(struct kobject *kobj)
{
	struct kset *kset = container_of(kobj, struct kset, kobj);
	pr_debug("kobject: '%s' (%p): %sn",
		 kobject_name(kobj), kobj, __func__);
	kfree(kset);
}

         使用container_of函数,可以通过kobj定位到它所在的kset,使用free函数回收kset的内存空间。

四. kobject相关函数介绍

1. kobject_init函数

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

	if (!kobj) {    /* 判断kobj是否为NULL */
		err_str = "invalid kobject pointer!";
		goto error;
	}
	if (!ktype) {    /* 判断ktype是否为NULL */
		err_str = "must have a ktype to be initialized properly!n";
		goto error;
	}
	if (kobj->state_initialized) {    /* 判断kobj是否被初始化过 */
		/* 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结构体,介绍kset时解析过 */
	kobj->ktype = ktype;
	return;

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

        kobject_init函数是对kobj指针指向的kobj结构成员进行初始化。

2. kobject_add函数

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
				  va_list vargs)
{
	const char *old_name = kobj->name;    /* 备份kobj的name */
	char *s;

	if (kobj->name && !fmt)    /* kobj的name存在,fmt不合法时,直接返回 */
		return 0;

	kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);   /* 更新kobj的name */
	if (!kobj->name) {
		kobj->name = old_name;
		return -ENOMEM;
	}

	/* ewww... some of these buggers have '/' in the name ... */
	while ((s = strchr(kobj->name, '/')))    /* xx/yy ->xx!yy  */
		s[0] = '!';

	kfree(old_name);
	return 0;
}

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

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) {    /* kobject_add前需先初始化 */
		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主要是将kobj关联到其父kobj中,详细细节参考kset的介绍,区别在于kobj是否指向某一个kset。

3. kobject_get

struct kobject *kobject_get(struct kobject *kobj)
{
	if (kobj)
		kref_get(&kobj->kref);
	return kobj;
}

        kobject_get作用是将kobj的kref加1。

4. kobject_cleanup

static void kobject_cleanup(struct kobject *kobj)
{
	struct kobj_type *t = get_ktype(kobj);
	const char *name = kobj->name;

	pr_debug("kobject: '%s' (%p): %s, parent %pn",
		 kobject_name(kobj), kobj, __func__, kobj->parent);

	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' eventn",
			 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_deln",
			 kobject_name(kobj), kobj);
		kobject_del(kobj);
	}

	if (t && t->release) {
		pr_debug("kobject: '%s' (%p): calling ktype releasen",
			 kobject_name(kobj), kobj);
		t->release(kobj);
	}

	/* free name if we allocated it */
	if (name) {
		pr_debug("kobject: '%s': free namen", name);
		kfree(name);
	}
}

        kobject_cleanup的作用是调用kobj的ktype中释放函数,并且发送uevent消息KOBJ_REMOVE

五. sysfs相关函数介绍

1. sysfs_create_file



static inline int __must_check sysfs_create_file(struct kobject *kobj,
						 const struct attribute *attr)
{
	return sysfs_create_file_ns(kobj, attr, NULL);
}

        sysfs_create_file函数会在kobj->name名称的文件目录下创建文件,文件名称是attr->name。示例参考文章最后的代码举例。

2. sysfs_create_link

/**
 *	sysfs_create_link - create symlink between two objects.
 *	@kobj:	object whose directory we're creating the link in.
 *	@target:	object we're pointing to.
 *	@name:		name of the symlink.
 */
int sysfs_create_link(struct kobject *kobj, struct kobject *target,
		      const char *name)
{
	return sysfs_do_create_link(kobj, target, name, 1);
}

        sysfs_create_link函数会在kobj->name名称的文件目录下创建一个软链接文件,文件名是第三个参数name,该文件链接到target->name的目录文件。示例参考文章最后的代码举例。

六. 总结

代码举例

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/platform_device.h>

struct kset *kobject_test_kset;
struct kset *device_kset;
struct kset *driver_kset;

struct kobject *dev1_kobj;
struct kobject *dev2_kobj;
struct kobject *drv1_kobj;
struct kobject *drv2_kobj;

struct attribute dev1_attr = {
	.name = "dev1"
};

struct attribute dev2_attr = {
	.name = "dev2"
};

struct attribute drv1_attr = {
	.name = "drv1"
};

static int kobject_test_init(void)
{
	kobject_test_kset = kset_create_and_add("kobkect_test", NULL, NULL);

	device_kset = kset_create_and_add("device", NULL, &kobject_test_kset->kobj);
	driver_kset = kset_create_and_add("driver", NULL, &kobject_test_kset->kobj);

	dev1_kobj = kobject_create_and_add("dev1", &device_kset->kobj);
	dev2_kobj = kobject_create_and_add("dev2", &device_kset->kobj);
	drv1_kobj = kobject_create_and_add("drv1", &driver_kset->kobj);
	drv2_kobj = kobject_create_and_add("drv2", &driver_kset->kobj);

    sysfs_create_file(dev1_kobj, &dev1_attr);
	sysfs_create_file(dev2_kobj, &dev2_attr);
	sysfs_create_file(drv1_kobj, &drv1_attr);

	sysfs_create_link(drv2_kobj, drv1_kobj, "drv2");

    return 0;
}


void kobject_test_exit(void)
{
	kobject_put(dev1_kobj);
	kobject_put(dev2_kobj);
	kobject_put(drv1_kobj);
	kobject_put(drv2_kobj);
	kset_unregister(device_kset);
	kset_unregister(driver_kset);
	kset_unregister(kobject_test_kset);
	
	
    return;
}

代码效果

/ # ls -l /sys/
total 0
drwxr-xr-x    2 root     root             0 Jan  1 08:01 block
drwxr-xr-x   12 root     root             0 Jan  1 08:01 bus
drwxr-xr-x   19 root     root             0 Jan  1 08:00 class
drwxr-xr-x    4 root     root             0 Jan  1 08:00 dev
drwxr-xr-x   17 root     root             0 Jan  1 08:00 devices
drwxr-xr-x    3 root     root             0 Jan  1 08:01 firmware
drwxr-xr-x    2 root     root             0 Jan  1 08:01 fs
drwxr-xr-x    5 root     root             0 Jan  1 08:01 kernel
drwxr-xr-x    4 root     root             0 Jan  1 08:01 kobkect_test
drwxr-xr-x   24 root     root             0 Jan  1 08:00 module
/ # ls -l /sys/kobkect_test/
total 0
drwxr-xr-x    4 root     root             0 Jan  1 08:01 device
drwxr-xr-x    4 root     root             0 Jan  1 08:01 driver
/ # ls -l /sys/kobkect_test/device/
total 0
drwxr-xr-x    2 root     root             0 Jan  1 08:01 dev1
drwxr-xr-x    2 root     root             0 Jan  1 08:01 dev2
/ # ls -l /sys/kobkect_test/device/dev1/
total 0
----------    1 root     root          4096 Jan  1 08:16 dev1
/ # ls -l /sys/kobkect_test/device/dev2/
total 0
----------    1 root     root          4096 Jan  1 08:16 dev2
/ # ls -l /sys/kobkect_test/driver/drv1/
total 0
----------    1 root     root          4096 Jan  1 08:17 drv1
/ # ls -l /sys/kobkect_test/driver/drv2/
total 0
lrwxrwxrwx    1 root     root             0 Jan  1 08:17 drv2 -> ../drv1

        kobject通过层级结构管理内核中的设备模型,对它的结构了解非常重要,后面会结合驱动模型一起分析

最后

以上就是顺利皮皮虾为你收集整理的设备模型管理对象kobject分析的全部内容,希望文章能够帮你解决设备模型管理对象kobject分析所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(52)

评论列表共有 0 条评论

立即
投稿
返回
顶部