提示:Linux 下实现U盘和TF卡挂载
前言
最近学习的模块是Linux 驱动下的热插拔技术知识点,讨论的知识点具体包括:
- 内核如何发送事件到用户空间
uevent事件kset_uevent_ops结构体netlink监听广播知识点uevent_helper机制 【暂未讲解,网上很多资料说明】
那么实际热插拔在驱动上面怎么应用的呢? 基本上这里要将的首先是知识点 udev/mdev 是具体的应用知识点,然后实际举例说明 如何配置驱动的。
其实系统基于热插拔技术已经很完善了,写好了的。但是还是需要工程师配置规则,配置实现方式的。
参考资料
驱动-热插拔-内核发送事件到用户空间-uevent
驱动-热插拔-kset_uevent_ops知识点
驱动-热插拔-Netlink广播监听内核状态
一、 udev mdev 知识点
udev - 现代 Linux 系统的标准
udev 是Userspace /dev的缩写。它是当前绝大多数桌面、服务器和移动版 Linux 发行版(如 Ubuntu, Fedora, Debian, Android等)的标准设备管理器。
核心特点
-
运行在用户空间(Userspace):与它的前任(
devfs,运行在内核空间)不同,udev是一个用户空间的守护进程(systemd-udevd)。这使得它更加灵活和稳定,不会因为一个错误导致内核崩溃。 -
与内核通过 netlink 通信:
udev监听内核通过netlink套接字发出的uevent事件。每当内核检测到一个设备被加入(如插入U盘)或移除时,它会向用户空间广播一个uevent,udev接收到后就会执行相应的规则。 -
基于规则的强大灵活性:
udev的核心是它的规则文件(通常位于/etc/udev/rules.d/和/lib/udev/rules.d/)。这些规则允许管理员:
1、动态创建设备节点:决定设备节点的名称(如永远将某个特定的U盘命名为 /dev/my_backup_disk)、权限和所有者。
2、自动加载内核模块:当设备插入时,自动加载所需的驱动模块。
3、触发自定义脚本:设备插入时自动挂载文件系统、启动某个程序,或者发送通知。
- 依赖 systemd(在现代系统中):虽然早期
udev可以独立运行,但现在它已是systemd项目的一部分,并由systemd-udevd服务管理,与整个系统初始化过程深度集成。
工作流程简述
-
内核检测到硬件变化(新增或移除)。
-
内核发送一个
uevent到用户空间。 -
systemd-udevd守护进程接收到该事件。 -
udev根据规则文件(rules.d)中的规则匹配事件信息(如设备类型、厂商ID、产品ID等)。 -
根据匹配的规则,
udev在/dev下创建或删除设备节点,并可能执行其他操作(如运行脚本、设置权限等)。
mdev - 嵌入式系统的轻量级选择
mdev 是 mini-udev 的缩写,它是 BusyBox 工具集提供的一个精简版的设备管理工具。BusyBox 被称为“嵌入式 Linux 的瑞士军刀”,它将许多常用 Unix 工具打包成一个单一的可执行文件。
核心特点
-
极度轻量级:
mdev的设计目标是体积小、依赖少、资源占用低,非常适合资源受限的嵌入式系统或极简环境。 -
也运行在用户空间:和
udev一样,它也是用户空间程序。 -
配置简单:它的配置通常只有一个文件:
/etc/mdev.conf。这个文件的语法比udev的规则简单得多,主要用于设置设备节点的权限、所有者和偶尔执行简单的命令。 -
需要内核支持 uevent_helper:
mdev的工作方式与udev不同。它依赖于内核的uevent_helper机制。当内核发生uevent时,如果/sys/kernel/uevent_helper文件被设置了(例如设置为/sbin/mdev),内核就会直接执行这个helper程序(即mdev),并将事件细节作为环境变量传递给它。这是一种更简单但效率较低的方式。
工作流程简述
- 系统启动时,初始化脚本(通常是
/etc/init.d/rcS)会执行以下两行关键命令:
echo /sbin/mdev > /proc/sys/kernel/hotplug # 设置热插拔助手程序为 mdev
mdev -s # 在启动时扫描 /sys 并初始化 /dev 目录
-
内核检测到热插拔事件。
-
内核查看
hotplug设置,发现是/sbin/mdev。 -
内核直接调用
/sbin/mdev程序来处理这个事件。 -
mdev根据/etc/mdev.conf中的配置,创建或删除/dev下的设备节点。
udev/mdev 对比总结
| 特性 | udev | mdev |
|---|---|---|
| 全称 | Userspace /dev | mini-udev |
| 来源 | systemd 项目 | BusyBox 工具集 |
| 体积/资源 | 较大,功能完整 | 极小,非常轻量 |
| 应用场景 | 桌面、服务器、移动设备等现代通用 Linux 系统 | 资源紧张的嵌入式系统、Initramfs 早期用户空间 |
| 配置方式 | 复杂的规则文件(/etc/udev/rules.d/*.rules) | 简单的配置文件(/etc/mdev.conf) |
| 通信机制 | 通过 netlink 套接字主动监听内核事件 | 通过内核的 uevent_helper 机制被内核调用 |
| 功能 | 极其强大(动态节点、模块加载、触发脚本等) | 基础(创建节点、设置权限) |
| 依赖 | 依赖 systemd 和较完整的用户空间环境 | 只依赖 BusyBox,几乎可在任何环境运行 |
二、使用udev 挂载U盘和TF卡
配置buildroot文件系统支持udev
我们编写了一个名为 mdev 的应用程序, 用来处理 uevent 事件, 而实际上 udev和 mdev 的可执行程序都是很复杂的, 也并不需要我们自己来写, 只需要在构建 buildroot 文件系统时勾选对应的选项即可。 首先来到 buildroot 的源码目录下。

然后使用以下命令加载 rk3568 的默认配置文件并进入到图形化配置界面
make rockchip_rk3568_defconfig
make menuconfig

最终实现如下效果配置:

最终烧写固件镜像,查看是否已经启动了udev: 命令如下
ps -aux | grep -nR udev

检查到/sbin/udevd 进程就表示当前系统使用的是 udev, 至此配置 buildroot 文件系统支持udev 就完成了
使用 udev 挂载U盘和TF步骤
先总结 给出 挂载的步骤,从大方向上面先了解一下步骤。
udev挂载U盘步骤
| 步骤编号 | 主要步骤 | 关键内容/命令示例 | 简要说明 |
|---|---|---|---|
| 1 | 了解基础 | udev 规则,sysfs | udev 运行在用户空间,监听内核事件,并根据规则响应。 |
| 2 | 创建 udev 规则文件 | ACTION==“add”, KERNEL==“sd[b-z]?*”, … 在 /etc/udev/rules.d/ 目录下创建 .rules 文件定义规则。 | |
| 3 | 编写挂载/卸载脚本 | mount -t vfat /dev/$1 /mnt/usb | 脚本需可执行,负责挂载(创建目录)和卸载(卸载并删除目录)操作。 |
| 4 | 设置脚本权限 | chmod +x /path/to/your/script.sh | 确保 udev 能够执行你编写的脚本。 |
| 5 | 重载 udev 规则 | sudo udevadm control --reload-rules | 使新的规则文件生效23。 |
| 6 | 调试与测试 | udevadm monitor --property | 插入和拔出U盘,使用 mount, lsblk 检查挂载情况。 |
udev挂载TF步骤
| 步骤编号 | 核心步骤 | 关键内容/命令示例 | 要点说明 |
|---|---|---|---|
| 1 | 识别设备属性 | udevadm monitor --property | 插入TF卡,观察其属性,找到 SUBSYSTEM 和 KERNEL 等关键标识。 |
| 2 | 创建 udev 规则文件 | ACTION==“add”, SUBSYSTEM==“block”, … | 在 /etc/udev/rules.d/ 下创建 .rules 文件,编写匹配TF卡的规则。 |
| 3 | 编写挂载/卸载脚本 | mount /dev/$1 /mnt/tfcard | 脚本需可执行,负责安全地挂载(创建目录)和卸载(卸载并删除目录)。 |
| 4 | 设置脚本权限 | chmod +x /path/to/script.sh | 确保 udev 有权限执行你的脚本。 |
| 5 | 重载并测试规则 | sudo udevadm control --reload-rules | 重新加载规则并触发事件进行测试。 |
| 6 | 调试与验证 | journalctl -f 或 tail -f /var/log/syslog | 查看系统日志,使用 lsblk 或 mount 命令验证挂载是否成功。 |
使用udev 挂载U盘
这里举例,简单实操,实际这些步骤都是写在系统里面提前配置好的。
创建 udev 规则文件
udev 规则文件通常存放在 /etc/udev/rules.d/ 目录下,文件通常以数字开头(如 99-usb-mount.rules),数字越小优先级越高。
比如我们创建一个 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"
-
ACTION=="add": 表示设备添加事件。 -
KERNEL=="sd[a-z][0-9]*": 匹配设备名称,如sda1, sdb1等。 -
SUBSYSTEMS=="block": 确认设备属于USB子系统。 -
RUN+="...": 指定要执行的脚本,%k 是内核提供的设备名(如sda1)。 -
规则文件命名时,数字越小优先级越高。更详细的匹配键(如
ENV{ID_FS_TYPE}=="vfat"匹配文件系统类型)可以让规则更精确

编写挂载/卸载脚本
上面规则001.rules 文件里面已经配置了 不同action 下的路径,那么我们在对应的文件夹下创建执行文件并编写脚本即可
[root@topeet:/etc/udev/rules.d]# cd usb
[root@topeet:/etc/udev/rules.d/usb]# ls -l
total 8
----rwxrwx 1 root root 42 Aug 27 11:32 usb-add.sh
----rwxrwx 1 root root 35 Aug 27 11:32 usb-remove.sh
[root@topeet:/etc/udev/rules.d/usb]#
[root@topeet:/etc/udev/rules.d/usb]#
[root@topeet:/etc/udev/rules.d/usb]# cat usb-add.sh
#!/bin/sh
/bin/mount -t vfat /dev/$1 /mnt
[root@topeet:/etc/udev/rules.d/usb]#
[root@topeet:/etc/udev/rules.d/usb]#
[root@topeet:/etc/udev/rules.d/usb]# cat usb-remove.sh
#!/bin/sh
sync
/bin/umount -l /mnt
[root@topeet:/etc/udev/rules.d/usb]#
[root@topeet:/etc/udev/rules.d/usb]#
具体来说, usb-add.sh 脚本和 usb-remove.sh 脚本如下:
#!/bin/sh
/bin/mount -t vfat /dev/$1 /mnt/
#!/bin/sh
sync
/bin/umount -l /mnt/
设置脚本权限
我们是自己调试,拿着机器直接输入命令:在 /etc/udev/rules.d/usb 目录下:
chmod 777 *
如下结果:

重载 udev 规则并触发新规则
让新规则生效:
sudo udevadm control --reload-rules # 重新加载规则文件
sudo udevadm trigger # 可选:重新触发所有设备事件,立即测试规则
或者直接重启机器即可
插拔U盘,看实际结果

使用udev 挂载TF卡
将 TF 卡挂载到/mnt 目录下, 在不做任何修改的情况下, 直接插入TF卡, 会发现 TF 卡直接挂载到了/mnt/sdcard 目录,如下:

这是因为在/lib/udev/rules.d 目录下已经帮我们添加了很多的 udev 规则, 如下图所示

这里的规则文件跟我们上一小节自己创建的规则文件所实现的作用是相同的, 只是 /etc/udev/rules.d/目录的规则文件比/lib/udev/rules.d 目录的规则文件优先级高。
所以要实现 TF 卡自动挂载到/mnt 命令让就需要进入到 /etc/udev/rules.d/目录下, 这次我们创建一个名为 002.rules 的文件

创建 udev 规则文件 002.rules
KERNEL=="mmcblk[0-9]p[0-9]", SUBSYSTEM=="block", ACTION=="add", RUN+="/etc/udev/rules.d/tf/tf-add.sh %k"
SUBSYSTEM=="block", ACTION=="remove", RUN+="/etc/udev/rules.d/tf/tf-remove.sh"
加载、卸载脚本 tf-add.sh 、tf-remove.sh
tf-add.sh
#!/bin/sh
/bin/mount -t vfat /dev/$1 /mnt
tf-remove.sh
#!/bin/sh
sync
/bin/umount -l /mnt
授权可执行脚本
进入脚本文件夹: /etc/udev/rules.d/tf 批量授权 chmod 777 * 结果如下

重载 udev 规则并触发新规则
让新规则生效:
sudo udevadm control --reload-rules # 重新加载规则文件
sudo udevadm trigger # 可选:重新触发所有设备事件,立即测试规则
或者直接重启机器即可
插拔TF卡 验证结果
- 拔出
TF卡。 - 重新插入
TF卡。 - 使用
lsblk、mount或df -h命令检查TF卡是否已自动挂载到你指定的目录。 - 使用
sudo tail -f /var/log/syslog或journalctl -f查看系统日志,检查是否有来自你的脚本的日志或任何错误信息。
如下,看实际结果:

三、使用mdev 挂载U盘和TF卡
上面udev 挂载U盘和TF卡已经举例说明,这里其实就是方式不一样,更简单而已,也是基于配置来的。
mdev 环境搭建及验证
对于环境配置,配置menuconfig 系统支持mdev 以及支持busybox 这里暂不讲解,可以自行查阅资料实现。
验证命令 ps -aux | grep -nR mdev

检查到/sbin/mdev 进程就表示当前系统使用的是 mdev, 至此配置 buildroot 文件系统支持mdev 就完成了。
mdev 挂载U盘
配置规则文件
mdev 使用/etc/mdev.conf 文件来配置 mdev 工具的规则和行为, 要想使用 mdev 自动挂载 U 盘需要向/etc/mdev.conf 文件中添加以下两条规则,
sd[a-z][0-9] 0:0 666 @/etc/mdev/usb_insert.sh
sd[a-z] 0:0 666 $/etc/mdev/usb_remove.sh
下面是对上述两个规则的详细介绍:
- (1)
sd[a-z][0-9]是一个正则表达式模式, 用于匹配以"sd"开头, 后跟一个小写字母和一
个数字的设备节点, 例如/dev/sda1、 /dev/sdb2等。 - (2)
0:0 666表示设置设备节点的所有者和权限。0:0表示所有者和所属组的用户ID和
组ID均为 0, 即root用户。 666 表示权限为可读可写。 - (3)
@/etc/mdev/usb_insert.sh表示当符合规则的设备插入时,mdev会执行/etc/mdev/u sb_insert.sh脚本。 @ 符号表示执行的是一个shell命令。 - (4)
$/etc/mdev/usb_remove.sh表示当符合规则的设备移除时,mdev会执行/etc/mdev/ usb_remove.sh脚本。 $ 符号表示执行的是一个内部命令。
默认的/etc/mdev.conf 本身就是有一些规则的,继续添加两条规则

如下:

加载和卸载脚本并配置到系统
规则添加完成之后就要去对应的目录下添加 usb_insert.sh 和 usb_remove.sh 脚本文件了,
首先创建/etc/mdev目录并进入到该目录创建 usb_insert.sh 和 usb_remove.sh 并授权

脚本如下:
usb_insert.sh
#!/bin/sh
if [ -d /sys/block/*/$MDEV ]; then
mount /dev/$MDEV /mnt
sync
fi
usb_remove.sh
#!/bin/sh
sync
umount -l /mnt
查看实际验证结果
插入U盘之前和插入U盘之后,结果如下:


可以看到 U 盘 sda1 就成功挂载到了/mnt 目录,我们在拔出U盘 挂载的U盘就会消失 了。
mdev 挂载TF 卡
配置规则文件
我们实现了 U 盘的自动挂载, 而为了帮助同学们举一反三, 本小节要使用 mdev 实现 TF 卡的自动挂载,跟 U 盘自动挂载相同, TF 卡自动挂载也需要向/etc/mdev.conf 文件中添加以下两条类似的规则
mmcblk[0-9]p[0-9] 0:0 666 @/etc/mdev/tf_insert.sh
mmcblk[0-9] 0:0 666 $/etc/mdev/tf_remove.sh
每一行都是一个规则, 具有以下格式:
<设备节点正则表达式> <设备的所有者:设备的所属组> <设备的权限> <设备插入或移除时需要执行的命令>
下面是对上述两个规则的详细介绍:
- (1)
mmcblk[0-9]p[0-9]是一个正则表达式模式, 用于匹配以"mmcblk"开头的TF卡块设
备, 例如/dev/mmcblk1p1等。 - (2)
0:0 666表示设置设备节点的所有者和权限。0:0表示所有者和所属组的用户ID和
组ID均为 0, 即 root 用户。 666 表示权限为可读可写。 - (3)
@/etc/mdev/tf_insert.sh表示当符合规则的设备插入时,mdev会执行/etc/mdev/tf_i nsert.sh脚本。 @ 符号表示执行的是一个shell命令。 - (4)
$/etc/mdev/tf_remove.sh表示当符合规则的设备移除时,mdev会执行/etc/mdev/tf_remove.sh脚本。$符号表示执行的是一个内部命令。
配置加载、卸载脚本
root@RK356X:/]# cd /etc/
[root@RK356X:/etc]# cd mdev/
[root@RK356X:/etc/mdev]# ls -l
total 16
-rw-r--r-- 1 root root 75 Aug 27 20:07 tf_insert.sh
-rw-r--r-- 1 root root 35 Aug 27 20:07 tf_remove.sh
-rwxrwxrwx 1 root root 84 Aug 2 2024 usb_insert.sh
-rwxrwxrwx 1 root root 31 Aug 2 2024 usb_remove.sh
[root@RK356X:/etc/mdev]# cat tf_insert.sh
#!/bin/sh
if [ -d /sys/block/*/$MDEV ]; then
mount /dev/$MDEV /mnt
sync
fi
[root@RK356X:/etc/mdev]# cat tf_remove.sh
#!/bin/sh
sync
/bin/umount -l /mnt
[root@RK356X:/etc/mdev]#
即如下:
tf_insert.sh
#!/bin/sh
if [ -d /sys/block/*/$MDEV ]; then
mount /dev/$MDEV /mnt
sync
fi
tf_remove.sh
#!/bin/sh
sync
/bin/umount -l /mnt
TF插拔验证


总结
- 这里才是热插拔的一个实际案例,实际简单的案例
- 搞清楚
udev/mdev这才是热插拔机制核心的应用机制 - 通过实际案例
udev/mdev实现U盘和TF卡挂载卸载 - 这里只是简单的规则,加载和卸载,实际应用中会有一定的比较全的机制 但核心思想就这些
2211

被折叠的 条评论
为什么被折叠?



