OpenEuler操作系统中检测插入的USB设备并自动挂载

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=="sda-z0-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=="sda-z0-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 狗头,先不改了

相关推荐
IT大白鼠18 分钟前
彭大帅的AI运维助手——自然语言管理 Linux 集群与网络设备——第 2 篇 · 安全守规矩的 AI:分级安全管控是灵魂
linux·运维·人工智能
风华同学22 分钟前
免密SSH登录Ubuntu
linux·运维·ubuntu
꯭自꯭闭꯭23 分钟前
达梦守护集群手工切换及故障切换
linux·运维·服务器·数据库
cuijiecheng20181 小时前
RK3588 DEP 接入 Dante Domain Manager(DDM)完整测试记录
linux
爱吃香菜的初学者1 小时前
十二.Linux——管道
linux·运维·服务器
网硕互联的小客服9 小时前
Linux服务器磁盘应该如何合理分区?
linux·运维·服务器
贵沫末10 小时前
Ubuntu——常用软件安装
linux·运维·ubuntu
Doraemomo11 小时前
Linux内核驱动开发——中断与定时器
linux·运维·驱动开发
2301_8084143811 小时前
Linux中动静态库的理解
linux·运维·服务器
M78佐菲12 小时前
ARM学习笔记(11)
linux·arm开发·笔记·嵌入式硬件·学习