当前位置: 首页 > news >正文

驱动开发硬核特训 · Day 21(上篇加强版):深入理解子系统机制与实战初探

📅 日期:2025-04-27
📚 技术平台:嵌入式Jerry(B站)


1. 为什么要有子系统?(深度版)

在 Linux 内核发展早期,设备管理较为混乱,每种设备(如网卡、硬盘、LED)各自为政,驱动开发非常困难,系统维护复杂、扩展困难。

在这里插入图片描述

1.1 难点总结

  • 接口不统一:每类设备定义自己的操作方式
  • 用户访问混乱:无法通过统一路径管理设备
  • 内核膨胀严重:各种 if-else、switch-case 代码横行

1.2 子系统带来的变化

✅ 为每一类功能设备定义 统一接口规范
✅ 在 /sys/class/ 下暴露出 统一访问路径
✅ 驱动开发者只需遵循子系统接口,避免重复设计
✅ 设备热插拔、动态加载、统一管理变得简单。


2. 子系统本质是什么?

一句通俗的话:

子系统是内核根据设备功能,把一大堆设备分类管理的机制。

技术上:

  • 基于 kobject/kset/ktype 的设备模型机制
  • 在 sysfs 文件系统中形成 class 目录
  • 提供标准的注册/注销流程

2.1 子系统 VS 设备模型

项目设备模型 (device model)子系统 (subsystem)
定义Linux 内核通用框架特定类型设备的组织分类
基础kobject/kset/ktype基于设备模型构建
目的支撑总线/驱动/设备统一管理便于管理一类设备
示例platform bus, devicenet, block, regulator

3. 子系统的基本结构和机制

一个子系统通常包括:

  • Class:对应 sysfs 的 /sys/class/,逻辑分组
  • Device:每个实例(一个 LED、一个 BUCK)对应一个 device
  • Driver:驱动设备,通常与特定 bus 匹配

结构图理解:

class_create() -> 创建类 (Class)
device_create() -> 创建设备 (Device)用户空间访问:
/sys/class/<class>/<device>/

4. 子系统创建与使用详细流程

4.1 创建子系统

内核代码核心步骤:

// 1. 创建一个 Class(子系统)
struct class *my_class;my_class = class_create(THIS_MODULE, "mychar_class");
if (IS_ERR(my_class))return PTR_ERR(my_class);

对应生成 /sys/class/mychar_class/


4.2 注册设备到子系统

// 2. 在子系统下挂一个设备
struct device *dev;dev = device_create(my_class, NULL, devt, NULL, "mychar");
if (IS_ERR(dev))return PTR_ERR(dev);

对应生成 /sys/class/mychar_class/mychar 节点。

同时,udev等工具可自动生成 /dev/mychar 设备文件。


4.3 销毁流程

// 卸载时销毁
device_destroy(my_class, devt);
class_destroy(my_class);

确保模块退出时资源清理,防止内核内存泄漏。


5. 详细实例:基于 i.MX8MP EVK 控制 LED

5.1 设备树节点

imx8mp-evk.dts 中已有:

gpio-leds {compatible = "gpio-leds";pinctrl-names = "default";pinctrl-0 = <&pinctrl_gpio_led>;status {label = "yellow:status";gpios = <&gpio3 16 GPIO_ACTIVE_HIGH>;default-state = "on";};
};

说明:

  • GPIO3_16 = 32 * 2 + 16 = 80号 GPIO
  • 默认状态点亮

5.2 驱动代码示例(增加丰富注释版)

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/gpio.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/device.h>#define LED_GPIO 80  // gpio3_16static dev_t devt;
static struct cdev led_cdev;
static struct class *led_class;
static struct device *led_device;// 写接口,实现开关LED
static ssize_t led_write(struct file *filp, const char __user *buf, size_t len, loff_t *off)
{char kbuf[4] = {0};if (copy_from_user(kbuf, buf, len))return -EFAULT;if (kbuf[0] == '1')gpio_set_value(LED_GPIO, 1); // 点亮elsegpio_set_value(LED_GPIO, 0); // 熄灭return len;
}// 文件操作结构
static struct file_operations led_fops = {.owner = THIS_MODULE,.write = led_write,
};static int __init led_init(void)
{int ret;// 申请GPIOret = gpio_request(LED_GPIO, "led_gpio");if (ret)return ret;gpio_direction_output(LED_GPIO, 1);// 分配设备号alloc_chrdev_region(&devt, 0, 1, "ledchar");cdev_init(&led_cdev, &led_fops);cdev_add(&led_cdev, devt, 1);// 创建子系统(class)led_class = class_create(THIS_MODULE, "led_class");led_device = device_create(led_class, NULL, devt, NULL, "ledchar");pr_info("ledchar device initialized\n");return 0;
}static void __exit led_exit(void)
{gpio_set_value(LED_GPIO, 0);gpio_free(LED_GPIO);device_destroy(led_class, devt);class_destroy(led_class);cdev_del(&led_cdev);unregister_chrdev_region(devt, 1);pr_info("ledchar device removed\n");
}module_init(led_init);
module_exit(led_exit);
MODULE_LICENSE("GPL");

