ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

RK3568平台 热插拔机制

2026/9/18 5:28:08 拓冰建站 浏览量
RK3568平台 热插拔机制

一.热插拔的基本概念

热插拔是指在设备运行的情况下,能够安全地插入或拔出硬件设备,而无需关闭或重启系统。这意味着你可以在计算机或其他电子设备上插入或拔出硬件组件(比如USB设备,扩展卡,硬件驱动器等),而无需关机或中断正在进行的操作。

比如鼠标,键盘,打印机,存储设备等。

二.热插拔用到的文件系统

mdev:mdev 是一个轻量级的热插拔设备文件系统,通常用于嵌入式Linux 系统。它是udev的简化版本,使用 uevent_helper 机制来处理设备的插入和拔出事件。mdev 在设备插入时调用相应的用户程序来创建设备节点。

udev:udev 是目前在 PC 机上广泛使用的热插拔设备文件系统。它基于netlink 机制,监听内核发送的 uevent 来处理设备的插入和拔出。udev 能够动态创建和管理设备节点,并在设备插入时加载适当的驱动程序。它提供了丰富的配置选项,使用户能够灵活地管理设备文件。

udev 是目前应用最广泛的设备文件系统,而mdev主要用于嵌入式系统中,提供了轻量级的设备管理功能。

三.内核发送事件到用户空间

kobject_uevent 函数,用于生成和发送uevent 事件到用户空间。

int kobject_uevent(struct kobject *kobj, enum kobject_action action);

kobj : 要发送 uevent 事件的内核对象(kobject)

action: 表示触发 uevent 的动作,可以是设备的插入,拔出,属性变化等。

驱动程序:

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/configfs.h>
#include <linux/kernel.h>
#include <linux/kobject.h>struct kobject *mykobject01;
struct kset *mykset;
struct kobj_type mytype;// 模块的初始化函数
static int mykobj_init(void)
{int ret;// 创建并添加一个ksetmykset = kset_create_and_add("mykset", NULL, NULL);// 分配并初始化一个kobjectmykobject01 = kzalloc(sizeof(struct kobject), GFP_KERNEL);mykobject01->kset = mykset;// 初始化并添加kobject到ksetret = kobject_init_and_add(mykobject01, &mytype, NULL, "%s", "mykobject01");// 触发一个uevent事件,表示kobject的属性发生了变化ret = kobject_uevent(mykobject01, KOBJ_CHANGE);return 0;
}// 模块退出函数
static void mykobj_exit(void)
{// 释放kobjectkobject_put(mykobject01);kset_unregister(mykset);
}module_init(mykobj_init);  // 指定模块的初始化函数
module_exit(mykobj_exit);  // 指定模块的退出函数MODULE_LICENSE("GPL");    // 模块使用的许可证
MODULE_AUTHOR("topeet");  // 模块的作者

驱动加载之后,如上图所示 udev 接收到 change 动作,说明uevent 事件已经发送成功了。/mykset/mykobject01 是 kobject 在根目录/sys/下的路径。

四.使用 udev 挂载 U 盘

可以在串口终端输入以下命令来查看 udev 是否已经启用了

ps -aux | grep -nR udev

要想使用 udev 来实现U 盘的自动挂载,还需在开发板的/etc/udev/rules.d 目录下创建相应的规则文件(/etc/udev/rules.d 目录不存在可以手动创建,一般都已经存在了),这里我们创建一个名为 001.rules 的文件。

KERNEL=="sd[a-z][0-9]", SUBSYSTEM=="block", ACTION=="add", RUN+="/etc/udev/rules.d/usb/usb-add.sh %k"
SUBSYSTEM=="block", ACTION=="remove", RUN+="/etc/udev/rules.d/usb/usb-remove.sh"

(1)KERNEL=="sd[a-z][0-9]" KERNEL:表示匹配设备的内核名。 "sd[a-z][0-9]":是一个正则表达式模式,sd:表示设备名以"sd" 开头,[a-z]:表示设备名的第三个字符是小写字母,[0-9]:表示设备名的第四个字符是数字。这个模式用于匹配 USB 存储设备的块设备节点,如 /dev/sda1、/dev/sdb2 等。

(2)SUBSYSTEM=="block" SUBSYSTEM:表示匹配设备的子系统名称。 "block":表示设备的子系统是块设备子系统,即与磁盘、分区等相关的设备。这部分规则是为了确保只匹配块设备子系统下的设备。

(3)ACTION=="add"和 ACTION=="remove" ACTION:表示匹配设备的动作。 "add":表示设备被添加。 "remove":表示设备被 yichu。 这部分规则是为了处理设备被添加和被删除的事件

(4)RUN+="/etc/udev/rules.d/usb/usb-add.sh %k" RUN+="...":表示在匹配的设备上执行指定的命令。 "/etc/udev/rules.d/usb/usb-add.sh":是要执行的命令的路径,即在设备添加时执行/etc/udev/rules.d/usb/usb-add.sh 脚本文件。 %k:是 Udev 提供的一个变量,表示匹配的设备的内核名。

可以注意到当块设备被添加的时候会执行/etc/udev/rules.d/usb/usb-add.sh 脚本,块设备被删除的时候会执行/etc/udev/rules.d/usb/usb-remove.sh 脚本。

所以接下来我们要完善这两个脚本内容,首先在/etc/udev/rules.d/目录下创建名为 usb 的文件夹,并在这个创建usb-add.sh和usb-remove.sh 脚本。

 /etc/udev/rules.d/usb/usb-add.sh:

#!/bin/sh
/bin/mount -t vfat /dev/$1 /mnt/

/etc/udev/rules.d/usb/usb-remove.sh:

#!/bin/sh
sync
/bin/umount -l /mnt/