1. Sysfs

您所在的位置:网站首页 Linux设备节点在用户态创建 1. Sysfs

1. Sysfs

2024-07-16 00:14| 来源: 网络整理| 查看: 265

1. Sysfs¶

前面章节驱动学习中,我们测试驱动时经常使用/sys目录下文件,我们本章就简单介绍下Sysfs文件系统。

Sysfs是一个内存文件系统(还有configfs,debugfs等),提供了一种用于向用户空间导出内核对象的方法, 展示了kobject对象层次结构的视图,用户可以查看或者设置一些内核参数。kobject层次结构的展现在系统硬件相关的设备、驱动、总线等层级关系,为linux统一设备模型作为管理之用。

1.1. Sysfs的目录结构¶

相对于我们熟悉的磁盘文件系统,Sysfs文件系统是一种虚拟文件系统,也就是文件系统中文件不对应硬盘上任何文件,存在于内存中。 该文件系统初始化默认挂载到/sys下,可以使用下面命令挂载(也可以挂载到其他地方):

mount -t sysfs sysfs /sys

一般情况下/sys的目录结构:

cat@lubancat:/$ ls -l /sys total 0 drwxr-xr-x 2 root root 0 Oct 19 09:49 block drwxr-xr-x 33 root root 0 Oct 19 09:49 bus drwxr-xr-x 69 root root 0 Oct 19 09:49 class drwxr-xr-x 4 root root 0 Oct 19 09:49 dev drwxr-xr-x 11 root root 0 Oct 19 09:49 devices drwxr-xr-x 3 root root 0 Oct 19 09:49 firmware drwxr-xr-x 8 root root 0 Oct 19 09:49 fs drwxr-xr-x 12 root root 0 Oct 19 09:49 kernel drwxr-xr-x 151 root root 0 Oct 19 09:49 module drwxr-xr-x 3 root root 0 Oct 19 09:49 power

这些目录是在子系统注册kobject核心的系统启动时刻产生的, 当它们被初始化以后, 它们开始搜寻在各自的目录内注册了的对象。 一个kobject对应一个目录,包含的对象属性对应一个文件,文件只支持 目录、 普通文件 (文本或二进制文件)和 符号链接文件 三种类型。

1.1.1. block目录¶

这里是系统中当前所有的块设备所在,按照功能来说放置在/sys/class之下会更合适,但只是由于历史遗留因素而一直存在于/sys/block。 块设备现在是已经移到/sys/class/block, 旧的接口/sys/block为了向后兼容保留存在,现在该目录下的都是链接文件:

cat@lubancat:/sys/block$ ls -l total 0 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop0 -> ../devices/virtual/block/loop0 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop1 -> ../devices/virtual/block/loop1 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop2 -> ../devices/virtual/block/loop2 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop3 -> ../devices/virtual/block/loop3 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop4 -> ../devices/virtual/block/loop4 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop5 -> ../devices/virtual/block/loop5 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop6 -> ../devices/virtual/block/loop6 lrwxrwxrwx 1 root root 0 Oct 19 10:12 loop7 -> ../devices/virtual/block/loop7 lrwxrwxrwx 1 root root 0 Oct 19 10:12 mmcblk0 -> ../devices/platform/fe310000.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0 lrwxrwxrwx 1 root root 0 Oct 19 10:12 mmcblk0boot0 -> ../devices/platform/fe310000.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot0 lrwxrwxrwx 1 root root 0 Oct 19 10:12 mmcblk0boot1 -> ../devices/platform/fe310000.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0/mmcblk0boot1 lrwxrwxrwx 1 root root 0 Oct 19 10:12 mmcblk1 -> ../devices/platform/fe2b0000.dwmmc/mmc_host/mmc1/mmc1:aaaa/block/mmcblk1 lrwxrwxrwx 1 root root 0 Oct 19 10:12 ram0 -> ../devices/virtual/block/ram0 lrwxrwxrwx 1 root root 0 Oct 19 10:12 zram0 -> ../devices/virtual/block/zram0 1.1.2. bus、devices、class目录¶

这些目录都是与Linux统一设备模型有重要关系,是构成Linux统一设备模型的一部分。

bus目录下的每个子目录都是注册好了的总线类型。这里是设备按照总线类型分层放置的目录结构, 每个子目录(总线类型)下包含两个子目录——devices和drivers文件夹;其中devices下是该总线类型下的所有设备, 而这些设备都是符号链接,它们分别指向真正的设备(/sys/devices/下);如下图bus下的usb总线中的device则是Devices目 录下/pci()/dev 0:10/usb2的符号链接。而drivers下是所有注册在这个总线上的驱动,每个driver子目录下 是一些可以观察和修改的driver参数。