5.3 加载模块效果

# 加载模块
insmod ledchar.ko# 查看/sys目录
ls /sys/class/led_class/ledchar/# 控制LED
echo 1 > /dev/ledchar
echo 0 > /dev/ledchar

✅ 通过字符设备 /dev/ledchar 写操作,控制真正硬件上的 GPIO 灯亮灭。
✅ 同时在 /sys/class/led_class/ledchar/ 中可查看设备属性。


6. 深度讲解:子系统、字符设备、设备模型关系总结

类型作用示例本质
字符设备 (cdev)提供read/write/ioctl接口/dev/ledchar用来读写硬件
子系统 (class)统一组织管理设备/sys/class/led_class/便于分类管理
设备模型 (device model)内核统一抽象bus/device/driver子系统基于它

7. 为什么字符设备要挂子系统?

  • 如果直接裸 cdev:仅 /dev 有节点,sysfs 中无管理
  • 如果挂到子系统
    • /dev 节点
    • /sys/class 路径
    • 更易于热插拔、用户空间管理
    • 可以扩展 attribute 节点(如调节亮度)

所以,规范写法:

字符设备 + 子系统结合,才是完整专业驱动。


8. 小结与今日提炼

✅ 子系统统一管理同类设备,提升 Linux 内核规范性。
✅ 子系统本质上是设备模型的一个应用。
✅ 字符设备可直接使用,也可以挂到子系统,推荐挂载管理。
✅ 电源子系统(regulator)、LED子系统、网卡子系统都是应用范例。


9. 后续学习计划(已升级)

阶段主题备注
✅ 阶段一设备模型 + 字符设备已完成
✅ 阶段二子系统概念与应用今日完成
🔜 阶段三电源子系统(regulator)深度学习明日开始

10. 技术交流

💬 更多 Linux 驱动开发教学,请关注 B 站:【嵌入式Jerry


相关文章:

  • 微服务架构下 MySQL 大表分库分表方案
  • 【Linux网络】构建与优化HTTP请求处理 - HttpRequest从理解到实现
  • std::mutex底层实现原理
  • Spring Boot集成RocketMQ
  • Win7 SSL证书问题
  • 【C++11】列表初始化
  • 第34课 常用快捷操作——按“空格键”旋转图元
  • 使用 binlog2sql 闪回 MySQL8 数据
  • 高精度运算(string函数)
  • 生成式AI全栈入侵:当GPT-4开始自动编写你的Next.js路由时,人类开发者该如何重新定义存在价值?
  • Java多态终极指南:从基础到高级应用
  • 前端技术个人求职简历模板
  • 股指期货成交量是单边还是双边?
  • NVIDIA GPU 计算能力与 COLMAP 编译配置指南【2025最新版!!!】
  • 【AI提示词】战略顾问
  • 侵水防触电的原理是什么? 侵水防触电算先进技术吗?-优雅草卓伊凡
  • 计算机视觉——对比YOLOv12、YOLOv11、和基于Darknet的YOLOv7的微调对比
  • mmap详解
  • SpringBoot中暗藏的设计模式
  • RabbitMQ全栈实践手册:从零搭建消息中间件到SpringAMQP高阶玩法
  • 屋顶上的阳光与火光:战争如何改变了加沙的能源格局
  • 湖州通告13批次不合格食品,盒马1批次多宝鱼甲硝唑超标
  • 文化体验+商业消费+服务创新,上海搭建入境旅游新模式
  • 白酒瓶“神似”北京第一高楼被判侵权,法院一审判赔45万并停售
  • 体坛联播|皇马上演罢赛闹剧,杨瀚森宣布参加NBA选秀
  • 经济日报:AI时代如何寻找“你的赛道”