Linux 磁盘热插拔与自动挂载实践

2次阅读

udev规则错误导致设备不触发挂载,因匹配条件不当(如缺attr{removable}==”1″);systemd-mount报“no media found”是因未格式化或无卷标;需用唯一id挂载并加锁防冲突,且必须显式设置uid/gid和umask权限。

Linux 磁盘热插拔与自动挂载实践

udev 规则写错导致设备插入后不触发挂载

udev 是 linux 下处理热插拔事件的核心机制,规则写错就等于告诉系统“别管这个设备”。最常见的是匹配条件太宽或太窄:SUBSYSTEM=="block" 必须有,但光有它不够;漏掉 ATTR{removable}=="1"ENV{ID_BUS}=="usb",U 盘可能被当成内置盘忽略。

实操建议:

  • 先用 udevadm monitor --subsystem-match=block 插拔设备,确认事件是否发出;再用 udevadm info -n /dev/sdb(替换成你的设备节点)查出真实属性值,照抄进规则,别凭经验写
  • 规则文件必须放在 /etc/udev/rules.d/99-usb-mount.rules 这类带数字前缀的路径,且权限为 0644,否则 udevd 不加载
  • 写完运行 sudo udevadm control --reload-rules && sudo udevadm trigger --subsystem-match=block,但注意:trigger 不会重放已拔出的设备,得真插一次
  • 调试时在规则里加 RUN+="/bin/sh -c 'logger udev-test: %p'",然后 journalctl -t kernel -g udev-test 看有没有日志,比盲猜快得多

systemd-mount 挂载失败报 “No media found”

这个错误不是磁盘坏了,而是 systemd-mount 默认跳过未格式化或无文件系统的块设备。它比 mount 更严格,不会尝试挂载空分区或 FAT32 但未打标(no label)的 U 盘。

实操建议:

  • 先确认分区有文件系统:sudo blkid /dev/sdb1,如果输出为空,说明没格式化或签名损坏,用 sudo mkfs.vfat -F32 /dev/sdb1 重建(注意备份)
  • 如果已有文件系统但没卷标,sudo fatlabel /dev/sdb1 MYUSBsudo e2label /dev/sdb1 MYUSB 加个 label,systemd-mount 就能识别
  • 挂载命令别直接用 systemd-mount /dev/sdb1,改用 systemd-mount --no-block --automount=yes /dev/sdb1,避免阻塞和权限问题
  • 挂载点默认在 /run/media/$USER/xxx,但该目录由 udisks2 管理;如果系统没装 udisks2,得自己建目录并确保 $USER 有写权限,否则 mount 会静默失败

自动挂载后普通用户无法读写文件

根源是挂载选项没设对。Linux 默认用 uid=0,gid=0 挂载,即使你用 systemd-mount,若没显式指定 -o uid=1000,gid=1000,所有文件属主都是 root,普通用户只能看不能删。

实操建议:

  • 在 udev 规则里调用 systemd-mount 时,必须带上完整选项:RUN+="/usr/bin/systemd-mount --no-block --automount=yes -o uid=%G,gid=%G,umask=000,fmask=113,dmask=002 /dev/%k"%G 是当前登录用户的 gid,%k 是内核名如 sdb1)
  • 别信 “defaults” 选项 —— 它在不同文件系统下行为不同,FAT32 下 umask 控制权限,ext4 下得靠 uid/gid,混用必出问题
  • 如果用 /etc/fstab 配合 autofs,注意 user 选项只允许挂载者卸载,不解决读写权限;真正起作用的是 uid=1000,gid=1000fmask/dmask
  • 挂载后立刻检查:ls -ld /run/media/$USER/*,确认目录属主是自己;再 touch /run/media/$USER/xxx/testfile 实测写入,别只看 ls 权限位

多个 USB 设备同时插拔引发挂载冲突

udev 事件是异步的,两个 U 盘几乎同时插入时,规则可能并发执行,导致 systemd-mount 尝试挂载同一个临时路径,或 blkid 查到的设备名(如 /dev/sdb)在执行中途被重分配。

实操建议:

  • 永远用设备的唯一标识符,而不是 /dev/sdX。在 udev 规则中优先使用 SYMLINK+="usb-%E{ID_SERIAL_SHORT}",然后挂载 /dev/disk/by-id/usb-xxx 路径
  • 在 RUN 命令中加简单锁:RUN+="/bin/sh -c 'mkdir /run/usb-mount-lock-%E{ID_SERIAL_SHORT} 2>/dev/NULL || exit 1; systemd-mount ... ; rmdir /run/usb-mount-lock-%E{ID_SERIAL_SHORT}'",利用 mkdir 原子性防并发
  • 避免在规则里做耗时操作(比如调用 blkid 多次),全用 udev 自带的 ENV{ID_FS_TYPE}ENV{ID_FS_LABEL} 变量,它们已在上一阶段解析好
  • 如果设备反复出现 “device busy” 错误,大概率是前一次挂载没 clean up —— 检查 systemctl --user list-units | grep mount,手动 stop 对应的 dev-disk-byx2did-xxx.device 单元再试

热插拔看着是插上线就完事,实际卡点全在 udev 的时机、systemd-mount 的策略、以及文件系统层对“可挂载”的定义差异。少一个 ENV{ID_BUS}=="usb",或者多一个没删的锁目录,都够你对着 journalctl 发十分钟呆。

text=ZqhQzanResources