热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

设备模型三(潜谈sysfs)

前言引出一个问题:假设sysaxx,xx是kobja的属性文件,当对xx进行写操作时,即echo‘1’sysaxx实际上,调用了kobja的ktype中定义的接口函

  1. 前言
    引出一个问题:假设 /sys/a/xx, xx是kobj a的属性文件, 当对xx进行写操作时,即echo ‘1’ > /sys/a/xx
    实际上,调用了kobj a的ktype中定义的接口函数, 这一系列的流程涉及到
    1.属性文件的创建函数 sysfs_create_file
    2.sysfs的挂载
    3.sysfs的读写操作
    下面就围绕上面三点展开讨论
  2. sysfs的挂载
    系统初始化时期已经将sysfs挂载上了形成了一颗独立的VFS树,我们来看看如何形成的,其实如果看了前面介绍的VFS的理解这篇文章,理解sysfs的挂载应该比较轻松
    这里写图片描述
    sysfs_init中

err = register_filesystem(&sysfs_fs_type);//注册文件系统if (!err) {sysfs_mnt = kern_mount(&sysfs_fs_type);//挂载if (IS_ERR(sysfs_mnt)) {printk(KERN_ERR "sysfs: could not mount!\n");err = PTR_ERR(sysfs_mnt);sysfs_mnt = NULL;unregister_filesystem(&sysfs_fs_type);goto out_err;}}

很明显,根据代码, 注册完sysfs_fs_type文件系统后,紧接着挂载

static struct file_system_type sysfs_fs_type = {.name = "sysfs",.mount = sysfs_mount, //挂载函数.kill_sb = sysfs_kill_sb,
};

kern_mount函数会调用sysfs_mount函数
在这里插入图片描述

完成挂载后sysfs_dirent, inode, dentry三者关系
这里写图片描述
就是说,我们的sysfs的VFS树创建完毕


  1. 接下来讨论sysfs_create_file函数
    流程下图所示:
    这里写图片描述
    sysfs_create_file(a的kobj, attr, “xx”)创建完之后(注意,属性文件xx虽然是个文件,但在sysfs中也有对应的目录项,只不过此目录项不是文件夹属性,即:如果是文件夹的情况,比如 kobj_create_and_add函数,函数内部生成文件夹目录项调用的是:sd = sysfs_new_dirent(name, mode, SYSFS_DIR);,而sysfs_create_file函数内部生成属性文件目录项则是 sd = sysfs_new_dirent(name, mode, SYSFS_KOBJ_ATTR);), 仅仅是在sysfs的目录结构上建立了关系,但是并没有生成inode, 这个就很奇怪了啊,跟ext3的套路不一样我去,没关系,我们慢慢看, 我们可以看到sysfs_direct的对应关系(/sys/a/xx)如下图:
    这里写图片描述
    至此,创建文件就这样了

  2. sysfs的读写操作
    话说本以为sysfs的读写操作跟其他VFS差不多,就是找dentry,获取inode,比如ext3文件系统,我在创建文件的时候inode已经生成了,但是sysfs不同, 通过调用sysfs_create_file之后,并没有生成indoe, 那什么时候生成inode呢? 是在打开文件的时候生成的inode…
    对于文件的打开操作,在VFS的一文的讨论中并没有说明,接下来需讨论一下(依旧跟据上面的例子继续分析),看下图为当前的目录情况
    这里写图片描述
    当调用open函数时,调用关系如下图
    这里写图片描述
    这里补充一下:
    sysfs_dir_inode_operations 中 有sysfs_lookup函数,此函数首先:
    ①通过 a的目录项寻找 xx的目录项 (通过上面的 sys_create_file已创建)。
    ②生成一个新的 inode,并初始化,与传入进来的 dentry(xx的)建立关系。dentry->d_inode = inode;
    ③xx的inode与目录项目建立关系:dentry->d_fsdata = sd(xx的目录项sysfs_dirent)

上图给出了open的调用关系链,接下来看看inode, dentry, sysfs_direct 在open过程中的情况
这里写图片描述
关于步骤1,在补充解释一下:
如果再 /sys 上建立目录,但是没被打开,即、/sys/a/xx
则open(/sys/a/xx)后,
①通过dentry(/sys)找 a的dentry,发现没找到
②新建立 dentry(a), 调用 sysfs_lookup 建立 a的inode,a的目录项,a的dentry三者关系,
③在回到 步骤1 (goto retry),以同样的方法,处理xx文件