devices目录下是全局设备结构体系,包含所有被发现的注册在各种总线上的各种物理设备。一般来说, 所有的物理设备都按其在总线上的拓扑结构来显示。/sys/devices是内核对系统中所有设备的分层次表达模型, 也是/sys文件系统管理设备的最重要的目录结构。

class目录下则是包含所有注册在kernel里面的设备类型,这是按照设备功能分类的设备模型, 我们知道每种设备都具有自己特定的功能,比如:鼠标的功能是作为人机交互的输入,按照设备功能分类无论它 挂载在哪条总线上都是归类到/sys/class/input下。

下面图片可以直观的看出之间的联系:

1.1.3. firmware目录¶

该目录包含具有固件对象和属性的子目录,关于内核的固件加载和firmware驱动,有兴趣可以自己去了解下。 该目录下:

cat@lubancat:/sys/firmware$ ls -l total 0 drwxr-xr-x 3 root root 0 Oct 19 11:34 devicetree -r-------- 1 root root 163840 Oct 19 11:34 fdt

其中的devicetree文件夹,描述加载设备树信息,根节点对应base目录,每一个设备树节点对应一个目录,每个属性对应一个文件。 fdt文件是原始dtb文件,是uboot传给内核的设备树文件,可以使用hexdump -C查看。

1.1.4. fs目录¶

这里按照设计是用于描述系统中所有文件系统,包括文件系统本身和按文件系统分类存放的已挂载点,描述已注册的文件系统视图, 但目前只有 fuse,ext4 等少数文件系统支持 sysfs 接口,一些传统的虚拟文件系统(VFS)层次控制参数仍然在sysctl(/proc/sys/fs) 接口中。 目录结构如下:

cat@lubancat:/sys/fs$ ls -l total 0 drwx-----T 2 root root 0 Oct 19 14:41 bpf drwxr-xr-x 10 root root 280 Oct 19 14:41 cgroup drwxr-xr-x 5 root root 0 Oct 19 16:58 ext4 drwxr-xr-x 3 root root 0 Oct 19 14:41 fuse drwxr-x--- 2 root root 0 Oct 19 14:41 pstore drwxr-xr-x 3 root root 0 Oct 19 16:58 xfs

有兴趣可以去了解下cgroup,bpf文件。

1.1.5. kernel目录¶

该目录是内核所有可调整参数的位置,有些内核可调整参数仍然位于sysctl(/proc/sys/kernel)接口中。

1.1.6. module目录¶

该目录有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(.ko文件), 都可能会出现在/sys/module目录下。

编译为外部模块(.ko文件)在加载后,会/sys/module/出现对应的模块文件夹,在这个文件夹下会出现一些属性文件和属性目录, 表示此外部模块的一些信息,如版本号、加载状态、所提供的驱动程序等。

编译进内核的模块则只在当它有非0属性的模块参数时会出现对应的/sys/module/, 这些模块的可用参数会出现在/sys/modules/parameters/中, 如:/sys/module/printk/parameters/time 这个可读写参数控制着内联模块printk在打印内核消息时是否加上时间前缀。

cat@lubancat:/sys/module/printk/parameters$ ls -al total 0 drwxr-xr-x 2 root root 0 Oct 19 14:34 . drwxr-xr-x 3 root root 0 Oct 18 15:12 .. -rw-r--r-- 1 root root 4096 Oct 19 14:36 always_kmsg_dump -rw-r--r-- 1 root root 4096 Oct 19 14:36 console_suspend -rw-r--r-- 1 root root 4096 Oct 19 14:36 ignore_loglevel -rw-r--r-- 1 root root 4096 Oct 19 14:36 time cat@lubancat:/sys/module/printk/parameters$ cat time Y cat@lubancat:/sys/module/printk/parameters$ echo 0 | sudo tee time 0 cat@lubancat:/sys/module/printk/parameters$ cat time N cat@lubancat:/sys/module/printk/parameters$

所有内联模块的参数也可以由 “.=” 的形式写在内核启动参数上,如启动内核时加上参数 “printk.time=1”与向 “/sys/module/printk/parameters/time” 写入1的效果相同, 没有非0属性参数的内联模块不会出现于此。

1.1.7. power目录¶

该目录下是系统中电源选项,包含电源管理子系统提供的统一接口文件。 一些属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令进行关机、重启等操作。

1.2. Sysfs使用¶

