解析kobject、kobj_type和kset

一、kobject

/sys目录(即sysfs虚拟文件系统)下的每一个目录项(包括子目录)都对应着一个kobject结构体实例,创建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;
};

二、kobj_attribute

kobject对应目录下可能存在若干文件,这些文件被称为属性文件,每个文件对应着一个kobj_attribute结构体实例,创建kobj_attribute结构体实例时需要指定文件名、用户权限、读写函数(可选)。
include/linux/kobject.h:

struct kobj_attribute {
	struct attribute attr;
	ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
			char *buf);
	ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
			 const char *buf, size_t count);
};

三、attribute_group

若干kobj_attribute结构体的组合,将kobj_attribute绑定到kobject时需要用到attribute_group结构体。
include/linux/sysfs.h:

/**
 * struct attribute_group - data structure used to declare an attribute group.
 * @name:	Optional: Attribute group name
 *		If specified, the attribute group will be created in
 *		a new subdirectory with this name.
 * @is_visible:	Optional: Function to return permissions associated with an
 *		attribute of the group. Will be called repeatedly for each
 *		non-binary attribute in the group. Only read/write
 *		permissions as well as SYSFS_PREALLOC are accepted. Must
 *		return 0 if an attribute is not visible. The returned value
 *		will replace static permissions defined in struct attribute.
 * @is_bin_visible:
 *		Optional: Function to return permissions associated with a
 *		binary attribute of the group. Will be called repeatedly
 *		for each binary attribute in the group. Only read/write
 *		permissions as well as SYSFS_PREALLOC are accepted. Must
 *		return 0 if a binary attribute is not visible. The returned
 *		value will replace static permissions defined in
 *		struct bin_attribute.
 * @attrs:	Pointer to NULL terminated list of attributes.
 * @bin_attrs:	Pointer to NULL terminated list of binary attributes.
 *		Either attrs or bin_attrs or both must be provided.
 */
struct attribute_group {
	const char		*name;
	umode_t			(*is_visible)(struct kobject *,
					      struct attribute *, int);
	umode_t			(*is_bin_visible)(struct kobject *,
						  struct bin_attribute *, int);
	struct attribute	**attrs;
	struct bin_attribute	**bin_attrs;
};

使用如下代码将kobj_attribute结构体传入attribute_group结构体:

static struct kobj_attribute foo_attribute =
	__ATTR(foo, 0664, foo_show, foo_store);
static struct kobj_attribute bar_attribute =
	__ATTR(bar, 0664, bar_show, bar_store);

static struct attribute *attrs[] = {
	&foo_attribute.attr,
	&bar_attribute.attr,
	NULL,	/* need to NULL terminate the list of attributes */
};

static struct attribute_group attr_group = {
	.attrs = attrs,
};

四、kobj_type

用户访问属性文件时需要用到上级目录对应的kobject结构体中的kobj_type结构体。sysfs_ops结构体包含了一系列操作属性文件的函数,这些函数会从default_attrs指针数组中取出属性文件对应的attribute,由此找到attribute属于的kobj_attribute结构体,从而取出属性操作函数。
include/linux/kobject.h:

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

五、kset

kset结构体内含一个kobject结构体成员,所以也对应着/sys目录下的目录项。但是kset结构体比kobject多了一个kset_uevent_ops结构体成员,当kset结构体被注册或者kset目录下的kobject被添加、删除、移动、重命名等情况下会自动通过其上级kset结构体内的kset_uevent_ops结构体向用户空间发送uevent事件(调用kobject_uevent函数或者kobject_uevent_env函数,底层由netlink实现,当然我们也可以手动调用这两个函数向用户空间发送uevent事件)。kset结构体还比kobject多了一个链表头,可以将子目录项对应的kobject结构体实例加入该链表,使得kset可以遍历这些子目录项的kobject。注意不要将kset结构体内kobject成员的kset指针指向自己这个kset,kobject结构体内的kset指针只能指向上层的kset结构体,以使用上层kset结构体所提供的发送uevent事件等功能。
include/linux/kobject.h:

/**
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
	struct list_head list;
	spinlock_t list_lock;
	struct kobject kobj;
	const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

六、udev

udev是一个用户空间的应用程序,它会监听来自内核发出的uevent事件,如果这个uevent事件与一系列预设的规则相匹配(用户可以手动编辑这些规则),那么udev就会执行规则所指定的动作(用来处理热插拔设备等功能)。udev默认管理/dev目录下的设备文件,规则默认存储在/etc/udev/rules.d目录下,udev的配置文件是/etc/udev/udev.conf。但是在嵌入式系统中udev常常被放在initrd或initramfs中,如果需要更改udev的配置或者规则则需要重新打包initrd或initramfs,以确保下次开机时更改不会丢失。以下是uevent支持的触发动作的类型:
include/linux/kobject.h:

/*
 * The actions here must match the index to the string array
 * in lib/kobject_uevent.c
 *
 * Do not add new actions here without checking with the driver-core
 * maintainers. Action strings are not meant to express subsystem
 * or device specific properties. In most cases you want to send a
 * kobject_uevent_env(kobj, KOBJ_CHANGE, env) with additional event
 * specific variables added to the event environment.
 */
enum kobject_action {
	KOBJ_ADD,
	KOBJ_REMOVE,
	KOBJ_CHANGE,
	KOBJ_MOVE,
	KOBJ_ONLINE,
	KOBJ_OFFLINE,
	KOBJ_BIND,
	KOBJ_UNBIND,
	KOBJ_MAX
};

