一、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中的。
概览:
jpg格式原图下载链接:
解析kobject、kobj_type和kset.jpg
drawio流程图文件下载链接:
解析kobject、kobj_type和kset.drawio