Linux 文件系统与设备文件系统分析2(基于Linux6.6)---sysfs 文件系统与Linux设备模型介绍
一、Linux 设备模型
1.1、设备模型概述
Linux 设备模型(Linux Device Model)是 Linux 内核管理硬件设备的框架,它提供了设备的抽象层,使得内核可以以统一的方式与硬件进行交互。设备模型的核心目的是简化设备驱动程序的编写,并为设备之间的交互提供统一的接口。
1. 设备模型的主要组成部分
Linux 设备模型的核心组成部分包括:
- 设备(Device):表示硬件设备的实例,通常与硬件驱动程序(driver)关联。
- 驱动程序(Driver):设备驱动程序负责对硬件设备进行初始化、管理和操作,驱动程序和设备通过设备模型关联。
- 类(Class):用于表示设备类型的集合。一个类是多个设备的逻辑分类,类用于定义某一类设备共享的属性和操作。
- 总线(Bus):总线表示设备与设备之间的连接。总线是硬件设备和设备驱动程序之间的桥梁,不同类型的设备可能通过不同的总线连接。
- 设备节点(Device Node):设备文件在
/dev
目录中的表示。用户空间通过设备节点与设备进行交互。
2. 设备(Device)
设备是 Linux 设备模型中的基本单位。设备通常表示为 struct device
结构体,它包含了设备的相关信息,比如设备的状态、设备的驱动程序、设备的父设备、设备的子设备等。
设备结构体(struct device
)示例:
struct device {
const char *init_name; // 设备的名字
struct device_driver *driver; // 设备驱动
struct device *parent; // 父设备
struct device *children; // 子设备列表
struct device *bus; // 总线设备
void *driver_data; // 驱动程序特定的数据
...
};
3. 设备驱动程序(Driver)
设备驱动程序是控制硬件设备的代码,负责设备的初始化、数据传输、硬件状态管理等。每个设备都有一个驱动程序,该驱动程序负责实现对设备的操作。
设备驱动程序与设备通过设备模型的结构体 struct device_driver
关联。设备驱动程序通常会定义一些操作函数(如 probe
、remove
、suspend
、resume
等)来管理设备。
设备驱动程序结构体(struct device_driver
)示例:
struct device_driver {
const char *name; // 驱动程序的名字
int (*probe)(struct device *dev); // 设备匹配时的初始化函数
int (*remove)(struct device *dev); // 设备移除时的清理函数
...
};
4. 类(Class)
类用于表示一组功能相似的设备,它们共享相同的属性和操作接口。Linux 内核中有许多预定义的设备类,如字符设备、块设备等。类不仅仅是硬件设备的分类,它还提供了一些通用的接口和方法,用于操作这类设备。
类是通过 struct class
结构体定义的,struct class
结构体包含了类相关的信息和操作函数。
类结构体(struct class
)示例:
struct class {
const char *name; // 类的名称
struct device *dev_kobj; // 类的设备对象
struct class_dev_iter *class_dev_iter; // 设备迭代器
...
};
5. 总线(Bus)
总线是连接设备和驱动程序之间的桥梁。一个总线可以包含多个设备,设备通过总线与内核中的设备模型交互。总线类型有很多种,例如 PCI 总线、USB 总线等。
在 Linux 内核中,总线是由 struct bus_type
结构体表示的,包含了总线的相关信息,以及与总线交互的操作函数。
总线结构体(struct bus_type
)示例:
struct bus_type {
const char *name; // 总线的名称
struct device *dev; // 总线设备
int (*match)(struct device *dev, struct device_driver *drv); // 匹配函数
...
};
6. 设备模型的工作流程
设备模型的工作流程主要分为以下几个步骤:
-
设备和驱动程序的注册:
- 当内核启动时,硬件设备信息会被探测和注册。驱动程序会根据设备信息进行匹配,找到合适的驱动并将其绑定到设备上。
-
设备匹配:
- 驱动程序会根据设备的属性与设备模型中的设备进行匹配。如果匹配成功,驱动程序会被加载并注册到设备上。
-
设备的初始化和管理:
- 驱动程序通过调用设备模型提供的接口来对设备进行初始化,配置设备,提供设备操作方法等。
-
设备的注销和清理:
- 当设备不再需要使用时,驱动程序会释放与设备相关的资源并将设备从设备模型中移除。
7. 设备模型中的 sysfs
接口
sysfs
是一个虚拟文件系统,用于提供内核与用户空间之间的交互接口。通过 sysfs
,用户可以访问和修改设备的属性。每个设备、类、总线等都在 /sys
目录下有对应的条目,用户可以通过该接口与设备进行交互。
例如,设备的属性信息通常会通过 sysfs
提供,通过读取和写入文件,可以改变设备的状态或获取设备的状态。
1.2、设备模型结构
如表,Linux设备模型包含以下四个基本结构:
类型 |
所包含的内容 |
内核数据结构 |
对应/sys项 |
设备(Devices) |
设备是此模型中最基本的类型,以设备本身的连接按层次组织 |
struct device |
/sys/devices/*/*/.../ |
驱动 (Drivers) |
在一个系统中安装多个相同设备,只需要一份驱动程序的支持 |
struct device_driver |
/sys/bus/pci/drivers/*/ |
总线 (Bus) |
在整个总线级别对此总线上连接的所有设备进行管理 |
struct bus_type |
/sys/bus/*/ |
类别(Classes) |
这是按照功能进行分类组织的设备层次树;如 USB 接口和 PS/2 接口的鼠标都是输入设备,都会出现在/sys/class/input/下 |
struct class |
/sys/class/*/ |
device、driver、bus、class是组成设备模型的基本数据结构。kobject是构成这些基本结构的核心,kset又是相同类型结构kobject的集合。kobject和kset共同组成了sysfs的底层数据体系。本节采用从最小数据结构到最终组成一个大的模型的思路来介绍。当然,阅读时也可先从Device、Driver、Bus、Class的介绍开始,先总体了解设备模型的构成,然后再回到kobject和kset,弄清它们是如何将Device、Driver、Bus、Class穿插链接在一起的,以及如何将这些映像成文件并最终形成一个sysfs文件系统。
二、Sysfs文件系统
sysfs是一个基于内存的文件系统,它的作用是将内核信息以文件的方式提供给用户程序使用。
sysfs可以看成与proc,devfs和devpty同类别的文件系统,该文件系统是虚拟的文件系统,可以更方便对系统设备进行管理。它可以产生一个包含所有系统硬件层次视图,与提供进程和状态信息的proc文件系统十分类似。
sysfs把连接在系统上的设备和总线组织成为一个分级的文件,它们可以由用户空间存取,向用户空间导出内核的数据结构以及它们的属性。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系,其顶级目录包括block,bus,drivers,class,power和firmware等.
Sysfs 是 Linux 内核中的一个虚拟文件系统,用于向用户空间提供内核对象(如设备、驱动程序、内核模块等)和它们的属性信息。它通过一个文件系统接口,使得用户空间程序能够访问和操作内核对象,并与内核进行交互。Sysfs 通过挂载在 /sys
目录下来提供这些信息和控制接口。
2.1、Sysfs 文件系统的作用和特点
-
内核信息暴露: Sysfs 使内核的内部信息得以通过文件系统暴露出来,用户可以读取和修改这些信息。例如,硬件设备的状态、驱动程序的配置信息、内核参数等都可以通过 Sysfs 访问。
-
设备和驱动管理: Sysfs 文件系统包含了有关系统设备的信息,包括设备树(device tree)、设备驱动、设备属性、总线等。这使得用户能够通过读取、写入文件来查询或修改硬件设备的状态,或加载、卸载驱动程序。
-
动态性: Sysfs 文件系统是动态的。它是内核通过创建和删除文件节点来动态管理内核对象。随着设备的插入和移除,Sysfs 中的目录和文件会相应地变化。
-
与用户空间交互: Sysfs 提供了一种内核与用户空间交互的方式。例如,用户可以通过写入文件来控制设备的行为,或者通过读取文件获取设备的状态。这种机制提供了比传统的 ioctl 或 /proc 文件系统更简单、更结构化的接口。
sysfs提供一种机制,使得可以显式的描述内核对象、对象属性及对象间关系。sysfs有两组接口,一组针对内核,用于将设备映射到文件系统中,另一组针对用户程序,用于读取或操作这些设备。下表描述了内核中的sysfs要素及其在用户空间的表现:
sysfs在内核中的组成要素 |
在用户空间的显示 |
内核对象(kobject) |
目录 |
对象属性(attribute) |
文件 |
对象关系(relationship) |
链接(Symbolic Link) |
sysfs目录结构:
/sys 下的子目录 |
所包含的内容 |
/sys/devices |
这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构; |
/sys/dev |
这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件; |
/sys/bus |
这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接,它也是构成 Linux 统一设备模型的一部分; |
/sys/class |
这是按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input 之下,而不论它们是以何种总线连接到系统。它也是构成 Linux 统一设备模型的一部分; |
/sys/kernel |
这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中; |
/sys/module |
这里有系统中所有模块的信息,不论这些模块是以内联(inlined)方式编译到内核映像文件(vmlinuz)中还是编译为外部模块(ko文件),都可能会出现在/sys/module 中:
|
/sys/power |
这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。 |
三、深入理解 sysfs 文件系统
sysfs是一个特殊文件系统,并没有一个实际存放文件的介质。
3.1、kobject结构
sysfs的信息来源是kobject层次结构,读一个sysfs文件,就是动态的从kobject结构提取信息,生成文件。
sysfs文件系统与kobject结构紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。
kobject与sysfs的关系
每当在内核中注册一个kobject对象时,内核会自动在sysfs中创建一个相应的目录。该目录中的每个文件通常对应于kobject对象的一个属性。用户可以通过这些文件来读取或修改kobject对象的属性。
具体来说,sysfs 通过以下步骤与 kobject 进行交互:
- kobject注册:当内核模块或设备驱动注册一个kobject时,会调用
kobject_init()
和kobject_add()
函数。这时,内核会为这个kobject对象分配一个唯一的目录,通常位于/sys
目录下。 - 创建目录和属性:kobject可以通过
sysfs_create_group()
将一组属性(通过kobject_attribute
)添加到该目录中。每个属性通常对应于一个文件,用户可以通过读取或写入这些文件来访问或修改该属性。 - 属性文件操作:当用户空间的应用程序访问这些属性文件时,内核会调用相应的回调函数来处理读取或写入请求。
- kobject注销:当kobject不再需要时,可以调用
kobject_del()
函数来注销该对象,并在sysfs中删除对应的目录和属性文件。
3.2、sysfs 如何读写kobject 结构
sysfs就是利用VFS的接口去读写kobject的层次结构,建立起来的文件系统。 kobject的层次结构的注册与注销XX_register()形成的。文件系统是个很模糊广泛的概念, linux把所有的资源都看成是文件,让用户通过一个统一的文件系统操作界面,也就是同一组系统调用,对属于不同文件系统的文件进行操作。这样,就可以对用户程序隐藏各种不同文件系统的实现细节,为用户程序提供了一个统一的,抽象的,虚拟的文件系统界面,这就是所谓"VFS(Virtual Filesystem Switch)"。这个抽象出来的接口就是一组函数操作。
实现一种文件系统就是要实现VFS所定义的一系列接口,file_operations, dentry_operations, inode_operations等,供上层调用。
file_operations是描述对每个具体文件的操作方法(如:读,写);
dentry_operations结构体指明了VFS所有目录的操作方法;
inode_operations提供所有结点的操作方法。
举个例子,写C程序,open(“hello.c”, O_RDONLY),它通过系统调用的流程是这样的
open() -> 系统调用-> sys_open() -> filp_open()-> dentry_open() -> file_operations->open()
不同的文件系统,调用不同的file_operations->open(),在sysfs下就是sysfs_open_file()。我们使用不同的文件系统,就是将它们各自的文件信息都抽象到dentry和inode中去。这样对于高层来说,我们就可以不关心底层的实现,我们使用的都是一系列标准的函数调用。这就是VFS的精髓,实际上就是面向对象。
注意sysfs是典型的特殊文件。它存储的信息都是由系统动态的生成的,它动态的包含了整个机器的硬件资源情况。从sysfs读写就相当于向 kobject层次结构提取数据。
sysfs
是 Linux 内核提供的一种机制,用于通过文件系统将内核中的对象(kobject
)暴露给用户空间,从而提供一种简单的接口用于查询和控制内核对象。kobject
结构是 Linux 内核中表示各种对象的基本数据结构,通过 sysfs
,这些对象可以通过文件接口(通常位于 /sys
目录下)进行访问。
1. kobject
结构介绍
kobject
是内核对象的基本数据结构,代表一个内核中可管理的对象。一个 kobject
可以有多个属性(即与 kobject
关联的文件),这些属性可以是只读的、可写的,或者具有其他交互功能。sysfs
提供了对这些属性的访问。
kobject
结构体:
struct kobject {
struct kset *kset; // 父级对象的 kset
struct list_head entry; // 用于链表管理
const char *name; // 对象的名字
struct sysfs_ops *ops; // sysfs 操作函数
struct attribute *default_attrs; // 默认属性(sysfs文件)
// 其他成员
};
name
:表示对象的名称kset
:表示父对象的集合ops
:用于与 sysfs 交互的操作函数default_attrs
:定义与该对象相关联的属性
2. 如何通过 sysfs
读写 kobject
通过 sysfs
操作 kobject
结构,通常有两个主要的功能:读取和写入属性。
读取 kobject
属性
在 sysfs
中,读取属性会调用 kobj_attribute
结构的 show
函数。该函数负责将内核中某个变量或状态返回给用户空间。
例子:创建一个简单的内核模块,并通过 sysfs
读取 kobject
属性
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
static struct kobject *example_kobj;
static int example_value = 42;
static ssize_t example_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", example_value);
}
static struct kobj_attribute example_attr = __ATTR(example, 0444, example_show, NULL);
static int __init sysfs_example_init(void)
{
int error;
// 创建 kobject 对象
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
// 创建 sysfs 属性文件
error = sysfs_create_file(example_kobj, &example_attr.attr);
if (error)
kobject_put(example_kobj);
return error;
}
static void __exit sysfs_example_exit(void)
{
// 删除 sysfs 属性
kobject_put(example_kobj);
}
module_init(sysfs_example_init);
module_exit(sysfs_example_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple sysfs example");
解释:
example_show
:这个函数用于响应用户对example
属性文件的读取请求,它将example_value
的值(42)返回给用户空间。example_attr
:这是一个kobj_attribute
结构体,定义了属性的名称example
,以及读取和写入操作(这里只实现了读取操作)。kobject_create_and_add
:创建一个kobject
,并将其添加到内核的kernel_kobj
下(也就是/sys/kernel/example
)。sysfs_create_file
:将属性文件example
创建并关联到example_kobj
对象。
写入 kobject
属性
写入操作会调用 kobj_attribute
结构体的 store
函数。该函数将用户提供的数据解析并存储到内核的相应变量中。
例子:创建一个简单的内核模块,并通过 sysfs
写入 kobject
属性
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
static struct kobject *example_kobj;
static int example_value = 42;
static ssize_t example_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", example_value);
}
static ssize_t example_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
int ret;
ret = sscanf(buf, "%d", &example_value);
if (ret != 1)
return -EINVAL;
return count;
}
static struct kobj_attribute example_attr = __ATTR(example, 0664, example_show, example_store);
static int __init sysfs_example_init(void)
{
int error;
// 创建 kobject 对象
example_kobj = kobject_create_and_add("example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
// 创建 sysfs 属性文件
error = sysfs_create_file(example_kobj, &example_attr.attr);
if (error)
kobject_put(example_kobj);
return error;
}
static void __exit sysfs_example_exit(void)
{
// 删除 sysfs 属性
kobject_put(example_kobj);
}
module_init(sysfs_example_init);
module_exit(sysfs_example_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple sysfs example with write operation");
解释:
example_show
:这个函数用于读取example_value
。example_store
:此函数负责处理用户向example
属性写入的数据。它将输入的数据解析为一个整数,并更新example_value
的值。- 属性文件
example
被设置为0664
权限,允许用户写入数据。
3. 通过 sysfs
交互的应用实例
-
动态调整内核参数:你可以通过
sysfs
实现一些内核参数的动态修改,比如修改调度器的参数、网络配置或者设备驱动中的配置选项。例如,通过/sys/class/net/eth0/speed
文件读取和写入网络接口的速率。 -
设备控制:可以通过
sysfs
控制硬件设备。例如,/sys/class/block/sda/queue/rotational
可以用于查看或修改硬盘是否为旋转硬盘(HDD)或固态硬盘(SSD)。 -
内核调试:许多调试信息通过
sysfs
提供访问。例如,内核中可以通过sysfs
显示硬件信息、设备状态,或查询内核模块的参数。
四、实例分析
4.1、sysfs文件open流程
1、sysfs
文件的打开流程
-
用户空间请求打开文件
用户空间进程通过系统调用
open()
请求打开一个sysfs
文件,例如:
$ open("/sys/kernel/my_device/my_value", O_RDONLY);
该请求会被传递到内核中,内核会处理文件系统操作,并找到对应的 sysfs
文件。
-
VFS(Virtual File System)调用
在内核中,Linux 使用虚拟文件系统(VFS)作为所有文件系统的抽象层。VFS 会根据请求的路径(如
/sys/kernel/my_device/my_value
)查找该路径对应的文件对象。对于sysfs
文件系统,VFS 会将请求转发给sysfs
文件系统的实现。 -
sysfs 文件的 inode 对应的操作
在 Linux 文件系统中,每个文件都有一个对应的
inode
(索引节点)。sysfs
文件也是如此,每个sysfs
文件都有一个与之关联的inode
。inode
存储了文件的元数据以及文件操作的指针。sysfs
文件的inode
结构中会设置与该文件相关的操作函数指针。sysfs
主要关注以下操作:当打开一个
sysfs
文件时,VFS
会调用sysfs
文件的open
操作。如果文件是一个普通的属性文件(通过kobject
暴露的),则会调用kobject
相关的回调函数。open
: 用来处理文件的打开操作。read
: 处理读取文件操作。write
: 处理写入文件操作。release
: 处理文件关闭操作。
-
sysfs 打开操作
在
sysfs
文件系统中,open
操作会调用与该文件相关的sysfs
结构的open
回调函数,通常是一个类似于sysfs_open()
的函数。对于
sysfs
文件,内核并不会真正进行文件内容的加载,而是通过kobject
的show
和store
操作来处理数据。这些回调函数实际上是用于读取或写入内核对象的属性。
static int sysfs_open(struct inode *inode, struct file *file)
{
struct kobject *kobj = sysfs_get_kobject(inode);
if (kobj == NULL)
return -EINVAL;
return 0;
}
这个函数会首先获取与该文件关联的 kobject
,然后返回成功,表示文件被成功打开。sysfs_open
并不会实际执行数据的读取或写入,而只是为后续的操作做好准备。
-
权限检查
在打开文件时,内核会检查用户是否具有访问该文件的权限。例如,如果文件是只读的(
0644
),并且用户尝试进行写入操作,内核会拒绝该请求。 -
关联文件操作回调
sysfs
文件系统的每个文件都对应一组操作回调函数,例如show
和store
。这些回调函数实际上负责数据的读写工作。例如,文件
/sys/kernel/my_device/my_value
的读取操作会调用my_show()
函数,而写入操作则会调用my_store()
函数。- 读取:当文件被打开并准备读取时,
sysfs
会通过show
回调函数读取文件内容,通常是从内核对象中提取数据并返回给用户空间。 - 写入:当文件被打开并准备写入时,
sysfs
会调用store
回调函数,将用户空间的数据写入内核对象中。
- 读取:当文件被打开并准备读取时,
-
返回文件描述符
如果
open()
请求成功,内核会返回一个文件描述符,用户空间可以通过该文件描述符对文件进行后续的操作(如读取、写入等)。此时,文件已经在进程的文件描述符表中注册,可以被后续的系统调用(如read()
、write()
)使用。 -
用户空间操作
一旦文件被成功打开,用户空间就可以通过标准的文件操作接口(如
read()
、write()
、ioctl()
等)与内核交互。例如:
$ cat /sys/kernel/my_device/my_value
上述命令会触发 sysfs
的 read
操作,进而调用与文件关联的 show
函数来获取数据。
-
文件关闭
文件在使用完后会被关闭。此时,内核会调用与文件关联的
release
函数。对于sysfs
文件,release
操作通常是为了释放相关资源,例如减少引用计数。
static int sysfs_release(struct inode *inode, struct file *file)
{
// 释放资源,减少引用计数
return 0;
}
2、总结
sysfs
文件的 open
流程可以概括为以下几个步骤:
- 用户空间通过
open()
系统调用请求打开文件。 - VFS 将请求转发到
sysfs
文件系统。 sysfs
根据文件路径找到对应的inode
,并查找文件的open
操作。sysfs_open()
函数被调用,关联相应的kobject
。- 权限检查确保当前操作合法。
- 返回文件描述符,文件被成功打开。
- 用户空间通过
read()
、write()
等操作与内核交互。 - 文件关闭时,调用
release
函数释放资源。
4.2 sysfs文件read/write流程
sysfs
文件的读取(read
)和写入(write
)操作有特定的流程,涉及到内核如何处理这些操作以与内核对象进行交互。
1. sysfs
文件结构
sysfs
中的每个文件通常与内核对象(kobject
)的一个属性相关联。这些属性通常是通过 sysfs
文件暴露给用户空间的,用户可以读取或者写入这些文件,从而获取或修改内核对象的状态。
在 sysfs
中,每个文件都与一个属性对象(kobj_attribute
)绑定。kobj_attribute
定义了与该属性相关联的操作:读取操作 (show
) 和写入操作 (store
)。
2. sysfs
文件读取(read
)流程
当用户空间进程对某个 sysfs
文件执行 read()
操作时,Linux 内核会按照以下流程处理:
(1)用户调用 read()
系统调用
用户空间进程通过调用 read()
系统调用请求读取某个 sysfs
文件的数据。read()
调用会传入文件描述符、缓冲区和读取的字节数。
(2)查找文件对应的 kobj_attribute
内核会根据文件路径找到对应的 kobj_attribute
结构体。这个结构体包含了与该文件对应的内核属性及其操作函数:show
和 store
。
(3)调用 show()
函数
内核通过 kobj_attribute
中的 show()
函数来处理读取操作。show()
函数负责将内核对象的状态或者数据复制到用户空间的缓冲区。通常,show()
函数会从内核对象中获取某个值(例如设备状态、属性等),并将其格式化后返回给用户空间。
static ssize_t my_device_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
// 返回设备的某些状态或数据
return sprintf(buf, "%d\n", my_device_status);
}
(4)返回数据给用户空间
show()
函数将数据写入传入的 buf
缓冲区。然后,内核将缓冲区中的数据复制到用户空间,完成读取操作。此时,read()
系统调用返回读取的字节数。
3. sysfs
文件写入(write
)流程
当用户空间进程对某个 sysfs
文件执行 write()
操作时,Linux 内核会按照以下流程处理:
(1)用户调用 write()
系统调用
用户空间进程通过调用 write()
系统调用写入数据到某个 sysfs
文件。write()
调用会传入文件描述符、缓冲区和写入的字节数。
(2)查找文件对应的 kobj_attribute
内核会根据文件路径找到对应的 kobj_attribute
结构体,类似于 read()
流程。
(3)调用 store()
函数
内核通过 kobj_attribute
中的 store()
函数来处理写入操作。store()
函数会将用户空间提供的数据解析并存储到内核对象中。通常,store()
函数会根据用户提供的数据更新内核对象的状态。
static ssize_t my_device_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
// 更新设备的状态
sscanf(buf, "%d", &my_device_status);
return count;
}
(4)更新内核对象的状态
store()
函数会对传入的数据进行处理(例如转换为整数、浮点数或其他格式),并将其存储到内核对象的相应字段中。这通常涉及到修改内核中某个变量或设置某个硬件设备的配置。
(5)返回写入字节数
store()
函数返回实际写入的字节数,内核会将这个值作为 write()
系统调用的返回值,表示成功写入的字节数。
4. sysfs
中的 kobj_attribute
结构
kobj_attribute
是一个结构体,表示 sysfs
文件与内核对象属性之间的映射。它包含两个函数指针,show
和 store
,分别用于处理读取和写入操作:
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr, char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count);
};
attr
:继承自attribute
,包含与文件相关的元数据,例如文件的权限、名称等。show
:处理read()
操作的函数指针。store
:处理write()
操作的函数指针。
5. 总结
- 读取流程:用户通过
read()
系统调用读取sysfs
文件时,内核查找对应的kobj_attribute
,调用show()
函数获取数据,并将数据返回给用户空间。 - 写入流程:用户通过
write()
系统调用向sysfs
文件写入数据时,内核查找对应的kobj_attribute
,调用store()
函数处理数据,并更新内核对象的状态。