目录
sysfs文件系统概览
kobject框架
sysfs和kobject实现为两套独立的框架。一个kobject在sysfs中表现为一个目录,属性在sysfs中表现为一个文件。kernfs_node->priv指向kobject;kobject->sd指向kernfs_node,这实现了kobject框架和sysfs的连接。总的来说sysfs实现了文件/目录结构层次的管理;kobject框架提供了底层文件/目录的操作方法。
数据结构主要成员说明
struct kernfs_super_info | |
struct super_block *sb | 指向vfs的super_block |
struct kernfs_root *root | 指向sysfs_root |
struct list_head node | 链接到sysfs_root->supers |
struct kernfs_root | |
struct kernfs_node *kn | 指向根目录对应的kernfs_node |
struct list_head supers | 用与链接挂载到更目录下文件系统的kernfs_super_info |
sysfs中的每一个目录或者文件都有一个kernfs_node来描述,就像vfs中的inode一样。
struct kernfs_node | |
struct kernfs_node *parent | 指向父目录 |
const char *name | 文件或者目录名 |
struct rb_node rb | 用于连接到父目录的红黑树中 |
union { struct kernfs_elem_dir dir; struct kernfs_elem_symlink symlink; struct kernfs_elem_attr attr; }; | struct kernfs_elem_dir { unsigned long subdirs; struct rb_root children; //用于挂接子目录的红黑树 struct kernfs_root *root; }; struct kernfs_elem_symlink { struct kernfs_node *target_kn; }; struct kernfs_elem_attr { //属性文件在sysfs中的通用操作函数集,比如sysfs_prealloc_kfops_rw const struct kernfs_ops *ops; struct kernfs_open_node *open; …… }; |
void *priv | 指向kobject |
unsigned short flags | 可以是这三个值:KERNFS_DIR , KERNFS_FILE ,KERNFS_LINK |
kset为一类kobject的集合,比如devices_kset,在sysfs中对应目录 '/sys/devices'
struct kset | |
struct list_head list | 用于链接kset下面的kobject |
struct kobject kobj | Kset自己的kobject,kset在sysfs中也是一个目录 |
const struct kset_uevent_ops *uevent_ops | 例如device_uevent_ops |
设备驱动中device,bus等结构体中都会内嵌一个kobject,会在sysfs创建一个目录
struct kobject | |
const char *name | Kobject都是嵌入其他结构中,往往与kobject父结构名相同 |
struct list_head entry | 链接到kset中 |
struct kobject *parent | 指向父kobject |
struct kset *kset | 指向所属的kset |
struct kobj_type *ktype | 例如device_ktype,其中包含一组操作函数集 |
struct kernfs_node *sd | 指向sysfs中的kernfs_node |
struct kref kref | 引用计数 |
还有一个属性结构体就不具体讲解了,不同属性类型会有不同的结构实现,但是都内嵌一个struct attribute。
sysfs文件系统挂载
2.1 sysfs文件系统类型注册
-
static
struct
file_system_type sysfs_fs_type = {
-
.name =
"sysfs",
-
.mount = sysfs_mount,
-
.kill_sb = sysfs_kill_sb,
-
.fs_flags = FS_USERNS_MOUNT,
-
};
-
-
int __init sysfs_init(void)
-
{
-
int err;
-
sysfs_root =
kernfs_create_root(
NULL, KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
-
NULL);
// 创建代表sysfs根目录的kernfs_node
-
if (
IS_ERR(sysfs_root))
-
return
PTR_ERR(sysfs_root);
-
sysfs_root_kn = sysfs_root->kn;
-
err =
register_filesystem(&sysfs_fs_type);
// 将sysfs_fs_type 注册到全局链表file_systems中
-
if (err) {
-
kernfs_destroy_root(sysfs_root);
-
return err;
-
}
-
return
0;
-
}
2.2 sysfs挂载
sysfs挂载流程如下:
sget_userns :分配super_block并且让sb->s_fs_info结构体 kernfs_super_info
kernfs_fill_super:初始化super_block中的相关字段,创建根目录对应的dentry和inode
-
static int kernfs_fill_super(struct super_block *sb, unsigned long magic)
-
{
-
struct
kernfs_super_info *info =
kernfs_info(sb);
-
struct
inode *inode;
-
struct
dentry *root;
-
-
info->sb = sb;
-
sb->s_iflags |= SB_I_NOEXEC | SB_I_NODEV;
-
sb->s_blocksize = PAGE_SIZE;
-
sb->s_blocksize_bits = PAGE_SHIFT;
-
sb->s_magic = magic;
-
sb->s_op = &kernfs_sops;
-
sb->s_xattr = kernfs_xattr_handlers;
-
sb->s_time_gran =
1;
-
-
mutex_lock(&kernfs_mutex);
-
inode =
kernfs_get_inode(sb, info->root->kn);
//创建根目录对应的inode
-
mutex_unlock(&kernfs_mutex);
-
-
root =
d_make_root(inode);
//创建根目录对应的dentry
-
kernfs_get(info->root->kn);
-
root->d_fsdata = info->root->kn;
//连接更目录的dentry和kernfs_node
-
sb->s_root = root;
-
sb->s_d_op = &kernfs_dops;
-
return
0;
-
}sysfs文件系统操作
3.1 文件/目录创建
下面以一个例子来讲解sysfs文件/目录创建过程。例子来源linux-4.12.3/drivers/input/evdev.c
-
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
-
const
struct input_device_id *id)
-
{
-
struct
evdev *evdev;
-
int minor;
-
int dev_no;
-
int error;
-
-
evdev =
kzalloc(
sizeof(
struct evdev), GFP_KERNEL);
-
......
-
evdev->dev.parent = &dev->dev;
-
evdev->dev.release = evdev_free;
-
device_initialize(&evdev->dev);
//初始化dev相关字段,尤其是我们关注的kobject
-
......
-
-
error =
cdev_device_add(&evdev->cdev, &evdev->dev);
//将dev添加到内核相关数据结构中并且在proc和sysfs中创建相关文件
-
......
-
}
-
-
void device_initialize(struct device *dev)
-
{
-
dev->kobj.kset = devices_kset;
-
kobject_init(&dev->kobj, &device_ktype);
//让kobj->ktype指向device_ktype,后面我们将会涉及到
-
......
-
}
函数cdev_device_add会进一步调用到device_add:
-
int device_add(struct device *dev)
-
{
-
struct
device *parent;
-
struct
kobject *kobj;
-
......
-
error =
kobject_add(&dev->kobj, dev->kobj.parent,
NULL);
//为dev在sysfs中创建目录
-
if (error) {
-
glue_dir =
get_glue_dir(dev);
-
goto Error;
-
}
-
error =
device_create_file(dev, &dev_attr_uevent);
//在前面创建的目录中创建属性文件uevent
-
if (error)
-
goto attrError;
-
......
-
}
目录创建
kobject在sysfs中表示为一个目录,kobject_add在sysfs中创建一个目录,其流程如下:
sysfs_create_dir_ns: 在sysfs中创建目录的接口。sysfs与kobject关系紧密但kobject并不是默认集成到sysfs
中,sysfs中目录也不是一定有一个kobject与之对应。
populate_dir:为默认属性列表kobj->ktype-> default_attrs在目录kobject下创建对应文件
-
int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
-
{
-
struct
kernfs_node *parent, *kn;
-
if (kobj->parent)
-
parent = kobj->parent->sd;
//获取父目录对应的kernfs_node
-
else
-
parent = sysfs_root_kn;
//如果在sysfs根目录下直接创建目录,sysfs_root_kn在2.1节有讲到过
-
-
kn =
kernfs_create_dir_ns(parent,
kobject_name(kobj),
//完成在sysfs中创建目录的实际工作
-
S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
-
…….
-
-
kobj->sd = kn;
//关联kobject框架和sysfs,可以通过kobject找到在sysfs中对应的kernfs_node
-
return
0;
-
}
从函数kernfs_create_dir_ns的参数可以看出sysfs和kobject是相互独立的,sysfs目录和kobject并不是天生一一对应。
-
struct
kernfs_node *
kernfs_create_dir_ns(
struct kernfs_node *parent,
-
const
char *name,
umode_t mode,
-
void *priv,
const
void *ns)
-
{
-
struct
kernfs_node *kn;
-
int rc;
-
//sysfs中目录或者文件都有一个kernfs_node来描述,这里就是创建一个用于描述sysfs中目录的kernfs_node。
-
// S_IFDIR和KERNFS_DIR表明创建的是一个目录
-
kn =
kernfs_new_node(parent, name, mode | S_IFDIR, KERNFS_DIR);
-
-
kn->dir.root = parent->dir.root;
-
kn->ns = ns;
-
kn->priv = priv;
//这里priv传入的是kobj,可以通过kernfs_node找到对应的kobject
-
-
rc =
kernfs_add_one(kn);
//将kn连接到父目录的红黑树中 kn->parent->dir.children.rb_node
-
}
文件创建
截取前面提到的文件创建代码:
-
int device_add(struct device *dev)
-
{
-
......
-
error =
device_create_file(dev, &dev_attr_uevent);
-
......
-
}
在进一步文件闯将讲解之前先认识下dev_attr_uevent。
static DEVICE_ATTR_RW(uevent);
中间过程就不讲了,宏展开之后如下:
-
struct
device_attribute dev_attr_ uevent = {
-
.attr = {
-
.name = __stringify(_name),
-
.mode =
VERIFY_OCTAL_PERMISSIONS(_mode)
-
},
-
.show = uevent _show,
-
.store = uevent_store,
-
}
函数device_create_file最终会调用到sysfs_add_file_mode_ns,参数attr也就是前面传递下来的属性dev_attr_uevent,下面看在kobject对应目录下创建文件的具体过程:
-
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
-
const
struct attribute *attr,
bool is_bin,
-
umode_t mode,
const
void *ns)
-
{
-
struct
lock_class_key *key =
NULL;
-
const
struct
kernfs_ops *ops;
-
struct
kernfs_node *kn;
-
loff_t size;
-
-
if (!is_bin) {
-
struct
kobject *kobj = parent->priv;
-
const
struct
sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
//这里的ktype就是3.1节提到的device_ktype,这个后面会有讲解。
-
if (sysfs_ops->show && sysfs_ops->store) {
-
if (mode & SYSFS_PREALLOC)
-
ops = &sysfs_prealloc_kfops_rw;
//根据不同的mode给属性设置不同的ops
-
else
-
ops = &sysfs_file_kfops_rw;
-
}
else
if (sysfs_ops->show) {
-
……
-
-
size = PAGE_SIZE;
-
}
else {
-
……
-
}
-
-
kn = __kernfs_create_file(parent, attr->name, mode &
0777, size, ops,
-
(
void *)attr, ns, key);
//创建属性文件,创建文件对应的kernfs_node
-
……
-
}
-
-
struct
kernfs_node *__kernfs_create_file(
struct kernfs_node *parent,
-
const
char *name,
-
umode_t mode,
loff_t size,
-
const
struct kernfs_ops *ops,
-
void *priv,
const
void *ns,
-
struct lock_class_key *key)
-
{
-
struct
kernfs_node *kn;
-
unsigned flags;
-
int rc;
-
-
flags = KERNFS_FILE;
-
/*分配kernfs_node初始化其相关字段,并让kn->parent指向父目录的kernfs_node,也就是dev->kobj->sd,标识S_IFREG表明创建的是普通文件*/
-
kn =
kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
-
-
kn->attr.ops = ops;
//设置属性文件的ops,这个后续讲解文件读取的时候会详细讲解
-
kn->attr.size = size;
-
kn->ns = ns;
-
kn->priv = priv;
//让kn->priv 指向dev_attr_uevent->attr
-
-
if (ops->seq_show)
-
kn->flags |= KERNFS_HAS_SEQ_SHOW;
-
if (ops->mmap)
-
kn->flags |= KERNFS_HAS_MMAP;
-
if (ops->release)
-
kn->flags |= KERNFS_HAS_RELEASE;
-
-
rc =
kernfs_add_one(kn);
//将kn连接到父目录的红黑树中 kn->parent->dir.children.rb_node
-
return kn;
-
}
3.2 sysfs文件打开
dir_inode->i_op->lookup :对应函数kernfs_iop_lookup,用于查找目录下面的文件。
f->f_op->open: 这里对应函数kernfs_fop_open
文件查找
-
const
struct
inode_operations kernfs_dir_iops = {
-
.lookup = kernfs_iop_lookup,
-
……
-
.mkdir = kernfs_iop_mkdir,
-
.rmdir = kernfs_iop_rmdir,
-
.rename = kernfs_iop_rename,
-
};
-
-
static
struct
dentry *
kernfs_iop_lookup(
struct inode *dir,
-
struct dentry *dentry,
-
unsigned
int flags)
-
{
-
struct
dentry *ret;
-
struct
kernfs_node *parent = dentry->d_parent->d_fsdata;
//获取父目录在sysfs中的kernfs_node
-
struct
kernfs_node *kn;
-
struct
inode *inode;
-
const
void *ns =
NULL;
-
// 在parent->dir.children.rb_node中查找文件对应的kernfs_node
-
kn =
kernfs_find_ns(parent, dentry->d_name.name, ns);
-
-
kernfs_get(kn);
-
dentry->d_fsdata = kn;
//连接vfs中dentry和sysfs中的kernfs_node
-
-
inode =
kernfs_get_inode(dir->i_sb, kn);
//创建文件对应的inode,这个后续细讲
-
-
ret =
d_splice_alias(inode, dentry);
//主要工作是dentry与inode以及将dentry加入到缓存中
-
}
函数kernfs_get_inode中分配inode结构然后调用kernfs_init_inode初始化inode相关成员
-
static void kernfs_init_inode(struct kernfs_node *kn, struct inode *inode)
-
{
-
kernfs_get(kn);
-
inode->i_private = kn;
-
inode->i_mapping->a_ops = &kernfs_aops;
-
inode->i_op = &kernfs_iops;
-
-
switch (
kernfs_type(kn)) {
-
case KERNFS_DIR:
//如果是目录的inode就设置i_op和i_fop分别为kernfs_dir_iops和kernfs_dir_fops
-
inode->i_op = &kernfs_dir_iops;
-
inode->i_fop = &kernfs_dir_fops;
-
if (kn->flags & KERNFS_EMPTY_DIR)
-
make_empty_dir_inode(inode);
-
break;
-
case KERNFS_FILE:
//如果inode是普通文件就设置i_fop为kernfs_file_fops
-
inode->i_size = kn->attr.size;
-
inode->i_fop = &kernfs_file_fops;
-
break;
-
case KERNFS_LINK:
-
inode->i_op = &kernfs_symlink_iops;
-
break;
-
default:
-
BUG();
-
}
-
-
unlock_new_inode(inode);
-
}
文件打开
-
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 = file->f_path.dentry->d_fsdata;
-
struct
kernfs_root *root =
kernfs_root(kn);
-
const
struct
kernfs_ops *ops;
-
struct
kernfs_open_file *of;
-
bool has_read, has_write, has_mmap;
-
-
ops =
kernfs_ops(kn);
-
-
has_read = ops->seq_show || ops->read || ops->mmap;
-
has_write = ops->write || ops->mmap;
-
has_mmap = ops->mmap;
-
//分配一个kernfs_open_file来管理一个打开的sysfs文件
-
of =
kzalloc(
sizeof(
struct kernfs_open_file), GFP_KERNEL);
-
if (!of)
-
goto err_out;
-
-
of->kn = kn;
//连接kernfs_open_file和kernfs_node
-
of->file = file;
-
-
if (ops->prealloc) {
-
int len = of->atomic_write_len ?: PAGE_SIZE;
-
of->prealloc_buf =
kmalloc(len +
1, GFP_KERNEL);
//分配读取数据所需内存
-
error = -ENOMEM;
-
if (!of->prealloc_buf)
-
goto err_free;
-
mutex_init(&of->prealloc_mutex);
-
}
-
if (ops->seq_show)
-
error =
seq_open(file, &kernfs_seq_ops);
-
else
-
error =
seq_open(file,
NULL);
//分配seq_file并让file->private_data指向seq_file
-
if (error)
-
goto err_free;
-
-
of->seq_file = file->private_data;
-
of->seq_file->
private = of;
//在后续读写操作中可以通过file->private_data-> private得到kernfs_open_file
-
……
-
-
}
3.3 sysfs文件读取
-
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,
-
};
kn->attr.ops 在3.1节"文件创建"中初始化为sysfs_prealloc_kfops_rw
kobj->ktype->sysfs_ops: 在3.1节中将kobj->ktype设置为device_ktype,device_ktype是静态定义的
device_ktype. sysfs_ops设置为dev_sysfs_ops;
dev_attr: 在3.1节"文件创建"中我们的例子就是创建属性文件dev_attr_uevent. attr,uevent属性是通过
宏DEVICE_ATTR_RW(uevent)静态定义的;
kset->uevent_ops: 在3.1节的例子中我们将dev->kobj.kset 设置为devices_kset,这里的kset->uevent_ops
就是device_uevent_ops.
-
static ssize_t uevent_show(struct device *dev, struct device_attribute *attr,
-
char *buf)
-
{
-
struct
kobject *top_kobj;
-
struct
kset *kset;
-
struct
kobj_uevent_env *env =
NULL;
-
……
-
top_kobj = &dev->kobj;
-
while (!top_kobj->kset && top_kobj->parent)
-
top_kobj = top_kobj->parent;
-
if (!top_kobj->kset)
-
goto out;
-
-
kset = top_kobj->kset;
-
if (!kset->uevent_ops || !kset->uevent_ops->uevent)
-
goto out;
-
-
env =
kzalloc(
sizeof(
struct kobj_uevent_env), GFP_KERNEL);
-
if (!env)
-
return -ENOMEM;
-
-
retval = kset->uevent_ops->
uevent(kset, &dev->kobj, env);
-
if (retval)
-
goto out;
-
-
for (i =
0; i < env->envp_idx; i++)
-
count +=
sprintf(&buf[count],
"%s\n", env->envp[i]);
-
-
return count;
-
}
-
-
static int dev_uevent(struct kset *kset, struct kobject *kobj,
-
struct kobj_uevent_env *env)
-
{
-
struct
device *dev =
kobj_to_dev(kobj);
-
int retval =
0;
-
-
if (
MAJOR(dev->devt)) {
-
const
char *tmp;
-
const
char *name;
-
umode_t mode =
0;
-
kuid_t uid = GLOBAL_ROOT_UID;
-
kgid_t gid = GLOBAL_ROOT_GID;
-
-
add_uevent_var(env,
"MAJOR=%u",
MAJOR(dev->devt));
-
add_uevent_var(env,
"MINOR=%u",
MINOR(dev->devt));
-
name =
device_get_devnode(dev, &mode, &uid, &gid, &tmp);
-
if (name) {
-
add_uevent_var(env,
"DEVNAME=%s", name);
-
……
-
}
-
}
-
......
-
}