2025-03-18
Linux的sysfs是一种虚拟文件系统,用于将内核对象、属性和关系导出到用户空间,方便用户和管理员查看和配置内核及硬件信息。sysfs通常挂载在/sys目录下。
sysfs class是sysfs中的一个重要部分,用于按功能分类展示设备信息。每个类对应一种设备类型(如网络设备、输入设备等),类目录下包含该类设备的子目录,每个子目录代表一个具体设备,包含其属性和相关链接。
sysfs classnet: 网络设备信息。
input: 输入设备(如键盘、鼠标)信息。
block: 块设备(如硬盘、分区)信息。
tty: 串行设备信息。
drm: 图形设备信息。
sound: 音频设备信息。
// simple.c
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/minmax.h>
#include <linux/module.h>
#include <linux/slab.h>
#define DEV_NAME "my-dev"
#define CLASS_NAME "my-class"
static dev_t dev_nr;
static struct class *my_class;
static struct device *my_dev;
static int answer = 42;
DEVICE_INT_ATTR(answer, 0600, answer);
static char text[64] = "Hello World!\n";
static ssize_t text_store(struct device *dev, struct device_attribute *attr,
                           const char *buffer, size_t size) {
    pr_info("simple_text_store\n");
    memset(text, 0, sizeof(text));
    size = min(size, sizeof(text));
    strncpy(text, buffer, size);
    return size;
}
static ssize_t text_show(struct device *dev, struct device_attribute *attr,
                          char *buffer) {
    pr_info("simple_text_show\n");
    strcpy(buffer, text);
    return strlen(text);
}
DEVICE_ATTR(text, 0600, text_show, text_store);
static int __init simple_init(void) {
    pr_info("simple_init\n");
    int status = alloc_chrdev_region(&dev_nr, 0, MINORMASK + 1, DEV_NAME);
    if (status < 0) {
        pr_err("alloc_chrdev_region error\n");
        return status;
    }
    my_class = class_create(CLASS_NAME);
    if (IS_ERR(my_class)) {
        pr_err("class_create error\n");
        status = PTR_ERR(my_class);
        goto free_dev_nr;
    }
    my_dev =
        device_create(my_class, NULL, dev_nr, NULL, DEV_NAME"%d", MINOR(dev_nr));
    if (IS_ERR(my_dev)) {
        pr_err("device_create error\n");
        status = PTR_ERR(my_dev);
        goto free_class;
    }
    status = device_create_file(my_dev, &dev_attr_text);
    if (status) {
        pr_err("device_create_file for text error\n");
        goto free_dev;
    }
    status = device_create_file(my_dev, &dev_attr_answer.attr);
    if (status) {
        pr_err("device_create_file for answer error\n");
        goto free_text;
    }
    return 0;
free_text:
    device_remove_file(my_dev, &dev_attr_text);
free_dev:
    device_destroy(my_class, dev_nr);
free_class:
    class_destroy(my_class);
free_dev_nr:
    unregister_chrdev_region(dev_nr, MINORMASK + 1);
    return status;
}
static void __exit simple_exit(void) {
    pr_info("simple_exit\n");
    device_remove_file(my_dev, &dev_attr_answer.attr);
    device_remove_file(my_dev, &dev_attr_text);
    device_destroy(my_class, dev_nr);
    class_destroy(my_class);
    unregister_chrdev_region(dev_nr, MINORMASK + 1);
}
module_init(simple_init);
module_exit(simple_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("heng30");
MODULE_VERSION("v0.0.1");
MODULE_DESCRIPTION("A simple kernel module");
#!/bin/sh
top-dir = $(shell pwd)
kernel-version = $(shell uname -r)
kernel-dir ?= /lib/modules/$(kernel-version)/build
obj-m += simple.o
all:
        make -C $(kernel-dir) modules M=$(top-dir)
clean:
        rm -f *.o *.ko *.mod *.mod.c *.order *.symvers *.dtbo
        make -C $(kernel-dir) clean m=$(top-dir)
编译程序:make
安装驱动:insmod simple.ko
查看文件:ls /sys/class/my-class/my-dev0
answer  dev  power  subsystem  text  uevent
读取数据:cat /sys/class/my-class/my-dev0/text
写入数据:echo "good" > /sys/class/my-class/my-dev0/text
移除驱动:rmmod simple.ko