sysfs模型的核心是kobject。把kobject对象与目录项紧密连接在一起,将kobject映射到目录上。 由struct Kobject表示,并在中定义。struct kobject表示一个内核对象,可能是一个设备,例如在sysfs文件系统中显示为目录的内容。

这个里简单介绍下设备模型核心数据结构kobject:

kobject(include/linux/kobject.h)¶ 1 2 3 4 5 6 7 8 9 10struct 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; /*....*/ }

name:kobj的名字,在/sys下的目录名使用这个创建

parent:父kobj节点,在/sys下创建目录会在该父目录下

kset: 对kobj进行分类,具有相同属性的kobject在一个集合等

ktype: kobj相关属性,对象的操作方法等,该结构体详细参考内核include/linux/kobject.h

kref:引用计数,为零时调用析构函数

sd:使用kobject_create_and_add函数时,会关联到sysfs_dirent,实现映射。

1.2.1. 创建一个目录¶ struct kobject * kobject_create_and_add ( const char * name, struct kobject * parent);

name:创建kobj的名字

parent:指定父kobject,实际就是在那个目录下创建一个目录。比如为kernel_kobj,将在/sys/kernel目录下创建目录,如果为NULL,将在/sys下创建。

1.2.2. 创建一个文件¶ int sysfs_create_file ( struct kobject * kobj, const struct attribute * attr);

kobj:我们创建的kobject

attr:属性描述

更多的函数可以参考内核源码include/linux/sysfs.h文件。

1.3. 简单实验¶

根据前面小节的函数接口,我们简单编写个驱动程序,在/sys目录下创建一个sysfs_test目录,并在该目录下创建一个文件。

示例配套代码目录:linux_driver/Sysfs

1.3.1. 程序源码¶ linux_driver/Sysfs/sys_test.c¶ 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67#include #include #include #include #include #include #include volatile int test_value = 0; struct kobject *kobj_test; /*该函数被调用在sysfs文件被读时*/ static ssize_t sysfs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { pr_info("读sysfs!\n"); return sprintf(buf, "test_value = %d\n", test_value); } /* 该函数被调用在sysfs文件被写时*/ static ssize_t sysfs_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count) { pr_info("写sysfs!\n"); sscanf(buf,"%d",&test_value); return count; } /*使用__ATTR宏初始化sysfs_test_attr结构体,该宏定义在include/linux/sysfs.h*/ struct kobj_attribute sysfs_test_attr = __ATTR(test_value, 0664, sysfs_show, sysfs_store); /*模块初始化函数*/ static int __init sysfs_test_driver_init(void) { /*创建一个目录在/sys下 */ kobj_test = kobject_create_and_add("sysfs_test",NULL); /*在sysfs_test目录下创建一个文件*/ if(sysfs_create_file(kobj_test,&sysfs_test_attr.attr)){ pr_err("创建sysfs文件失败.....\n"); goto error_sysfs; } pr_info("驱动模块初始化完成!\n"); return 0; error_sysfs: kobject_put(kobj_test); sysfs_remove_file(kernel_kobj, &sysfs_test_attr.attr); return -1; } /*模块退出函数*/ static void __exit sysfs_test_driver_exit(void) { kobject_put(kobj_test); sysfs_remove_file(kernel_kobj, &sysfs_test_attr.attr); pr_info("设备驱动模块移除!\n"); } module_init(sysfs_test_driver_init); module_exit(sysfs_test_driver_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Embedfire"); MODULE_DESCRIPTION("一个简单的使用sysfs的驱动程序");

在实际上层驱动模型中(cdev、bus、device等),都内嵌了object结构体,直接使用,内核在对应目录下创建文件。

1.3.2. 测试¶

使用交叉编译器编译,生成sys_test.ko模块,传输到板卡,,然后使用命令: sudo insmod sys_test.ko加载内核模块。 之后读写在/sys/sysfs_test下创建的文件:

1.4. 总结¶

设备驱动程序中需要与用户层的接口,一般有多种方法。可以注册虚拟的字符设备文件,用户层可以编程通过read/write/ioctl交互; 注册proc接口支持,这个是操作系统本身和应用程序之间的通信提供了一个安全的接口,主要显示进程信息和内核各子系统的信息等,现在设备驱动一般不放到这; 注册sysfs属性,直接添加目录和属性文件,用户层可以通过脚本或者命令直接操作。

关于Sysfs目录下的一些文件的详细说明,可以参考内核源码Documentation/ABI/下的文档,这些文档描述了/sys下文件的属性以及使用。

1.5. 参考¶

内核文档: Documentation/ABI/*

Documentation/filesystems/sysfs.txt

Documentation/kobject.txt

samples/kobject/kobject-example.c



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3