[Linux 基础] -- sysfs 基本原理与使用方法

 

本文乃fireaxe原创,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,并注明原作者及原链接。内容可任意使用,但对因使用该内容引起的后果不做任何保证。

作者:fireaxe.hq@outlook.com

博客:fireaxe.blog.chinaunix.net 


本文基于linux 4.x代码,对sysfs的基本概念、实现原理进行了介绍;结合cpufreq分析了sysfs的使用方法;最后,通过一个例子对全文进行了总结。

参考文档:https://www.ibm.com/developerworks/cn/linux/l-cn-sysfs/

1、基本概念

sysfs 给应用程序提供了统一访问设备的接口,但可以看到,sysfs 仅仅是提供了一个可以统一访问设备的框架,但究竟是否支持 sysfs 还需要各个设备驱动程序的编程支持;在 2.6 内核诞生 5 年以来的发展中,很多子系统、设备驱动程序逐渐转向了 sysfs 作为与用户空间友好的接口,但仍然也存在大量的代码还在使用旧的 proc 或虚拟字符设备的 ioctl 方式;如果仅从最终用户的角度来说,sysfs 与 proc 都是在提供相同或类似的功能,对于旧的 proc 代码,没有绝对的必要去做 proc 至 sysfs 的升级;因此在可预见的将来,sysfs 会与 proc,debugfs,configfs 等共存很长一段时间。

sysfs 虚拟文件系统提供了一种比 proc 更为理想的访问内核数据的途径。

sysfs 文件系统总是被挂载在 /sys 挂载点上。虽然在较早期的 2.6 内核系统上并没有规定 sysfs 的标准挂载位置,可以把 sysfs 挂载在任何位置,但较近的 2.6 内核修正了这一规则,要求 sysfs 总是挂载在 /sys 目录上;针对以前的 sysfs 挂载位置不固定或没有标准被挂载,有些程序从 /proc/mounts 中解析出 sysfs 是否被挂载以及具体的挂载点,这个步骤现在已经不需要了。

sysfs 与 proc 相比有很多优点,最重要的莫过于设计上的清晰。一个 proc 虚拟文件可能有内部格式,如 /proc/scsi/scsi,它是可读可写的,(其文件权限被错误地标记为了 0444!这是内核的一个 BUG),并且读写格式不一样,代表不同的操作,应用程序中读到这个文件的内容一般还需要进行字符串解析,而在写入时需要先用字符串格式化按指定的格式写入字符串进行操作;相比而言,sysfs 的设计原则是一个属性文件只做一件事情,sysfs 属性文件一般只有一个值,直接读取或写入。整个 /proc/scsi 目录在 2.6 内核中已被标记为过时(LEGACY),它的功能已经被相应的 /sys 属性文件所完全取代。新设计的内核机制应该尽量使用 sysfs 机制,而将 proc 保留给纯净的进程文件系统。

2、 sysfs 目录结构

/sys 下的目录结构是经过精心设计的: 在 /sys/devices 下是所有设备的真是对象,包括如视频卡和以太网卡等真实的设备,也包括 ACPI 等不那么显而易见的真实设备、还有 tty bonding 等纯粹虚拟的设备;在其他目录如 class,bus 等中则在分类的目录中含有大量对 devices 中真实对象引用的符号链接文件。

