没志青年
发布于 2025-06-14 / 13 阅读
0

10 Linux sysfs 文件系统

好的,我们来深入讲解 sysfs​ 文件系统。它与 proc文件系统密切相关,但在设计目的和使用方式上有显著区别。

简单来说,sysfs是一个基于内存的虚拟文件系统,它在用户空间和内核空间之间提供了一个结构化的、清晰的接口,主要用于导出内核设备模型的各个部分——即系统中的总线、设备、驱动以及它们的相互关系。

如果说 proc是内核的“杂乱的控制面板”,那么 sysfs就是一份“井然有序的设备清单和关系图”。

导出内核设备模型,展示总线、设备、驱动的层次结构和关系。

1、导出内核设备模型

sysfs将设备模型以一种人类可读的层次化目录结构呈现出来。

sysfs清晰地展示了“哪个驱动绑定到了哪个设备”、“设备挂在了哪条总线上”这样的关系。

2、提供统一的设备属性接口

3、与 udev协同工作,实现动态设备管理

sysfs的布局与关键目录 (/sys)

sysfs通常挂载在 /sys目录下。其主要子目录有:

/sys/block/:包含所有块设备(如硬盘、分区)的符号链接。

/sys/bus/:按总线类型组织的目录。这是理解设备关系的关键。

pci/, usb/, i2c/, spi/等。

进入 pci0000:00/(一个PCI总线实例),你会看到 devices/和 drivers/两个目录。

devices/下的目录名是内核内部的设备名(如 0000:00:02.0),它是一个符号链接,指向 /sys/devices/...下的实际设备目录。

drivers/下是每个已加载的PCI驱动程序的目录(如 i915显卡驱动)。

/sys/class/:按设备功能组织的目录。这是用户最常与之交互的部分,它跨越了总线的界限。

  • input/:所有输入设备(鼠标、键盘、触摸屏),你可以在这里找到 mouseX, eventY。

  • net/:所有网络设备(eth0, wlan0)。

  • tty/:所有终端和伪终端设备。

  • leds/:所有LED指示灯。

  • power_supply/:所有电源设备(电池、交流适配器)。

  • 进入 net/下的某个网卡目录,你会发现 address(MAC地址)、operstate(运行状态)等属性文件。

/sys/devices/:所有设备在系统全局设备树中的物理位置。这里的目录结构是内核看到的真实硬件拓扑结构,非常复杂。其他目录(如 /sys/bus/pci/devices/)下的符号链接最终都指向这里。

/sys/firmware/:包含系统固件相关的信息,如 ACPI 表、设备树(Device Tree)等。

/sys/module/:包含已加载的内核模块的信息。

/sys/power/:用于控制系统的电源状态,如休眠和唤醒。你可以向 state文件写入 mem(挂起到内存)或 disk(挂起到磁盘)来触发休眠。

作用:

  1. 建立系统中总线、驱动、设备三者之间的联系

  2. 向用户空间展示内核中各种设备的拓扑图

  3. 提供给用户空间对设备获取信息和操作的接口,取代 ioctl 的部分功能

(99+ 封私信 / 51 条消息) linux设备驱动程序--sysfs用户接口的使用 - 知乎

sysfs 虚拟文件系统,。

sys

  • block:

  • bus:

    • platform

  • class:

  • dev:

sysfs文件系统

sysfs在内核中的组成要素

在用户空间/sys下的显示

内核对象(kobject)

目录

对象属性(attribute)

文件

对象关系(relationship)

链接(Symbolic Link)

四个基本结构

类型

所包含的内容

内核数据结构

对应/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/?/

目录组织结构

/sys下的子目录

所包含的内容

/sys/devices

这是内核对系统中所有设备的分层次表达模型,也是/sys文件系统管理设备的最重要的目录结构。

/sys/dev

这个目录下维护一个按字符设备和块设备的主次号码(major:minor)链接到真实的设备(/sys/devices下)的符号链接文件。

/sys/bus

这是内核设备按总线类型分层放置的目录结构, devices 中的所有设备都是连接于某种总线之下,在这里的每一种具体总线之下可以找到每一个具体设备的符号链接。

/sys/class

按照设备功能分类的设备模型,如系统所有输入设备都会出现在/sys/class/input 之下,而不论它们是以何种总线连接到系统。

/sys/kernel

这里是内核所有可调整参数的位置,目前只有 uevent_helper, kexec_loaded, mm, 和新式的slab 分配器等几项较新的设计在使用它,其它内核可调整参数仍然位于sysctl(/proc/sys/kernel) 接口中。

/sys/module

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

/sys/power

这里是系统中电源选项,这个目录下有几个属性文件可以用于控制整个机器的电源状态,如可以向其中写入控制命令让机器关机、重启等。

Linux统一设备模型:/sys/bus、/sys/class、

class_create

