应监听subsystem=="block"且devtype=="disk",校验attrs{removable}=="1"和env{id_bus}=="usb",规则仅触发轻量动作(如写标记或启动systemd服务),挂载逻辑交由具备完整环境的systemd服务执行。

要实现文件系统挂载的实时监控,核心是让系统在U盘、移动硬盘等块设备插拔瞬间就感知并响应,而不是靠轮询或定时检查。这必须依赖内核与用户空间协同机制——udev 是最稳定、最轻量、也最贴近内核事件的方式。
监听 block 子系统,不是 usb
USB设备插拔在内核中分两个阶段:先是 USB 总线层面识别(SUBSYSTEM=="usb"),但此时存储功能尚未就绪;真正能挂载的是后续生成的块设备(如 /dev/sdb),属于 block 子系统。所以规则必须匹配:
- SUBSYSTEM=="block" —— 只关注存储类设备
- DEVTYPE=="disk" —— 排除分区(sdb1)、LVM逻辑卷等,只抓整块磁盘
- ATTRS{removable}=="1" —— 确保是可移动介质,过滤掉内置硬盘
- ENV{ID_BUS}=="usb" —— 进一步限定为 USB 总线设备(比 vendor/product 更通用)
规则写法要兼顾安全与可用性
直接在 RUN 中执行 mount/umount 容易失败,因为 udev 环境极简(无 PATH、无会话、无挂载点就绪保障)。推荐做法是“解耦触发与执行”:
- 规则只做轻量动作:例如写标记文件
RUN+="/bin/touch /run/usb-arrived"或触发 systemd 服务RUN+="/usr/bin/systemctl start usb-handler@%k.service" - 实际挂载逻辑交给 systemd 服务:它有完整环境、可设依赖(如 After=local-fs.target)、支持日志和重启策略
- 若坚持在规则中挂载,务必加校验:
ENV{ID_FS_TYPE}!="", ENV{ID_FS_TYPE}!="ntfs"避免空文件系统或 NTFS 权限问题
调试必须用 udevadm,别靠重启
写完规则别急着拔插测试,先验证是否真被识别:
- 监听实时事件:
udevadm monitor --subsystem-match=block,插入U盘看是否有add@/devices/.../sdb输出 - 模拟触发规则:
udevadm test $(udevadm info -q path -n /dev/sdb),它会打印所有匹配规则、变量展开结果和 RUN 命令是否被解析 - 查设备属性:
udevadm info --name=/dev/sdb --attribute-walk | grep -E "(removable|ID_BUS|ID_VENDOR)",确认你写的匹配条件有对应值
挂载后自动通知应用层
如果上层程序(比如文件管理器、备份工具)需要知道挂载完成,不要让它轮询 /proc/mounts。更可靠的方式是:
- 在挂载脚本末尾发 D-Bus 信号:
dbus-send --system --type=signal /com/Example/Mount com.Example.Mount.Changed string:"/dev/sdb1" string:"/mnt/usb" string:"add" - 或写入 inotify 监控目录(如 /run/media/$USER/),让应用监听该路径的 create/remove 事件
- 避免用 notify-send 等依赖桌面会话的命令——udev 运行在 system context,没 DISPLAY 和 DBUS_SESSION_BUS_ADDRESS










