【Linux驱动开发 ---- 4.1_sysfs 详解】

Linux驱动开发 ---- 4.1_sysfs 详解

目录

  • [Linux驱动开发 ---- 4.1_sysfs 详解](#Linux驱动开发 ---- 4.1_sysfs 详解)
      • [📌 **sysfs 详细讲解:**](#📌 sysfs 详细讲解:)
      • [📌 **如何在内核中创建 `sysfs` 文件**:](#📌 如何在内核中创建 sysfs 文件:)
        • [**步骤 1:定义 `device_attribute` 结构体**](#步骤 1:定义 device_attribute 结构体)
        • [**示例:定义属性文件 `myattribute`**](#示例:定义属性文件 myattribute)
        • [**步骤 2:实现 `show` 函数**](#步骤 2:实现 show 函数)
        • [**示例:实现 `show` 函数**](#示例:实现 show 函数)
        • [**步骤 3:注册 `sysfs` 文件**](#步骤 3:注册 sysfs 文件)
        • [**步骤 4:卸载时移除 `sysfs` 文件**](#步骤 4:卸载时移除 sysfs 文件)
      • [📌 **完整示例:创建和操作 `sysfs` 文件**](#📌 完整示例:创建和操作 sysfs 文件)
      • [📌 **代码解析:**](#📌 代码解析:)
      • [📌 **如何使用:**](#📌 如何使用:)
      • [📌 **总结**:](#📌 总结:)

📌 sysfs 详细讲解:

sysfs 是 Linux 内核提供的一个虚拟文件系统,它将内核中的设备信息暴露给用户空间。通过 sysfs,用户可以通过文件接口与内核交互,比如读取硬件设备的状态、修改设备参数等。

主要功能
  • 暴露内核对象 :通过 sysfs,设备、驱动、总线等内核对象的信息被暴露为文件,用户可以通过这些文件读取或写入数据。
  • 设备和驱动的接口 :很多硬件设备和内核模块在启动时会创建与之相关的 sysfs 文件,供用户空间配置或查询设备状态。
常见路径
  • /sys/class/:包含类目录,如字符设备、块设备等。例如 /sys/class/net/ 存放网络设备的信息。
  • /sys/devices/:存放与设备相关的路径,系统中的硬件设备和其属性通过 sysfs 展现出来。例如 /sys/devices/system/cpu/ 存放 CPU 相关的信息。

📌 如何在内核中创建 sysfs 文件

在 Linux 内核模块中,我们可以通过 sysfs 来创建设备属性文件。这些文件通常会映射到内核对象(如设备)的属性值。

步骤 1:定义 device_attribute 结构体

首先,你需要定义一个 struct device_attribute 类型的结构体来描述你的设备属性。这个结构体至少包含以下内容:

  • attr:设备属性本身,包含属性文件的名字和访问权限。
  • show:一个函数指针,用于读取该属性时的逻辑。
  • store:一个函数指针,用于修改该属性时的逻辑(可选)。
示例:定义属性文件 myattribute
c 复制代码
struct device_attribute dev_attr_myattribute = {
    .attr = { .name = "myattribute", .mode = S_IRUGO },  // 属性文件名称和权限
    .show = myattribute_show,  // 读取文件时调用的函数
};
  • attr :这里的 name 表示在 /sys/class/ 路径下文件的名字,mode 定义了文件的访问权限,S_IRUGO 表示该文件可读。
  • show:这是读取属性时调用的函数。
步骤 2:实现 show 函数

show 函数用于在读取 sysfs 文件时返回相应的数据。函数原型是:

c 复制代码
static ssize_t myattribute_show(struct device *dev, struct device_attribute *attr, char *buf);
  • dev:指向设备对象的指针。
  • attr:指向设备属性结构的指针。
  • buf:用于存放要返回给用户的数据。

show 函数中,我们通过 sprintf 函数将要返回的内容写入 buf

示例:实现 show 函数
c 复制代码
static ssize_t myattribute_show(struct device *dev, struct device_attribute *attr, char *buf) {
    return sprintf(buf, "42\n");  // 返回字符串 "42"
}

这个函数会将字符串 "42\n" 写入 buf,这个字符串就是当用户空间读取 /sys/class/ 中对应的文件时返回的数据。

步骤 3:注册 sysfs 文件

使用 sysfs_create_file 函数将设备属性注册到 sysfs 中。这个函数将属性文件创建到设备的 kobj(内核对象)下。kobj 是与设备关联的内核对象,用来在内核中组织和管理设备。

c 复制代码
sysfs_create_file(&dev->kobj, &dev_attr_myattribute.attr);
  • dev->kobj :设备对象的 kobj,指向设备的内核对象。
  • dev_attr_myattribute.attrdev_attr_myattribute 结构体中的 attr,它包含了属性文件的名字、权限和访问方式。
步骤 4:卸载时移除 sysfs 文件

当内核模块卸载时,必须清理已创建的 sysfs 文件。可以通过 sysfs_remove_file 来移除属性文件:

c 复制代码
sysfs_remove_file(&dev->kobj, &dev_attr_myattribute.attr);

📌 完整示例:创建和操作 sysfs 文件

我们将创建一个简单的内核模块,该模块会创建一个 sysfs 文件,返回一个静态的值。

代码实现
c 复制代码
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/fs.h>

static ssize_t myattribute_show(struct device *dev, struct device_attribute *attr, char *buf) {
    return sprintf(buf, "42\n");  // 返回值为 "42"
}

static struct device_attribute dev_attr_myattribute = {
    .attr = { .name = "myattribute", .mode = S_IRUGO },
    .show = myattribute_show,
};

static struct class *my_class;
static struct device *my_device;

static int __init my_module_init(void) {
    int ret;

    // 创建设备类
    my_class = class_create(THIS_MODULE, "myclass");
    if (IS_ERR(my_class)) {
        pr_err("Failed to create class\n");
        return PTR_ERR(my_class);
    }

    // 创建设备
    my_device = device_create(my_class, NULL, 0, NULL, "mydevice");
    if (IS_ERR(my_device)) {
        pr_err("Failed to create device\n");
        class_destroy(my_class);
        return PTR_ERR(my_device);
    }

    // 注册 sysfs 属性
    ret = sysfs_create_file(&my_device->kobj, &dev_attr_myattribute.attr);
    if (ret) {
        pr_err("Failed to create sysfs file\n");
        device_destroy(my_class, 0);
        class_destroy(my_class);
        return ret;
    }

    pr_info("Module initialized\n");
    return 0;
}

static void __exit my_module_exit(void) {
    // 移除 sysfs 文件
    sysfs_remove_file(&my_device->kobj, &dev_attr_myattribute.attr);

    // 销毁设备和类
    device_destroy(my_class, 0);
    class_destroy(my_class);

    pr_info("Module exited\n");
}

module_init(my_module_init);
module_exit(my_module_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple sysfs example");

📌 代码解析:

  1. myattribute_show :此函数负责返回字符串 "42\n",表示当用户从 sysfs 文件中读取时返回的值。
  2. dev_attr_myattribute :定义了一个名为 myattribute 的设备属性,并指定了它的权限为可读(S_IRUGO)。
  3. sysfs_create_file :将 myattribute 属性文件创建到设备的 sysfs 中,使得用户空间可以访问该属性。
  4. module_initmodule_exit :模块加载时创建设备并注册 sysfs 文件,卸载时删除 sysfs 文件并清理资源。

📌 如何使用:

  1. 编译并加载该内核模块。

    安装开发工具(如果尚未安装):

    bash 复制代码
    sudo apt-get install build-essential linux-headers-$(uname -r)

    1> 创建内核模块代码文件

    将你的内核模块代码保存为一个 .c 文件(例如 my_sysfs_module.c)。你可以使用任何文本编辑器(例如 nanovim)来编辑该文件。

    bash 复制代码
    nano my_sysfs_module.c

    然后将你提供的代码粘贴到 my_sysfs_module.c 文件中并保存。

    2> 创建 Makefile

    为了编译内核模块,你需要创建一个 Makefile,该文件告诉 make 如何编译模块。

    在同一目录下创建一个 Makefile 文件,内容如下:

    makefile 复制代码
    obj-m += my_sysfs_module.o
    
    all:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
    
    clean:
        make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

    解释:

    • obj-m += my_sysfs_module.o:告诉 make 需要编译的模块源文件。
    • make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules:调用内核的构建系统来编译模块。
    • clean 目标:清理编译文件。

    3> 编译内核模块

    在终端中运行以下命令来编译内核模块:

    bash 复制代码
    make

    这将生成一个 my_sysfs_module.ko 的内核模块文件。

    4> 加载内核模块

    使用 insmod 命令加载内核模块:

    bash 复制代码
    sudo insmod my_sysfs_module.ko

    如果加载成功,你将看到 "Module initialized" 消息,表示模块已成功加载并创建了 sysfs 文件。

    5> 查看创建的 sysfs 文件

    加载模块后,模块会在 /sys/class/myclass/mydevice/myattribute 创建一个 sysfs 文件。你可以使用以下命令查看该文件:

    bash 复制代码
    cat /sys/class/myclass/mydevice/myattribute

    该文件的内容应为 "42",这是在 myattribute_show 函数中返回的值。

    6> 卸载内核模块

    当你不再需要该模块时,可以使用 rmmod 卸载它:

    bash 复制代码
    sudo rmmod my_sysfs_module

    在卸载时,会调用 my_module_exit 函数,删除 sysfs 文件并销毁设备和类。你会看到 "Module exited" 消息。

    7> 清理编译文件

    为了清理编译产生的文件,可以运行:

    bash 复制代码
    make clean
复制代码
8> **总结**
通过这些步骤,你可以成功地编译、加载和操作一个内核模块,该模块创建了一个 `sysfs` 文件并返回一个静态值。
  1. 加载后,你会在 /sys/class/myclass/mydevice/myattribute 路径下看到一个名为 myattribute 的文件。
  2. 可以通过以下命令查看其内容:
bash 复制代码
cat /sys/class/myclass/mydevice/myattribute

输出应为:

bash 复制代码
42

📌 总结

通过 sysfs,我们可以方便地暴露内核中的信息给用户空间。通过以上步骤,你不仅学会了如何创建一个简单的 sysfs 文件,还了解了设备与内核对象之间的关系。

  • 使用 sysfs_create_file 创建文件;
  • 使用 show 函数返回数据;
  • 使用 sysfs_remove_file 移除文件。
相关推荐
lishaoan7726 分钟前
用TensorFlow进行逻辑回归(二)
人工智能·tensorflow·逻辑回归
学不动CV了35 分钟前
C语言32个关键字
c语言·开发语言·arm开发·单片机·算法
慌ZHANG42 分钟前
智慧气象新范式:人工智能如何重构城市级气象服务生态?
人工智能
Continue_with1 小时前
docker设置代理
运维·docker·容器
Eumenidus1 小时前
使用ESM3蛋白质语言模型进行快速大规模结构预测
人工智能·语言模型·自然语言处理
熊猫钓鱼>_>1 小时前
FastGPT革命:下一代语言模型的极速进化
人工智能·语言模型·自然语言处理
吕永强1 小时前
电网的智能觉醒——人工智能重构能源生态的技术革命与公平悖论
人工智能·科普
极限实验室1 小时前
喜报 - 极限科技荣获 2025 上海开源创新菁英荟「开源创新新星企业」奖
人工智能·开源
在美的苦命程序员1 小时前
芯片之后,AI之争的下一个战场是能源?
人工智能
姜暮儿1 小时前
U盘直接拔出不在电脑上弹出有何影响
stm32·单片机·嵌入式硬件