/sys 下的子目录所包含的内容
/sys/devices这是内核对系统中所有设备的分层表达模型,也是 /sys 文件系统管理设备的最重要的目录结构,下文会对它的内部结构作进一步分析;
/sys/dev这个目录下维护一个按字符设备和块设备的主次设备号(major:minor)链接到真实的设备(/sys/devices 下)的符号链接文件,它是在内核 2.6.26 首次引入;
/sys/bus这是内核设备按总线类型分层放置的目录结构, device 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号连接,它也是构成 Linux 统一设备模型的一部分;
/sys/class这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在 /sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分;
/sys/block这里是系统中当前所有的块设备所在,按照功能来说放置在 /sys/class 之下会更核识,但只是由于历史遗留因素而一直存在于 /sys/block,但从 2.6.22 开始就已标记为过时,只有在打开了 CONFIG_SYSFS_DEPRECATED 配置下编译才会有这个目录的存在,并且在 2.6.26 内核中已正式移到 /sys/class/block,旧的接口 /sys/block 为了向后兼容保留存在,但其中的内容已经变为指向它们在 /sys/devices/ 中真是设备的符号链接文件;
/sys/firmware这里是系统加载固件机制的对用户空间的接口,关于固件有专用于固件加载的一套 API,在附录 LDD3 一书中有关于内核支持固件加载机制的更详细的介绍;
/sys/fs这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,但目前只有 fuse,ext4 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在 sysctl(/proc/sys/fs)接口中;
/sys/kernel这里是内核所有可调整参数的位置,有些内核可调整参数仍然位于 sysctl(/proc/sys/fs)接口中;
/sts/module这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko 文件),都可能会出现在 /sys/module 中:编译为外部模块(ko 文件)在加载后会出现对应的 /sys/module//,并且在这个目录下会出现一些属性文件和属性目录来表示次外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等;编译为内联方式的模块则只在当它有非 0 属性的模块参数时会出现对应的 /sys/module/,这些模块的可用参数会出现在 /sys/module//parameters/ 中,如 /sys/module/printk/parameters/time 这个可读可写参数控制着内联模块 printk 在打印内核消息时是否加上时间前缀;所有内联模块的参数也可以由 ".=" 的形式写在内核启动参数上,如启动内核时加上参数 "printk.time = 1" 与向 "/sys/module/printk/parameters/time" 写入 1 的效果相同;没有非 0 属性参数的内联模快不会出现于此;
/sys/power这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

3、 从 Linux 统一设备模型角度看 sysfs

在 Linux 2.5 内核的开发过程中,人们设计了一套新的设备模型,目的是为了对计算机上的所有设备进行统一地表示和操作,包括设备本身和设备之间的连接关系。这个模型是在分析了 PCI 和 USB 的总线驱动过程中得到的,这两个总线类型能代表当前系统中的大多数设备类型,它们都有完善的热挺拔机制和电源管理的支持,也都有级连机制的支持,以桥接的 PCI/USB 总线控制器的方式可以支持更多的 PCI/USB 设备。为了给所有设备添加统一的电源管理的支持,而不是让每个设备中去独立实现电源管理的支持,人们考虑的是如何尽可能地重用代码;而且在有层次模型的 PCI/USB 总线中,必须以合理形式展示出这个层次关系,这也是电源管理等所要求的必须有层次结构。

如在一个典型的 PC 系统中,中央处理器(CPU)能直接控制的是 PCI 总线设备,而 USB 总线设备是以一个 PCI 设备(PCI-USB桥)的形式接入在 PCI 总线设备上,外部 USB 设备再接入在 USB 总线设备上;当计算机执行挂起(suspend)操作时, Linux 内核应该以 “外部USB设备->USB总线设备->PCI总线设备” 的顺序通知每一个设备将电源挂起;执行恢复(resume)时则以相反的顺序通知;反之如果不按此顺序则将有设备得不到正确的电源状态变迁的通知,将无法正常工作。

sysfs 是在这个 Linux 统一设备模型的开发过程中的一项副产物。为了将这些有层次结构的设备以用户程序可见的方式表达出来,人们很自然想到了利用文件系统的目录树结构(这是以 UNIX 方式思考问题的基础,一切都是文件!)在这个模型中,有几种基本类型,它们的对应关系见 表 2. Linux 统一设备模型的基本结构:

表 2. Linux 统一设备模型的基本结构

