OpenEuler操作系统中检测插入的USB设备并自动挂载
项目需求:工控机上openeuler操作系统是无界面版本的,在工控机上连接了激光雷达,当激光雷达采集完数据,我们要将采集数据导入u盘,故需要在工控机上插入u盘,操作系统自动检测并挂载u盘,进而导出数据到u盘
整体原理:
U盘插拔 -> 触发udev规则 -> 开启systemd服务 -> 调用实际挂载/卸除脚本
1.硬件连接与内核识别
当 U 盘插入工控机的 USB 接口时,内核会自动识别该 USB 设备,为其分配块设备节点(如/dev/sda,其中sda1为第一个分区,即/dev/sda1)。内核会记录设备的基本信息(如文件系统类型ext4、UUID 等,可通过blkid命令查看)。
2.udev 监测设备事件并触发规则
内核识别设备后,udev(设备管理守护进程) 会监测到 "设备添加" 事件(ACTION=="add"),并匹配你配置的/etc/udev/rules.d/99-usb-mount.rules规则:
规则中SUBSYSTEM=="block"指定只处理块设备(U 盘属于块设备),KERNEL=="sd[a-z][0-9]"匹配 U 盘分区(如sda1、sdb2等)。
当事件匹配时,udev 会通过TAG+="systemd"和ENV{SYSTEMD_WANTS}="usb-mount@%k.service"通知 systemd,触发名为usb-mount@%k.service的服务(%k会替换为实际设备名,如sda1)。
创建udev 规则
自动挂载udev规则
bash
sudo vi /etc/udev/rules.d/99-usb-mount.rules
写入以下内容
bash
ACTION=="add", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]", TAG+="systemd", ENV{SYSTEMD_WANTS}="usb-mount@%k.service"
ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]", RUN+="/bin/logger 'udev remove event: device=%k, env=$env{}'", TAG+="systemd", ENV{SYSTEMD_WANTS}="usb-umount@%k.service"
自动卸除udev规则
bash
sudo vi /etc/udev/rules.d/99-usb-umount.rules
写入以下内容
bash
ACTION=="remove", SUBSYSTEM=="block", KERNEL=="sd[a-z][0-9]", ENV{SYSTEMD_WANTS}="usb-umount@%k.service", RUN+="/usr/bin/logger udev remove event triggered for %k"
UDEV 规则的作用
udev 是 Linux 系统的设备管理框架,负责监测硬件设备的 "添加 / 移除" 事件,并根据规则执行预设操作。你配置的规则核心作用如下:
(1)事件匹配
ACTION=="add":只响应 "设备添加" 事件(即 U 盘插入时触发)。
SUBSYSTEM=="block":限定只处理块设备(U 盘、硬盘等存储设备,排除鼠标、键盘等 USB 设备)。
KERNEL=="sd[a-z][0-9]":精确匹配 U 盘分区(如sda1、sdb3,sd是 SCSI 磁盘的前缀,[a-z]是设备序号,[0-9]是分区号)。
(2)触发后续操作
TAG+="systemd":给事件添加systemd标签,让 systemd 接管事件处理(替代传统的RUN+直接执行命令,更可靠)。
ENV{SYSTEMD_WANTS}="usb-mount@%k.service":指定触发的 systemd 服务,%k是动态变量,自动替换为设备名(如sda1),即实际启动usb-mount@sda1.service。
(3)移除事件处理
ACTION=="remove":响应 U 盘移除事件,通过RUN+="/bin/logger ..."记录日志,并触发卸载服务usb-umount@%k.service,避免数据丢失。
3.systemd 服务执行挂载脚本
systemd 接收到 udev 的触发信号后,启动usb-mount@sda1.service(以sda1为例),执行服务中定义的挂载逻辑:
服务通过ExecStart=/root/workspace/auto_mount_usb.sh %I调用挂载脚本(%I传入设备名,如sda1)。
脚本需完成实际挂载操作:检查挂载点(如/mnt/usb)是否存在,执行文件系统检查(如e2fsck),最终通过mount命令将/dev/sda1挂载到/mnt/usb。
挂载成功 / 失败的日志会写入/var/log/usb_mount.log,便于排查问题(如你之前遇到的mount failed可在此日志中查看细节)。
创建systemd 服务
自动挂载usb的systemd服务
bash
vi /etc/systemd/system/usb-mount@.service
写入以下内容
bash
[Unit]
Description=Mount USB device to /mnt/usb
DefaultDependencies=no
After=local-fs.target
[Service]
Type=oneshot
ExecStart=/root/workspace/auto_mount_usb.sh %I
TimeoutSec=30
StandardOutput=append:/var/log/usb_mount.log
StandardError=append:/var/log/usb_mount.log
[Install]
WantedBy=sysinit.target
自动取消挂载usb的systemd服务
bash
vi /etc/systemd/system/usb-umount@.service
写入以下内容
bash
[Unit]
Description=Unmount USB device from /mnt/usb
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/root/workspace/auto_unmount_usb.sh %I
TimeoutSec=30
StandardOutput=append:/var/log/usb_mount.log
StandardError=append:/var/log/usb_mount.log
[Install]
WantedBy=sysinit.target
实际挂载脚本
bash
vi auto_mount_usb.sh
写入以下内容
bash
echo "Processing device node: $usb_dev"
if [[ ! -b "$usb_dev" ]]; then
echo "Device node $usb_dev does not exist or is not a block device"
exit 1
fi
mount_dir="/mnt/usb"
sudo rm -rf "$mount_dir"/* 2>/dev/null
sudo mkdir -p "$mount_dir" || {
echo "Failed to create mount directory: $mount_dir"
exit 1
}
udevadm settle
timeout=20
while ! blkid "$usb_dev" >/dev/null 2>&1 && [[ $timeout -gt 0 ]]; do
echo "Waiting for blkid to detect filesystem on $usb_dev... ($timeout s left)"
sleep 1
((timeout--))
done
if ! blkid "$usb_dev" >/dev/null 2>&1; then
echo "Device $usb_dev has no recognized filesystem"
exit 1
fi
fs_type=$(blkid -o value -s TYPE "$usb_dev")
echo "Detected filesystem type: $fs_type"
if mount | grep -q "^$usb_dev"; then
echo "Device $usb_dev is already mounted"
mount | grep "^$usb_dev"
exit 0
fi
if [[ "$fs_type" == ext* ]]; then
echo "Running e2fsck for $usb_dev"
e2fsck -fp "$usb_dev" || echo "e2fsck returned non-zero, continuing"
fi
uid=$(id -u)
gid=$(id -g)
case "$fs_type" in
ext*)
mount_options="defaults"
;;
ntfs)
mount_options="defaults,uid=$uid,gid=$gid,iocharset=utf8,utf8"
;;
exfat)
mount_options="defaults,uid=$uid,gid=$gid,iocharset=utf8,fmask=0022,dmask=0022"
;;
vfat)
mount_options="defaults,uid=$uid,gid=$gid,iocharset=utf8,fmask=0022,dmask=0022"
;;
*)
mount_options="defaults,uid=$uid,gid=$gid,iocharset=utf8"
;;
esac
echo "Mounting $usb_dev at $mount_dir with options: $mount_options"
if mount -t "$fs_type" -o "$mount_options" "$usb_dev" "$mount_dir"; then
echo "Mounted $usb_dev at $mount_dir"
df -hT "$mount_dir" | awk 'NR==1; NR>1{print}'
echo "To unmount: umount \"$mount_dir\""
else
echo "Mount failed for $usb_dev"
sudo rm -rf "$mount_dir"/* 2>/dev/null
exit 1
fi
echo "==========================="
实际取消挂载脚本
bash
vi auto_unmount_usb.sh
bash
#!/usr/bin/env bash
# USB auto-unmount script for fixed mount point
# Version: 1.1
# Date: 2025-06-09
export PATH=/usr/sbin:/usr/bin:/sbin:/bin
LOGFILE="/var/log/usb_mount.log"
exec >> "$LOGFILE" 2>&1
echo "==========================="
echo "[$(date '+%Y-%m-%d %H:%M:%S')] USB auto-unmount triggered"
usb_dev="$1"
if [[ -z "$usb_dev" ]]; then
echo "No device node provided to script"
exit 1
fi
mount_dir="/mnt/usb"
echo "Processing device node: $usb_dev"
if mountpoint -q "$mount_dir"; then
echo "Mount point $mount_dir is mounted, attempting to unmount"
cd /
if umount "$mount_dir"; then
echo "Unmounted successfully"
else
echo "Normal umount failed, trying lazy unmount"
if umount -l "$mount_dir"; then
echo "Lazy unmount succeeded"
else
echo "Lazy unmount failed"
exit 1
fi
fi
else
echo "Mount point $mount_dir not mounted or does not exist"
fi
if [ -d "$mount_dir" ]; then
echo "Cleaning up mount directory: $mount_dir"
rm -rf "${mount_dir:?}/"* 2>/dev/null || true
echo "Mount directory cleaned"
else
echo "Mount directory $mount_dir does not exist"
fi
echo "==========================="
systemd 服务(usb-mount@.service)的作用
systemd 是 Linux 的系统和服务管理器,负责管理进程生命周期。你配置的usb-mount@.service是一个 "模板服务"(通过@支持动态参数),核心作用如下:
(1)定义服务依赖与时机
After=local-fs.target:确保服务在本地文件系统初始化完成后启动,避免挂载点(如/mnt/usb)未就绪的问题。
DefaultDependencies=no:禁用默认依赖,避免服务被不必要的系统事件阻塞。
(2)执行挂载逻辑
Type=oneshot:服务为 "一次性任务"(挂载是瞬时操作,完成后自动退出)。
ExecStart=/root/workspace/auto_mount_usb.sh %I:调用挂载脚本,%I传入设备名(如sda1),脚本需实现具体的挂载逻辑(检查、挂载、日志等)。
(3)日志管理
StandardOutput=append:/var/log/usb_mount.log和StandardError=append:/var/log/usb_mount.log:将挂载过程的输出和错误日志写入指定文件,便于调试(如之前的mount failed可在此日志中查看具体原因)。
(4)模板化设计
服务名中的@允许传入动态参数(如usb-mount@sda1.service、usb-mount@sdb2.service),无需为每个 U 盘分区单独创建服务,简化配置。
4.加载配置
bash
sudo udevadm control --reload-rules
sudo systemctl daemon-reload
查看日志
bash
cat /var/log/usb_mount.log
5.测试
未插入U盘

插入u盘

再次拔出u盘,似乎自动卸除还有点问题,但不影响项目功能,留个bug [狗头],先不改了