Sysfs实现原理
来源:互联网 发布:上海通用维修编程系统 编辑:程序博客网 时间:2024/06/06 07:18
目录
1. sysfs文件系统概览 1
2. sysfs文件系统挂载 3
2.1 sysfs文件系统类型注册 3
2.2 sysfs挂载 3
3. sysfs文件系统操作 4
3.1 文件/目录创建 4
3.2 sysfs文件打开 7
3.3 sysfs文件读取 10
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);…… } } ......}
- Sysfs实现原理
- sysfs原理
- sysfs方式实现马达驱动
- sysfs方式实现马达驱动
- sysfs方式实现马达驱动
- sysfs方式实现马达驱动
- sysfs方式实现马达驱动
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- sysfs
- 无人机驾驶员培训学习记录(二)
- kafka管理器kafka-manager部署安装
- 【UML】关系之依赖关系
- jquery中用$.ajax实现注册(html、jquery、php、接口文档)、ajax验证用户提交数据
- 19. 数据结构进阶十九外部排序相关概念
- Sysfs实现原理
- 1018. 锤子剪刀布 (20)
- jQuery+Ajax实现用户登录
- 20. 数据结构进阶二十文件相关概念
- JSP
- python SyntaxError: Non-ASCII character '\xe4' in file
- Kotlin可以拯救Java程序员,但Java9程序员不用!
- 动态规划--01背包问题
- 【笔记+模板】 数论