Linux设备模型-kobject

每一个kobject对象都会关联一个sysfs文件目录,本节重点关注如何将kobject对象与sysfs文件系统关联起来,关注kobject对象默认的属性文件操作接口

1.kobject

首先看一下kobject的结构体定义 include/linux/kobject.h

struct kobject {
	const char		*name;
	struct list_head	entry;
	struct kobject		*parent;
	struct kset		*kset;
	struct kobj_type	*ktype;
	struct kernfs_node	*sd; /* sysfs directory entry */
	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;
};
复制代码

各参数的意义为:

/*这个kobject对象的名字*/
const char		*name;
/*链表结点,让kset对象将kobject串起来进行管理*/
struct list_head	entry;
/*该kobject对象的上层结点*/
struct kobject		*parent;
/*该kobject对象所属于的kset对象*/
struct kset		*kset;
/*该kobject对象的sysfs文件系统的相关操作和属性,重要*/
struct kobj_type	*ktype;
/*该kobject对象在sysfs文件系统中所对应的文件目录*/
struct kernfs_node	*sd; /* sysfs directory entry */
/*该kobject对象的引用次数*/
struct kref		kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work	release;
#endif
/*该kobject对象的初始化状态*/
unsigned int state_initialized:1;
/*表示kobject对象是否在sysfs下建立目录*/
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;
复制代码

2.构建kobject对象

下面将从源码分析如何构建kobject对象以及kobject对象怎么关联sysf文件系统

2.1 kobject_create_and_add

在lib/kobject.c中有kobject_create_and_add函数,此函数为构建kobject对象的入口

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) {
		pr_warn("%s: kobject_add error: %d\n", __func__, retval);
		kobject_put(kobj);
		kobj = NULL;
	}
	return kobj;
}
复制代码

其中kobject_create()作用是创建并且初始化一个kobject对象,kobject_add(kobj, parent, "%s", name)则是在sysfs目录下创建一个目录项与该kobject对象进行关联

2.2.1 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;
}

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

const struct sysfs_ops kobj_sysfs_ops = {
	.show	= kobj_attr_show,
	.store	= kobj_attr_store,
};
复制代码

先动态分配内存,存放kobject对象,并调用kobject_init完成目录属性文件的操作接口设置,设置kobject对象的ktype为dynamic_kobj_ktype,设置dynamic_kobj_ktype中的sysfs_ops变量为kobj_sysfs_ops,设置kobj_sysfs_ops为show变量为kobj_attr_show,store变量为kobj_attr_store,这就是该kobject对象默认的属性文件操作接口,分别对应着读写操作

2.3 kobject_init

lib/kobject.c

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

。。。。

	kobject_init_internal(kobj);
	kobj->ktype = ktype;
        return;

。。。。
}

复制代码

kobj->ktype = ktype用来初始化目录属性文件操作接口

2.4 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;
}
复制代码

kref_init(&kobj->kref)出初始化该kobject对象的引用次数,INIT_LIST_HEAD(&kobj->entry)初始化链表指针,kobj->state_in_sysfs = 0表示该kobject读写还没有与sysfs文件目录相关联,kobj->state_initialized = 1代表kobject对象已经初始化了

扫描二维码关注公众号,回复: 13769323 查看本文章

2.5 kobject_add

kobject_create函数大致已经分析完成,下面分析kobject_add函数,位于内核代码的lib/kobject.c下 ,函数原型为:

int kobject_add(struct kobject *kobj, struct kobject *parent,
		const char *fmt, ...)
{
	va_list args;
	int retval;
...........
	va_start(args, fmt);
	retval = kobject_add_varg(kobj, parent, fmt, args);
	va_end(args);
	return retval;
}
复制代码
struct kobject *kobj:表示传入的kobject对象,由前面kobject_create创建
struct kobject *parent:表示传入的kobject对象的parent指针
const char *fmt, ...:一堆变参
复制代码

2.6 kobject_add_varg

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);
	if (retval) {
		pr_err("kobject: can not set name properly!\n");
		return retval;
	}
	kobj->parent = parent;
	return kobject_add_internal(kobj);
}
复制代码

在这个函数中设置kObject对象的parent指针