函数功能:在/sys/class生成一个目录,目录名由name指定
函数原型:struct class *class_create(struct module *owner, const char *name);
函数参数:
- *owner:THIS_MODULE
- *name:目录名
函数返回值:
- 成功:class指针
- 失败:NULL

对于class是否创建成功,可以使用IS_ERR宏来判断

辅助接口:可以定义一个struct class 的指针变量cls来接受返回值,然后通过IS_ERR(cls)判断是否失败;
IS_ERR(cls);成功----------------->0
IS_ERR(cls);失败----------------->非0
PTR_ERR(cls);来获得失败的返回错误码;
*/

class_destroy

函数功能:删除class_create生成目录
函数原型:void class_destroy(struct class *cls)
函数参数:
 - cls:class指针
返回值:

device_create

函数功能:在/sys/class目录下class_create生成目录再生成一个子目录与该设备相对应,发uevent让应用程序udevd创建设备文件
函数原型:struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
函数参数:
- *class:class指针
- parent:父对象,一般NULL
- devt:设备号
- *drvdata:驱动私有数据,一般NULL
- *fmt:字符串的格式(这里重新看下视频)
- ...:不定参数
函数返回值:
- 成功:device指针
- 失败:NULL

device_destroy

函数功能:删除device_create生成目录
函数原型:void device_destroy(struct class *class, dev_t devt)
函数参数:
- *class:class指针
- devt:设备号
函数返回值:
​
- 

struct class *class_create(struct module *owner, const char *name);
/*
 * 功能:在/sys/class生成一个目录,目录名由name指定
 * 参数:
    struct module *owner - THIS_MODULE
    const char *name - 目录名
 * 返回值  成功:class指针   失败:NULL
*/
/*
辅助接口:可以定义一个struct class 的指针变量cls来接受返回值,然后通过IS_ERR(cls)判断是否失败;
IS_ERR(cls);成功----------------->0
IS_ERR(cls);失败----------------->非0
PTR_ERR(cls);来获得失败的返回错误码;
*/
void class_destroy(struct class *cls)
/*
* 功能:删除class_create生成目录
* 参数:
    struct class *cls - class指针
* 返回值
*/
struct device *device_create(struct class *class, struct device *parent,
                 dev_t devt, void *drvdata, const char *fmt, ...)
/*
 * 功能:在/sys/class目录下class_create生成目录再生成一个子目录与该设备相对应,发uevent让应用程序udevd创建设备文件
 * 参数:
    struct class *class - class指针
    struct device *parent - 父对象,一般NULL
    dev_t devt - 设备号
    void *drvdata - 驱动私有数据,一般NULL
    const char *fmt - 字符串的格式
     ... - 不定参数
 * 返回值
    成功:device指针
    失败:NULL
 */
void device_destroy(struct class *class, dev_t devt)
/*
 * 功能:删除device_create生成目录
 * 参数:
    struct class *class - class指针
    dev_t devt - 设备号
 * 返回值
*/

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
​
int major = 12;
int minor = 0;
int dev_num = 1;
​
struct mychar_dev
{
    struct cdev mydev;
​
    struct class *pcls;
    struct device *pdev;
};
​
struct mychar_dev gmydev;
​
int __init mychar_init(void)
{
    int ret = 0;
    dev_t devno = MKDEV(major, minor);
​
    /* 获取设备号 */
    ret = register_chrdev_region(devno, dev_num, "mychardev");
    if (ret)
    {
        ret = alloc_chrdev_region(&devno, minor, dev_num, "mychardev");
        if (ret)
        {
            printk("get devno failed\n");
            return -1;
        }
        major = MAJOR(devno);
    }
​
    /* 注册驱动 */
    cdev_init(&gmydev.mydev, &myops);
    gmydev.mydev.owner = THIS_MODULE;
    cdev_add(&gmydev.mydev, devno, dev_num);
​
    /* 在/sys/class下创建类目录,目录名为第二个参数 */
    gmydev.pcls = class_create(THIS_MODULE, "mychar");
    if (IS_ERR(gmydev.pcls))
    {
        cdev_del(&gmydev.mydev);
        unregister_chrdev_region(devno, dev_num);
        return -1;
    }
​
    /* 在上面的类目录下创建设备 */
    gmydev.pdev = device_create(gmydev.pcls, NULL, devno, NULL, "mychar");
    if (NULL == gmydev.pdev)
    {
        class_destroy(gmydev.pcls);
        cdev_del(&gmydev.mydev);
        unregister_chrdev_region(devno, dev_num);
        return -1;
    }
    return 0;
}
​
void __exit mychar_exit(void)
{
    dev_t devno = MKDEV(major, minor);
​
    device_destroy(gmydev.pcls, devno);
    class_destroy(gmydev.pcls);
​
    cdev_del(&gmydev.mydev);
​
    unregister_chrdev_region(devno, dev_num);
}