讲到这里,个人认为基本上很清晰了,说来也奇怪, sysfs居然在open的时候才生成inode, 为什么这个设计不清楚啊,如果有谁知道请告知一下,万分感谢,不过想想有一点可以确认的是,这么作似乎在节省inode缓存空间的使用, 意图在用的时候生成inode,假设如果sysfs在创建文件的时候生成inode,那它所生成的inode基本上不会iput(释放掉,从而让出inode空间)…,而像ext3这样的文件系统,基本上生成的文件(inode),写完就close了,close的时就iput了,所以…
之后调用完sysfs_lookup函数后, 调用__dentry_open函数,最后调用sysfs_open_file函数(VFS惯用伎俩),补充啰嗦一句, xx是文件类型(非目录),所以.open调用的是sysfs_open_file

static int sysfs_open_file(struct inode *inode, struct file *file)
{struct sysfs_dirent *attr_sd = file->f_path.dentry->d_fsdata; //大家可以看到这里的 attr_sd 就是 xx的sysfs_directstruct kobject *kobj = attr_sd->s_parent->s_dir.kobj;//这里attr->sd_sparent是a的sd,这里获取的kobj是a的kobj,关于s_dir.kobj的赋值可以参考前一篇文章struct sysfs_buffer *buffer;const struct sysfs_ops *ops;int error = -EACCES;/* need attr_sd for attr and ops, its parent for kobj */if (!sysfs_get_active(attr_sd))return -ENODEV;/* every kobject with an attribute needs a ktype assigned */if (kobj->ktype && kobj->ktype->sysfs_ops)ops = kobj->ktype->sysfs_ops; //将a的ktype赋值给opselse {WARN(1, KERN_ERR "missing sysfs attribute operations for ""kobject: %s\n", kobject_name(kobj));goto err_out;}

接着来

buffer = kzalloc(sizeof(struct sysfs_buffer), GFP_KERNEL); //生成了一个bufferif (!buffer)goto err_out;mutex_init(&buffer->mutex);buffer->needs_read_fill = 1;buffer->ops = ops; //将ops扔进buffer中file->private_data = buffer; //将buffer扔进file->private_data

这样open函数后, 在进行read或者write, 直接file->private_data->ops就可以了,如下图所示
这里写图片描述

其实就是操作xx,就是调用了a的ktype
总结:
此篇的讨论结束了,讨论了sysfs的挂载,以及读写脉络,最后贴出一张经典图
这里写图片描述
如上图sysfs超级块sysfs_sb、dentry根目录root、sysfs_direct根目录sysfs_root都是在sysfs初始化时创建。
sysfs_root下的子节点是添加设备对象或对象属性时调用sysfs_create_dir/ sysfs_create_file创建的,同时会申请对应的inode的索引号s_ino。注意此时并未创建inode。
inode是在用到的时候调用sysfs_get_inode函数创建并依据sysfs_sb地址和申请到的s_ino索引计算散列表位置放入其中。
dentry的子节点也是需要用的时候才会创建。比如open文件时,会调用path_walk根据路径一层层的查找指定dentry,如果找不到,则创建一个,并调用父dentry的inode的lookup函数(sysfs文件系统的为sysfs_lookup)查找对应的子inode填充指定的dentry。
说道这里大家可能对ktype还是没什么赶脚,没关系,带着问题继续往下看


推荐阅读
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
  • 本文介绍了Swing组件的用法,重点讲解了图标接口的定义和创建方法。图标接口用来将图标与各种组件相关联,可以是简单的绘画或使用磁盘上的GIF格式图像。文章详细介绍了图标接口的属性和绘制方法,并给出了一个菱形图标的实现示例。该示例可以配置图标的尺寸、颜色和填充状态。 ... [详细]
  • 本文整理了Java面试中常见的问题及相关概念的解析,包括HashMap中为什么重写equals还要重写hashcode、map的分类和常见情况、final关键字的用法、Synchronized和lock的区别、volatile的介绍、Syncronized锁的作用、构造函数和构造函数重载的概念、方法覆盖和方法重载的区别、反射获取和设置对象私有字段的值的方法、通过反射创建对象的方式以及内部类的详解。 ... [详细]
  • 微信官方授权及获取OpenId的方法,服务器通过SpringBoot实现
    主要步骤:前端获取到code(wx.login),传入服务器服务器通过参数AppID和AppSecret访问官方接口,获取到OpenId ... [详细]
author-avatar
安淡言
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有