2.7 kobject_set_name_vargs

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
				  va_list vargs)
{
	const char *s;

	if (kobj->name && !fmt)
		return 0;
        //参数格式化打印到s字符串中
	s = kvasprintf_const(GFP_KERNEL, fmt, vargs);
	if (!s)
		return -ENOMEM;

	/*
	 * ewww... some of these buggers have '/' in the name ... If
	 * that's the case, we need to make sure we have an actual
	 * allocated copy to modify, since kvasprintf_const may have
	 * returned something from .rodata.
	 */
	if (strchr(s, '/')) {
		char *t;

		t = kstrdup(s, GFP_KERNEL);
		kfree_const(s);
		if (!t)
			return -ENOMEM;
		strreplace(t, '/', '!');
		s = t;
	}
	kfree_const(kobj->name);
        //设置kobject对象的名字
	kobj->name = s;

	return 0;
}
复制代码

kobject_set_name_vargs则是设置kObject对象的名字

2.8 kobject_add_internal

static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;
......
	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)
			pr_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
			pr_err("%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;
}

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


static inline void kref_get(struct kref *kref)
  {
          refcount_inc(&kref->refcount);
  }

复制代码

通过kobject_get(kobj->parent)来获取kobject对象的所属于的上一级kobject对象,如果kobject对象的所属于的上一级kobject对象存在,那么将上一级的kobject对象引用加1,如果kobject对象的kset指针存在,而kobject对象的parent指针不存在,那么将parent设置为kobj->kset->kobj,并通过kobj_kset_join将kObject对象放入放入kset链表的末尾,并设置kobject的parent指针,kobj->state_in_sysfs = 1标志着该kobject对象已经与sysfs文件系统关联起来

2.9 create_dir()

从字面意思可知为创建一个dir,即目录

static int create_dir(struct kobject *kobj)
{
	const struct kobj_type *ktype = get_ktype(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;
	}

	if (ktype) {
		error = sysfs_create_groups(kobj, ktype->default_groups);
		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;
}
复制代码

其中sysfs_create_dir_ns为创建一个目录项,并关联父结点目录项,如果默认的属性存在,populate_dir用来创建默认的属性文件,即前面的kobj_sysfs_ops,sysfs_create_groups也用来创建属性文件接口

2.9 sysfs_create_dir_ns

fs/sysfs/dir.c

int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{
	struct kernfs_node *parent, *kn;
	kuid_t uid;
	kgid_t gid;

	BUG_ON(!kobj);
	
	if (kobj->parent)
		/*获取上一层节点的目录项*/
		parent = kobj->parent->sd;
	else
		/*设置上一层节点的目录项为sysfs根目录*/
		parent = sysfs_root_kn;

	if (!parent)
		return -ENOENT;

	kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
				  S_IRWXU | S_IRUGO | S_IXUGO, uid, gid,
				  kobj, ns);
	...
	/*kobj对象关联sysfs目录项*/
	kobj->sd = kn;
	return 0;
}
复制代码

在这个函数中先获取到上一层结点的目录项,如果上一层结点的目录项不存在的话,那么则设置上一层结点的目录项为sysfs根目录

2.10 kernfs_create_dir_ns

struct kernfs_node *kernfs_create_dir_ns(struct kernfs_node *parent,
					 const char *name, umode_t mode,
					 kuid_t uid, kgid_t gid,
					 void *priv, const void *ns)
{
	struct kernfs_node *kn;
	int rc;

	/* allocate */
	kn = kernfs_new_node(parent, name, mode | S_IFDIR,
			     uid, gid, KERNFS_DIR);
	...
	/*sysfs目录项关联kobject对象*/
	kn->priv = priv;
	...
}
复制代码

其中S_IFDIR和KERNFS_DIR表明创建的是一个目录,

2.11 kernfs_new_node

struct kernfs_node *kernfs_new_node(struct kernfs_node *parent,
				    const char *name, umode_t mode,
				    kuid_t uid, kgid_t gid,
				    unsigned flags)
{
	struct kernfs_node *kn;

	kn = __kernfs_new_node(kernfs_root(parent),
			       name, mode, uid, gid, flags);
	if (kn) {
		kernfs_get(parent);
		kn->parent = parent;
	}
	return kn;
}
复制代码

__kernfs_new_node创建一个新的kernfs_node结点,并且设置其父指针为传进来的kernfs_node *parent,即sysfs_create_dir_ns中创建的父结点目录项

3.总结

以上,我们可以用下图表示

image.png

猜你喜欢

转载自juejin.im/post/7082778807010066463
今日推荐