1. 问题背景与现象描述
最近在多个嵌入式开发板上(包括地平线、树莓派和香橙派)配置CH340串口设备时,遇到了一个奇怪的问题。虽然设备能被系统识别,但通过udev规则创建的符号链接总是无法持久存在。
具体表现为:
- 执行
lsusb能正确显示CH340设备:Bus 004 Device 005: ID 1a86:7523 QinHeng Electronics CH340 serial converter - 编写的udev规则看似正确:
bash复制KERNEL=="ttyUSB*", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", MODE:="0777", SYMLINK+="myserial" - 重新加载规则后(
sudo udevadm control --reload-rules && sudo udevadm trigger),预期的/dev/myserial符号链接却不存在
2. 问题排查过程
2.1 监控udev事件
使用udevadm monitor命令观察设备插拔时的完整事件流:
bash复制sudo udevadm monitor --environment --udev
从监控日志中发现了一个关键现象:规则其实已经生效了,但设备被另一个服务瞬间"抢走"又删除了。
关键证据日志片段:
code复制DEVLINKS=/dev/serial/by-id/usb-1a86_USB_Serial-if00-port0 /dev/myserial /dev/serial/by-path/platform-fc8c0000.usb-usb-0:1:1.0-port0
...
UDEV [1838.588118] remove /devices/platform/fc8c0000.usb/usb4/4-1/4-1:1.0/ttyUSB1/tty/ttyUSB1 (tty)
2.2 定位罪魁祸首
深入分析日志后发现大量与BRLTTY相关的记录:
code复制BRLTTY_BRAILLE_DRIVER=bm
BRLTTY_BRAILLE_DEVICE=usb:vendor=0X1a86+product=0X7523+serial=
BRLTTY_PID_FILE=/var/run/brltty.pid
SYSTEMD_WANTS=brltty-udev.service
BRLTTY是一个为视障人士提供盲文显示支持的系统服务,它会自动扫描并尝试接管符合特定条件的串口设备。由于CH340的USB VID/PID被误识别为可能的盲文设备,导致BRLTTY介入后重置了设备状态。
3. 解决方案
3.1 彻底卸载BRLTTY(推荐方案)
对于大多数开发环境,BRLTTY并非必需服务,可以直接卸载:
bash复制sudo apt-get remove brltty
卸载后需要:
- 重新加载udev规则
bash复制sudo udevadm control --reload-rules sudo udevadm trigger - 重新插拔设备验证符号链接是否持久存在
3.2 选择性禁用BRLTTY(临时方案)
如果系统确实需要保留BRLTTY服务,可以通过以下方式临时解决:
bash复制sudo systemctl stop brltty-udev.service
sudo systemctl disable brltty-udev.service
3.3 修改BRLTTY配置(高级方案)
编辑BRLTTY配置文件/etc/brltty.conf,在[Braille]部分添加排除规则:
code复制braille-device exclude usb:vendor=0x1a86
然后重启服务:
bash复制sudo systemctl restart brltty
4. 深入技术原理
4.1 udev规则执行流程
完整的设备事件处理流程:
- 内核检测到硬件变动,发送uevent到用户空间
- udevd守护进程接收事件,匹配规则数据库
- 执行匹配规则中的指令(创建符号链接、设置权限等)
- 其他子系统(如BRLTTY)通过libudev监听事件
- 冲突发生时,最后执行的规则会覆盖先前的结果
4.2 服务抢占机制分析
BRLTTY通过两种方式介入设备管理:
- 通过
brltty-udev.service监听udev事件 - 直接调用libudev API注册回调函数
其处理优先级高于普通udev规则,导致我们的符号链接被覆盖。
5. 最佳实践建议
5.1 可靠的udev规则编写
建议在规则中添加更严格的匹配条件,减少误匹配:
bash复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", \
ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1", \
MODE:="0666", SYMLINK+="myserial"
关键改进:
- 添加
ENV{ID_MM_DEVICE_IGNORE}防止ModemManager干扰 - 明确指定子系统为
tty而不仅是KERNEL匹配 - 权限设置为0666而非0777(串口设备不需要执行权限)
5.2 系统服务管理建议
对于嵌入式开发环境,建议禁用以下可能干扰串口设备的服务:
bash复制sudo systemctl disable --now brltty-udev.service
sudo systemctl disable --now ModemManager.service
5.3 多设备环境处理
当系统连接多个相同型号的串口设备时,可通过序列号区分:
bash复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", \
ATTRS{serial}=="你的设备序列号", \
SYMLINK+="myserial_%s{serial}"
获取序列号方法:
bash复制udevadm info -a -n /dev/ttyUSB0 | grep '{serial}' | head -n1
6. 验证与测试
6.1 验证符号链接
成功创建后应能通过以下方式验证:
bash复制ls -l /dev/myserial
readlink -f /dev/myserial
6.2 压力测试
模拟频繁插拔测试规则稳定性:
bash复制for i in {1..10}; do
echo "Test cycle $i"
sudo udevadm trigger
ls -l /dev/myserial || echo "Link missing"
sleep 1
done
6.3 系统日志监控
实时查看内核和udev日志:
bash复制journalctl -f -k | grep -E 'usb|tty'
journalctl -f -u systemd-udevd
7. 扩展应用
7.1 固定设备节点名
对于需要稳定设备名的应用场景,可以结合by-path或by-id:
bash复制SUBSYSTEM=="tty", ENV{ID_PATH}=="platform-fc8c0000.usb-usb-0:1:1.0", \
SYMLINK+="ttyMainBoard"
7.2 自动化权限管理
对于多用户系统,可以通过udev规则动态设置组权限:
bash复制SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", GROUP:="dialout", \
MODE:="0660"
然后添加用户到dialout组:
bash复制sudo usermod -aG dialout $USER
7.3 热插拔脚本执行
udev规则可以触发自定义脚本:
bash复制ACTION=="add", SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", \
RUN+="/usr/local/bin/ch340-hotplug.sh"
示例脚本内容:
bash复制#!/bin/bash
logger "CH340 device $DEVNAME connected"
# 这里可以添加自定义初始化逻辑
8. 疑难问题排查指南
8.1 常见失败原因
-
规则语法错误:缺少逗号、引号不匹配等
bash复制udevadm test /sys/class/tty/ttyUSB0 2>&1 | grep -i error -
规则加载顺序问题:某些规则被覆盖
bash复制udevadm info --export-db | grep -A10 'myserial' -
权限不足:需要检查MODE设置和用户组
8.2 高级调试技巧
-
启用udev调试模式:
bash复制sudo udevadm control --log-priority=debug sudo journalctl -f -u systemd-udevd -
检查规则匹配情况:
bash复制udevadm info -a -n /dev/ttyUSB0 udevadm test $(udevadm info -q path -n /dev/ttyUSB0) -
检查服务依赖关系:
bash复制
systemctl list-dependencies --reverse brltty-udev.service
9. 跨平台注意事项
在不同Linux发行版上可能遇到的情况:
9.1 Ubuntu/Debian系
- BRLTTY默认安装且自动启动
- 需要特别注意
brltty-udev.service
9.2 RHEL/CentOS系
- BRLTTY可能未预装
- 服务名可能为
brltty.service
9.3 嵌入式系统
- BusyBox的udev实现可能功能有限
- 可能需要直接操作
/dev目录 - 考虑使用静态设备节点创建
10. 替代方案比较
当udev规则无法满足需求时,可以考虑:
10.1 静态设备创建
bash复制mknod /dev/myserial c 188 0
chmod 666 /dev/myserial
缺点:设备次设备号可能变化
10.2 脚本监控方案
使用inotifywait监控设备目录:
bash复制inotifywait -m /dev -e create | while read path action file; do
[[ "$file" == ttyUSB* ]] && ln -sf "$file" /dev/myserial
done
10.3 系统d服务方案
创建自定义systemd服务:
ini复制[Unit]
Description=CH340 Device Linker
After=sys-devices-platform-serial8250-tty-ttyS0.device
[Service]
ExecStart=/usr/local/bin/ch340-linker.sh
Restart=always
[Install]
WantedBy=multi-user.target
