RK3576开发板外设管理:Udev实现U盘自动挂载的标准化方案
Forlinx
2026-03-05 14:44:00
RK3576开发板
项目背景与需求
本次项目是基于RK3576开发板(型号:OK3576-C)开发自助终端设备,客户核心需求是 系统启动后插入U盘可自动挂载到固定目录,且目录名无乱码。Ubuntu 24.04桌面版自带的文件管理器(如nautilus)虽支持U盘自动挂载,但挂载点路径会包含UUID或中文卷标,在终端操作时极易出现乱码,直接干扰后续自动化脚本的执行。
由于终端环境对路径的 固定性和可预测性要求极高,因此决定抛弃文件管理器的默认挂载机制,改用udev接管U盘的插拔事件,自定义挂载/卸载脚本,实现标准化的U盘挂载管理。
实现思路与核心逻辑
首先明确核心实现思路:udev是Linux内核设备管理器的用户空间守护进程,核心负责管理/dev目录下的设备节点,并能在设备插入/移除时触发自定义动作。我们的核心目标是—— U盘插入时自动执行挂载脚本,U盘拔出时自动执行卸载脚本。
需要注意的是,Ubuntu 24.04这类高版本系统中,直接在udev规则里调用shell脚本大概率会失效:一方面udev事件触发的运行环境极度受限,另一方面脚本执行为异步模式,易出现竞争条件导致执行失败。Linux官方推荐的最优方案是 通过systemd服务包装脚本,在udev规则中通过TAG+="systemd"和ENV{SYSTEMD_WANTS}触发服务,让脚本在systemd管理的干净环境中运行,从根本上规避权限、路径等问题。
基于此,梳理出本次改造需要修改/新增的核心文件,所有操作均基于飞凌RK3576开发板的Ubuntu 24.04环境:
- /etc/udev/udev.conf:确认udev基础配置,指向正确的规则目录;
- /etc/udev/rules.d/95-usb-mount.rules:编写udev核心规则,捕获U盘分区插拔事件,触发对应systemd服务;
- /etc/systemd/system/mount.service/umount.service:创建两个systemd服务单元,分别承载U盘挂载、卸载逻辑;
- /etc/systemd/system/mount.sh/umount.sh:编写实际的挂载、卸载脚本,由上述systemd服务调用。
分步实操实现方案
以下为分步实现步骤,所有配置命令、脚本代码均经过实际环境验证,直接复制即可使用,仅在格式上做缩进和注释优化,未改动任何核心参数。
确认udev基础配置
首先检查并确认/etc/udev/udev.conf配置文件,确保udev的规则目录、设备根目录指向正确,无需新增配置,仅启用必要项即可:
# /etc/udev/udev.conf # 参考udev.conf(5)获取详细配置说明 # udevd会在initrd中启动,修改此文件后建议重建initrd使配置生效 #udev_log=info #children_max= #exec_delay= #event_timeout=180 #timeout_signal=SIGKILL #resolve_names=early # 设备根目录 udev_root="/dev/" # udev规则文件存放目录 udev_rules="/etc/udev/rules.d/" # 日志级别设为错误,减少冗余日志 udev_log="err"
核心关注udev_rules参数,确保其指向/etc/udev/rules.d/,保持默认配置即可,无需额外修改。
编写udevU盘插拔检测规则
在/etc/udev/rules.d/目录下创建95-usb-mount.rules文件(95为规则优先级,数值越大优先级越低),编写U盘插拔的触发规则,核心实现 插入创目录+启挂载服务,拔出启卸载服务:
# /etc/udev/rules.d/95-usb-mount.rules
# 为U盘主设备(sd[a-z])创建符号链接usb%m,%m为内核次要设备号,保证设备标识唯一
KERNEL=="sd[a-z]", NAME="%k", SYMLINK+="usb%m", OPTIONS="last_rule"
# U盘分区(sd[a-z][0-9])插入事件:创建符号链接
ACTION=="add", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盘分区插入事件:自动创建挂载点目录,路径为/mnt/分区名(如/mnt/sda1)
ACTION=="add", KERNEL=="sd[a-z][0-9]", RUN+="/bin/mkdir -p /mnt/%k"
# U盘分区插入事件:核心触发挂载服务,仅匹配USB块设备,避免识别SATA等本地设备
ACTION=="add",KERNEL=="sd[a-z][0-9]",SUBSYSTEM=="block",ENV{ID_BUS}=="usb",TAG+="systemd", PROGRAM="/bin/systemd-escape -p %k", ENV{SYSTEMD_WANTS}+="mount.service"
# U盘分区移除事件:创建符号链接(保持与插入规则一致性,可省略)
ACTION=="remove", KERNEL=="sd[a-z][0-9]", SYMLINK+="usb%m", NAME="%k"
# U盘分区移除事件:触发卸载服务,仅匹配USB块设备
ACTION=="remove", KERNEL=="sd[a-z][0-9]", SUBSYSTEM=="block", ENV{ID_BUS}=="usb", PROGRAM="/usr/bin/systemctl start umount.service", TAG+="systemd"
规则关键说明:ENV{ID_BUS}=="usb"精准匹配USB设备,彻底排除本地SATA、NVMe等存储设备;RUN+="/bin/mkdir -p /mnt/%k"自动创建挂载点;TAG+="systemd"告诉udev通过systemd管理后续触发的服务。
创建systemd挂载/卸载服务单元
由于高版本Ubuntu无法直接在udev规则中执行脚本,因此创建两个systemd服务单元mount.service和umount.service,分别调用挂载、卸载脚本,服务文件存放于/etc/systemd/system/目录。
挂载服务:mount.service
# /etc/systemd/system/mount.service [Unit] # 服务描述 Description=mountservice # 服务启动时机:在网络服务后启动(挂载无需网络,仅为通用习惯,可省略) After=network.target [Service] # 服务类型:forking表示脚本会以后台进程模式运行 Type=forking # 执行用户/用户组:必须为root,挂载/卸载操作需要最高权限 User=root Group=root # 工作目录:挂载点根目录 WorkingDirectory=/mnt # 服务执行命令:调用挂载脚本,--log-target=journal让日志写入systemd日志,可通过journalctl查看 ExecStart=/etc/systemd/system/mount.sh start --log-target=journal [Install] # 服务安装目标:多用户模式下启用 WantedBy=multi-user.target
卸载服务:umount.service
# /etc/systemd/system/umount.service [Unit] # 服务描述 Description=umountservice # 服务启动时机:在网络服务后启动 After=network.target [Service] # 服务类型:forking表示脚本会以后台进程模式运行 Type=forking # 执行用户/用户组:root最高权限 User=root Group=root # 工作目录:挂载点根目录 WorkingDirectory=/mnt # 服务执行命令:调用卸载脚本 ExecStart=/etc/systemd/system/umount.sh start --log-target=journal [Install] # 服务安装目标:多用户模式下启用 WantedBy=multi-user.target
执行以下命令为两个服务文件添加可执行权限:
chmod 777 /etc/systemd/system/mount.service chmod 777 /etc/systemd/system/umount.service
编写挂载/卸载核心脚本
创建与systemd服务对应的挂载脚本mount.sh和卸载脚本umount.sh,存放于/etc/systemd/system/目录,脚本为实际执行U盘挂载、卸载的核心逻辑。
挂载脚本:mount.sh
#!/bin/bash
# /etc/systemd/system/mount.sh
# U盘自动挂载脚本,挂载路径/mnt/设备名(如/dev/sda1 → /mnt/sda1)
# 初始变量,后续会被遍历结果覆盖
var2="/mnt/sda"
# 遍历所有/dev目录下的USB分区设备
for V in $(ls /dev/sd[a-z][0-9])
do
# 打印当前设备名(调试用)
echo $V
# 赋值当前设备路径给变量
var2=$V
# 截取设备名(如从/dev/sda1中截取sda1)
echo ${var2:5:4}
# 执行挂载:将设备挂载到/mnt/设备名目录
/bin/mount $V /mnt/${var2:5:4}
done
脚本关键说明:挂载点由udev规则提前创建,脚本直接执行挂载即可;使用/bin/mount绝对路径执行,避免udev/systemd环境中PATH变量缺失导致的命令找不到问题。
卸载脚本:umount.sh
#!/bin/sh
# /etc/systemd/system/umount.sh
# U盘自动卸载脚本,检测设备不存在则卸载并删除挂载点
# 定义需监控的USB设备节点
usb_device_1="/dev/sda1"
usb_device_2="/dev/sdb1"
usb_device_3="/dev/sdc1"
usb_device_4="/dev/sdd1"
# 定义设备对应的挂载点目录
mount_dir_1="/mnt/sda1"
mount_dir_2="/mnt/sdb1"
mount_dir_3="/mnt/sdc1"
mount_dir_4="/mnt/sdd1"
# 延时1秒,等待设备彻底移除,避免竞争条件导致卸载失败
sleep 1
# 检测设备1是否存在,不存在则卸载并删除挂载点
if [ ! -e "$usb_device_1" ];then
umount $usb_device_1 > /dev/null 2>&1
rm -rf $mount_dir_1 > /dev/null 2>&1
fi
# 检测设备2是否存在,不存在则卸载并删除挂载点
if [ ! -e "$usb_device_2" ];then
umount $usb_device_2 > /dev/null 2>&1
rm -rf $mount_dir_2 > /dev/null 2>&1
fi
# 检测设备3是否存在,不存在则卸载并删除挂载点
if [ ! -e "$usb_device_3" ];then
umount $usb_device_3 > /dev/null 2>&1
rm -rf $mount_dir_3 > /dev/null 2>&1
fi
# 检测设备4是否存在,不存在则卸载并删除挂载点
if [ ! -e "$usb_device_4" ];then
umount $usb_device_4 > /dev/null 2>&1
rm -rf $mount_dir_4 > /dev/null 2>&1
fi
脚本关键说明:sleep 1为核心延时操作,等待内核完成设备移除的底层操作;> /dev/null 2>&1重定向所有输出和错误到空设备,保持系统日志干净。
执行以下命令为两个脚本添加可执行权限:
chmod 777 /etc/systemd/system/mount.sh chmod 777 /etc/systemd/system/umount.sh
重载配置并测试验证
完成所有配置和脚本编写后,无需重启系统,手动重载udev规则和systemd配置即可生效:
# 重载udev规则,让新编写的U盘插拔规则生效 udevadm control --reload-rules # 重载systemd配置,让新创建的挂载/卸载服务生效 systemctl daemon-reload
测试步骤:
- 插入U盘(单分区/多分区均可),执行ls /mnt/,可看到自动创建的挂载点目录(如sda1),执行df -h可验证U盘已成功挂载;
- 拔出U盘,执行ls /mnt/,对应的挂载点目录已被自动删除,执行df -h可验证U盘已成功卸载;
- 若需查看服务运行日志,可执行journalctl -u mount.service或journalctl -u umount.service。
验证总结
在飞凌RK3576开发板的Ubuntu 24.04桌面版环境中,经过多次实际插拔测试,该方案可实现 U盘插入自动挂载到/mnt/设备名固定目录,拔出自动卸载并删除挂载点,挂载点路径无任何UUID或中文卷标,彻底解决了默认挂载的乱码问题,完全满足自助终端设备的自动化脚本操作需求。
执行以下调试命令,可验证挂载/卸载服务是否已正常启用:
# 查看挂载服务启用状态 systemctl is-enabled mount.service # 查看卸载服务启用状态 systemctl is-enabled umount.service
正常情况下,两个命令的输出均为enabled,表示服务已成功启用并随系统开机自启。
经验反思
本次基于udev+systemd实现U盘自动挂载,踩过了高版本Ubuntu的环境坑,也积累了嵌入式Linux外设管理的实用经验,核心总结三点:
对于飞凌嵌入式RK3576开发板,Ubuntu 24.04桌面版的整体运行表现稳定,结合udev的设备事件监听和systemd的服务管理,可灵活定制USB、串口、网卡等各类外设的自动处理逻辑。本次U盘自动挂载的改造方案,也为后续嵌入式项目中的外设管理提供了可直接复用的模板。
华北区负责人
华东区负责人
华南区负责人
中西区负责人
相关产品 >
-
FET3576-C核心板
飞凌嵌入式RK3576核心板集成了强大的处理器和丰富的接口,提供出色的计算能力和扩展性。RK3576核心板以其卓越的性能、低功耗和稳定性,成为工业、AIoT、边缘计算、智能移动终端等领域的理想选择。无论是数据处理还是边缘计算,RK3576都能为项目提供强大的硬件支持。核心板推荐选择飞凌嵌入式瑞芯微系列RK3576J业级核心板、RK3576高性能核心板。 了解详情
-
OK3576-C开发板
RK3576开发板CPU选用瑞芯微RK3576,采用核心板+底板分体式设计,采用4个100Pin板对板连接器的方式将处理器的功能引脚以最便利的方式全部引出,并针对不同的功能做了深度优化,方便用户二次开发的同时简化用户设计,为您的项目提供良好的评估及设计依据。RK3576是瑞芯微专为AIoT市场打造的一款高算力、高性能、低功耗的国产化应用处理器,集成了4个ARM Cortex-A72和4个 ARM Cortex-A53高性能核;内置6TOPS超强算力NPU;嵌入式3D GPU加之带有MMU的专用2D硬件引擎,最大限度提升显示性能;H.265超清硬解码,最高支持8K分辨率。 了解详情

