USB挂载后执行脚本失败:udev触发异常的技术求助
你遇到的问题核心是udev版本229的行为变化——从这个版本开始,udev会在完成事件处理后,主动清理所有由它启动的子进程(包括后台运行的脚本片段),这就是为什么你看到sleep之后没有后续执行的原因。而旧版本udev(比如215、182)没有这个严格的清理机制,新版本(比如237)可能优化了进程生命周期管理,所以其他发行版能正常工作。
下面给你几个可行的解决方案:
方案1:让脚本进程脱离udev的控制组
修改你的脚本,使用nohup或者disown让后台进程脱离udev的进程树,避免被强制终止。调整后的脚本如下:
#!/bin/bash exec </dev/null >/tmp/key-inserted.log 2>&1; PS4=':$LINENO+'; set -x KEY_LABEL="DEV_JP" # 用nohup启动后台任务,彻底脱离udev环境 nohup bash -c ' KEY_LABEL="DEV_JP" cd /media/tuxin || exit for ((retries=0; retries<10; retries++)); do if [[ -d "$KEY_LABEL" && $(grep -q -e "$KEY_LABEL" /proc/mounts) ]]; then break fi sleep 1 done if [[ -d "$KEY_LABEL" ]]; then cd "$KEY_LABEL" || exit echo "SUCCESS" fi ' >/dev/null 2>&1 &
这里用nohup包裹核心逻辑,同时重定向输出到/dev/null,确保进程完全脱离udev的控制,不会被后续的清理操作终止。
方案2:改用systemd Path单元监控挂载点
如果udev的进程清理机制让你头疼,更可靠的方式是用systemd来监控USB挂载后的目录,替代udev直接运行脚本:
- 创建systemd路径单元文件
/etc/systemd/system/usb-key-monitor.path:
[Unit] Description=Monitor USB key mount point [Path] DirectoryNotEmpty=/media/tuxin/DEV_JP [Install] WantedBy=multi-user.target
- 创建对应的服务单元文件
/etc/systemd/system/usb-key-monitor.service:
[Unit] Description=Run script when USB key is mounted [Service] Type=oneshot User=tuxin ExecStart=/home/tuxin/insert-usbkey.sh
- 启用并启动这个单元:
sudo systemctl daemon-reload sudo systemctl enable --now usb-key-monitor.path
这样当/media/tuxin/DEV_JP目录出现内容(即USB挂载完成)时,systemd会自动触发你的脚本,完全避开udev的进程生命周期限制。
方案3:调整udev规则,延迟脚本执行
另一种思路是用at命令延迟执行脚本,让udev事件处理完成后再启动脚本:
修改你的udev规则,把RUN指令改成:
RUN+="/bin/su tuxin -c 'echo /home/tuxin/insert-usbkey.sh | at now + 10 seconds'"
(可以根据挂载速度调整延迟时间,比如1分钟改成now + 1 minute)
这样udev只是提交一个延迟任务,脚本会由atd服务在指定时间运行,完全不受udev的进程清理影响。
针对Linux Mint 18的额外说明
Linux Mint 18使用的udev 229是第一个引入严格进程清理的版本之一,后续发行版(比如Lubuntu 18.04的udev 237)可能对这个机制做了优化,或者允许通过某些方式保留子进程。上面的方案1和方案3是最直接的适配方案,方案2则是更现代、更可靠的长期解决方案。
内容的提问来源于stack exchange,提问作者Tuxin