七、一些重要的函数和结构体

  • kobject_create_and_add函数,定义于lib/kobject.c文件,创建并且初始化一个kobject,然后将其添加到sysfs中,但是没有初始化kobject的kset指针成员(需要手动指定):
    /**
     * kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
     *
     * @name: the name for the kobject
     * @parent: the parent kobject of this kobject, if any.
     *
     * This function creates a kobject structure dynamically and registers it
     * with sysfs.  When you are finished with this structure, call
     * kobject_put() and the structure will be dynamically freed when
     * it is no longer being used.
     *
     * If the kobject was not able to be created, NULL will be returned.
     */
    struct kobject *kobject_create_and_add(const char *name, struct kobject *parent);
    
  • sysfs_create_group函数,定义于fs/sysfs/group.c文件,将kobject与其所有的kobj_attribute一次性绑定到一起:
    /**
     * sysfs_create_group - given a directory kobject, create an attribute group
     * @kobj:	The kobject to create the group on
     * @grp:	The attribute group to create
     *
     * This function creates a group for the first time.  It will explicitly
     * warn and error if any of the attribute files being created already exist.
     *
     * Returns 0 on success or error code on failure.
     */
    int sysfs_create_group(struct kobject *kobj,
    		       const struct attribute_group *grp);
    
  • kset_create_and_add函数,定义于lib/kobject.c文件,创建并且初始化一个kset,然后将其添加到sysfs中:
    /**
     * kset_create_and_add - create a struct kset dynamically and add it to sysfs
     *
     * @name: the name for the kset
     * @uevent_ops: a struct kset_uevent_ops for the kset
     * @parent_kobj: the parent kobject of this kset, if any.
     *
     * This function creates a kset structure dynamically and registers it
     * with sysfs.  When you are finished with this structure, call
     * kset_unregister() and the structure will be dynamically freed when it
     * is no longer being used.
     *
     * If the kset was not able to be created, NULL will be returned.
     */
    struct kset *kset_create_and_add(const char *name,
    				 const struct kset_uevent_ops *uevent_ops,
    				 struct kobject *parent_kobj);
    
  • file_operations到seq_operations的转换,在fs/kernfs/file.c文件中完成:
    const struct file_operations kernfs_file_fops = {
    	.read		= kernfs_fop_read,
    	.write		= kernfs_fop_write,
    	.llseek		= generic_file_llseek,
    	.mmap		= kernfs_fop_mmap,
    	.open		= kernfs_fop_open,
    	.release	= kernfs_fop_release,
    	.poll		= kernfs_fop_poll,
    	.fsync		= noop_fsync,
    };
    
    static int kernfs_fop_open(struct inode *inode, struct file *file)
    {
    	struct kernfs_node *kn = inode->i_private;
    ...
    	struct kernfs_open_file *of;
    ...
    	of = kzalloc(sizeof(struct kernfs_open_file), GFP_KERNEL);
    ...
    	of->kn = kn;
    	of->file = file;
    ...
    	/*
    	 * Always instantiate seq_file even if read access doesn't use
    	 * seq_file or is not requested.  This unifies private data access
    	 * and readable regular files are the vast majority anyway.
    	 */
    	if (ops->seq_show)
    		error = seq_open(file, &kernfs_seq_ops);
    ...
    	of->seq_file = file->private_data;
    	of->seq_file->private = of;
    ...
    }
    
  • seq_operations到kernfs_ops的转换,也在fs/kernfs/file.c文件中完成:
    static const struct seq_operations kernfs_seq_ops = {
    	.start = kernfs_seq_start,
    	.next = kernfs_seq_next,
    	.stop = kernfs_seq_stop,
    	.show = kernfs_seq_show,
    };
    
    static int kernfs_seq_show(struct seq_file *sf, void *v)
    {
    	struct kernfs_open_file *of = sf->private;
    
    	of->event = atomic_read(&of->kn->attr.open->event);
    
    	return of->kn->attr.ops->seq_show(sf, v);
    }
    
  • kernfs_ops到sysfs_ops的转换,在fs/sysfs/file.c文件中完成:
    static const struct kernfs_ops sysfs_file_kfops_rw = {
    	.seq_show	= sysfs_kf_seq_show,
    	.write		= sysfs_kf_write,
    };
    
    /*
     * Determine ktype->sysfs_ops for the given kernfs_node.  This function
     * must be called while holding an active reference.
     */
    static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
    {
    	struct kobject *kobj = kn->parent->priv;
    
    	if (kn->flags & KERNFS_LOCKDEP)
    		lockdep_assert_held(kn);
    	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
    }
    
    /*
     * Reads on sysfs are handled through seq_file, which takes care of hairy
     * details like buffering and seeking.  The following function pipes
     * sysfs_ops->show() result through seq_file.
     */
    static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
    {
    	struct kernfs_open_file *of = sf->private;
    	struct kobject *kobj = of->kn->parent->priv;
    	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
    	ssize_t count;
    	char *buf;
    
    	/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
    	count = seq_get_buf(sf, &buf);
    	if (count < PAGE_SIZE) {
    		seq_commit(sf, -1);
    		return 0;
    	}
    	memset(buf, 0, PAGE_SIZE);
    
    	/*
    	 * Invoke show().  Control may reach here via seq file lseek even
    	 * if @ops->show() isn't implemented.
    	 */
    	if (ops->show) {
    		count = ops->show(kobj, of->kn->priv, buf);
    		if (count < 0)
    			return count;
    	}
    
    	/*
    	 * The code works fine with PAGE_SIZE return but it's likely to
    	 * indicate truncated result or overflow in normal use cases.
    	 */
    	if (count >= (ssize_t)PAGE_SIZE) {
    		print_symbol("fill_read_buffer: %s returned bad count\n",
    			(unsigned long)ops->show);
    		/* Try to struggle along */
    		count = PAGE_SIZE - 1;
    	}
    	seq_commit(sf, count);
    	return 0;
    }
    
  • 由sysfs_ops到kobj_attribute的调用,在lib/kobject.c文件中完成:
    const struct sysfs_ops kobj_sysfs_ops = {
    	.show	= kobj_attr_show,
    	.store	= kobj_attr_store,
    };
    
    /* default kobject attribute operations */
    static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
    			      char *buf)
    {
    	struct kobj_attribute *kattr;
    	ssize_t ret = -EIO;
    
    	kattr = container_of(attr, struct kobj_attribute, attr);
    	if (kattr->show)
    		ret = kattr->show(kobj, kattr, buf);
    	return ret;
    }
    
  • 其它结构体和函数可以去下面的流程图或者源文件里看。

八、流程图

该流程图主要讲了VFS是如何从kernfs_init_inode函数开始一步步调用到sysfs中我们自定义的kobject和kobj_attribute的,以及kobject、kobj_attribute和kset是如何创建、初始化并加入到sysfs中的。

概览:
解析kobject、kobj_type和kset

jpg格式原图下载链接:
解析kobject、kobj_type和kset.jpg
drawio流程图文件下载链接:
解析kobject、kobj_type和kset.drawio

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值