类型所包含的内容对应内核数据结构对应 /sys 项
设备(Devices)设备是此模型中最基本的类型,以设备本身的连接按层次组织struct device/sys/devices/*/*/.../
设备驱动(Device Driver)在一个系统中安装多个相同设备,只需要一份驱动程序的支持struct device_driver/sys/bus/pci/driver/*/
总线类型(Bus Types)在整个总线级别对此总线上连接的所有设备进行管理struct bus_type/sys/bus/*/
设备类别(Device Classes)这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在 /sys/class/input/ 下struct class/sys/class/*/

从内核在实现它们时所使用的数据结构来说, Linux 统一设备模型又是以两种基本数据结构进行树型和链表型结构组织的:

  1. kobject:在 Linux 设备模型中最基本的对象,它的功能是提供引用计数和维持父子(parent)结构、平级(sibling)目录关系,上面的 device,device_driver 等各种对象都是以 kobject 基础功能之上实现的;
    struct kobject 
    { 
        const char *name; 
        struct list_head entry; 
        struct kobject *parent; 
        struct kset *kset; 
        struct kobj_type *ktype; 
        struct sysfs_dirent *sd; 
        struct kref kref; 
        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; 
    }; 

    其中,struct kref 内含一个 atomic_t 类型用于引用计数,parent 是单个指向父节点的指针,entry 用于父 kset 以链表头结构将 kobject 结构维护成双向链表;

  2. kset:它用来对同类型对象提供一个包装集合,在内核数据结构上它也是由内嵌一个 kobject 实现,因而它同时也是一个 kobject (面向对象 OOP 概念中的继承关系),具有 kobject 的全部功能;

    struct kset 
    { 
        struct list_head list; 
        spinlock_t list_lock; 
        struct kobject kobj; 
        struct kset_uevent_ops *uevent_ops; 
    }; 

    其中的 struct list_head list 用于将集合中的 kobject 按 struct list_head entry 维护成双向链表;

涉及到文件系统实现来说,sysfs 是一种基于 ramfs 实现的内存文件系统,与其它同样以 ramfs 实现的内存文件系统(configfs,debugfs,tmpfs,. . .)类似,sysfs 也是直接以 VFS 中的 struct inode 和 struct dentry 等 VFS 层次的结构体直接实现文件系统中的各种对象;同时在每个文件系统的私有数据(如 dentry->d_fsdata 等位置)上,使用了称为 struct sysfs_dirent 的结构用于表示 /sys 中的每一个目录项。

 struct sysfs_dirent 
{ 
    atomic_t s_count; 
    atomic_t s_active; 
    struct sysfs_dirent *s_parent; 
    struct sysfs_dirent *s_sibling; 
    const char *s_name; 
    union {
        struct sysfs_elem_dir s_dir; 
        struct sysfs_elem_symlink s_symlink; 
        struct sysfs_elem_attr s_attr; 
        struct sysfs_elem_bin_attr s_bin_attr; 
    }; 
    unsigned int s_flags; 
    ino_t s_ino; umode_t s_mode; 
    struct iattr *s_iattr; 
}; 

在上面的 kobject 对象中可以看到有向 sysfs_dirent 的指针,因此在 sysfs 中是用同一种 struct sysfs_dirent 来统一设备模型中的 kset/kobject/attr/attr_group。

具体在数据结构成员上,sysfs_dirent 上有一个 union 共用体包含四种不同的结构,分别是 目录、符号链接文件、属性文件、二进制属性文件;其中目录类型可以对应 kobject,在相应的 s_dir 中也有对 kobject 的指针,因此在内核数据结构,kobject 与 sysfs_dirent 是互相作用的;

有了这些概念,再来回头看 sysfs 目录层次图所表达的 /sys 目录结构就是非常清晰明了:

  • 在 /sys 根目录之下的都是 kset,它们组织了 /sys 的顶层目录视图;
  • 在部分 kset 下有二级或更深层次的 kset;
  • 每个 kset 目录下再包含着一个或多个 kobject,这表示一个集合所包含的 kobject 结构体;
  • 在 kobject 下有属性(attrs)文件和属性组(attr_group),属性组就是组织属性的一个目录,它们一期向用户层提供了表示和操作这个 kobject 的属性特征的接口;
  • 在 kobject 下还有一些符号链接文件,指向其它的 kobject,这些符号链接文件用于组织上面所说的 device,driver,bus_type,class,module 之间的关系;
  • 不同类型如设备类型的、设备驱动类型的 kobject 都有不同的属性,不同驱动程序支持的 sysfs 接口也有不同的属性文件;而相同类型的设备上有很多相同的属性文件;

 

  • 2
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Linux dm-verity是一个Linux内核模块,用于对块设备进行数据完整性校验。它通过使用哈希函数和预先计算的哈希树来实现校验,可以防止恶意修改或篡改块设备中的数据。 dm-verity的源代码位于Linux内核的"drivers/md/dm-verity.c"文件中。该文件包含了实现dm-verity功能所需的所有函数和数据结构。 dm-verity的主要流程如下: 1. 初始化:在加载模块时,dm-verity会注册自己为块设备处理器,并初始化一些数据结构和参数。 2. 设置参数:当用户通过命令行或者sysfs文件系统设置dm-verity参数时,dm-verity会解析这些参数并存储到对应的数据结构中。 3. 启动校验:当用户通过命令行或者sysfs文件系统启动dm-verity时,dm-verity会从块设备中读取哈希树,并使用哈希函数进行校验。 4. 处理请求:当用户对块设备发出读写请求时,dm-verity会拦截这些请求并进行数据校验。如果数据完整,则将请求转发到原始的块设备中;如果数据不完整,则返回错误 ### 回答2: Linux dm-verity是一种用于数据完整性校验的Linux内核模块。它通过计算数据块的校验值,确保数据在存储或传输过程中没有被篡改。下面对dm-verity的源码进行解析。 dm-verity的源码位于Linux内核的源码树中的drivers/md/dm-verity目录下。主要的源码文件包括verity_target.c、verity_blk.c和verity_metadata.c。 verity_target.c文件实现了dm-verity的核心逻辑。它定义了用于创建和管理verity设备的API接口函数。在这个文件中,dm-verity模块会将读取和写入请求传递给真实的存储设备,并使用verity_blk.c中的函数来计算和验证数据的校验值。它还负责处理校验失败的情况,例如将读取请求重定向到镜像设备。 verity_blk.c文件实现了校验块设备的功能。它使用了基本块设备的API函数,例如bio和request_queue,来处理请求。在这个文件中,校验模块会计算每个数据块的哈希值,将其与元数据中的预期值进行比较,并确定数据的完整性。这个过程是通过使用散列函数和加密算法来计算和比较校验值的。 verity_metadata.c文件定义了元数据的结构和操作函数。元数据是用于存储每个数据块的哈希值和其他校验相关信息的地方。它使用了dm自带的二叉树数据结构来组织数据块。 除了这些源码文件,dm-verity还涉及到一些其他的文件,如kconfig和Makefile等。这些文件用于配置和编译dm-verity模块,并将其与Linux内核进行集成。 总结起来,Linux dm-verity源码的解析涉及到多个文件,用于实现数据完整性校验的各个方面,包括读写请求的管理、校验值的计算和验证、元数据的存储等。通过分析这些源码文件,我们可以更深入地了解dm-verity模块的工作原理和实现细节。 ### 回答3: DM-Verity 是一种用于保护 Linux 文件系统完整性的技术。它基于数据验证和完整性保护,可防止篡改和数据损坏。DM-Verity的源代码解析可以帮助我们了解其工作原理和实现细节。 DM-Verity 的源代码主要分为两个部分:内核模块和用户空间工具。 内核模块部分是通过 Linux 内核的 Device Mapper(DM)框架实现的。它包含了核心的验证逻辑,在存储设备的块层进行数据验证。DM-Verity使用 Merkle 树的数据结构来计算和验证每个块的哈希值,这些哈希值存储在一个元数据区域中。当读取文件时,DM-Verity会对读取的数据块进行哈希计算,并与元数据中的哈希进行比对,以验证数据的完整性。如果哈希不匹配,则说明数据已被篡改。 用户空间工具部分提供了一套命令行工具来配置和管理 DM-Verity。主要的工具有 veritysetup 和 fsverityctl。veritysetup用于创建和配置DM-Verity设备,包括进行根文件系统的完整性保护。fsverityctl用于管理和查询已配置的 DM-Verity 设备,提供了一些实用的操作,比如启用和禁用 DM-Verity,查询校验结果等。 通过分析 DM-Verity 的源代码,我们可以了解到其实现的核心原理和具体实现细节。例如,可以了解到 DM-Verity 是如何在内核中通过设备映射层实现数据验证和完整性保护的。此外,还可以学习到DM-Verity如何使用 Merkle 树来计算和验证数据块的哈希值,以及如何通过元数据区域存储和管理这些哈希值。对于学习和理解 Linux 文件系统的完整性保护机制以及数据安全性的提升,DM-Verity的源代码解析是非常有价值的。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值