前言
-
上一期我们在讲双雷达的时候使用过串口udev重命名,这一期我们详细讲一下背后的原理和具体查询过程
-
本文包括:
*1 串口udev重命名教程
2 udev原理
1 串口udev重命名教程
1-1 串口查询
- 查询串口
bash
ls /dev/tty*

- 通过插拔可以短暂确认本轮的临时串口号
1-2 串口信息确认
- 查询串口的信息
bash
dmesg | grep ttyUSB1

1-3 设备详细信息
- 查询具体信息
-n /dev/ttyUSB1:代表 name(名称)-a:代表"向上遍历设备树"
bash
udevadm info -a -n /dev/ttyUSB1 | grep -E "{idVendor}|{idProduct}|KERNELS=="

- 通过上述输出我们可以看到整个层级结构:
- 第一层:软件虚拟层,这是 Linux 内核驱动(ch341 驱动)最终创建的设备节点名称。
bash
KERNELS=="ttyUSB1"
- 第二层:USB 接口层,代表 USB 设备的"配置1,接口0
bash
KERNELS=="1-1.2.4:1.0"
- 第三层:真正的目标硬件,
KERNELS=="1-1.2.4"这是物理 USB 拓扑路径1a86:7523:这是南京沁恒(WCH)CH341 芯片的绝对唯一身份证号。不管你插在电脑的哪个口,这两个值都不会变。
bash
KERNELS=="1-1.2.4"
ATTRS{idProduct}=="7523"
ATTRS{idVendor}=="1a86"
- 第四层:上游 USB 集线器 Hub,这里我的32是经过拓展坞接到板子上的
bash
KERNELS=="1-1.2"
ATTRS{idProduct}=="2813"
ATTRS{idVendor}=="2109"
KERNELS=="1-1"
ATTRS{idProduct}=="0610"
ATTRS{idVendor}=="05e3"
- 第五层:主板根控制器(操作系统提供),这是你电脑主板上实际的 USB 控制器(根集线器)
bash
KERNELS=="usb1"
ATTRS{idProduct}=="0002"
ATTRS{idVendor}=="1d6b"
- 第六层:最底层的硬件总线(硬核底层)
bash
KERNELS=="xhci-hcd.2.auto"
KERNELS=="35100000.usb"
KERNELS=="35100000.usb3"
KERNELS=="35000000.hsio_apb"
KERNELS=="soc"
KERNELS=="platform"
1-4 编写 udev 规则
- 通过上一节的信息确认,我们得知了不会变的设备物理id
bash
KERNELS=="1-1.2.4"
ATTRS{idProduct}=="7523"
ATTRS{idVendor}=="1a86"
- 这时候我们只需要修改
udev规则文件即可
bash
sudo nano /etc/udev/rules.d/99-stm32-ch341.rules
- 把下述代码粘贴进去
bash
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="stm32", MODE="0666"
1-5 重新加载udev规则
bash
sudo udevadm control --reload-rules
sudo udevadm trigger
1-6 插拔验证
- 我们重新查找
bash
ls /dev/st*

2 udev原理
2-1 为什么需要 udev?
- 在早期的 Linux 中,
/dev目录下的设备节点是静态的。这就导致了一个致命问题:"插拔风暴"。 - 假设你只有一个串口设备,它永远是
/dev/ttyUSB0,这没什么问题。 - 但在我们的机器人开发中(比如双雷达 + STM32 底盘),如果你先插雷达,雷达可能抢占
ttyUSB0和ttyUSB1;如果你先插 STM32,STM32 就变成了ttyUSB0。设备的命名完全取决于你插线的先后顺序。 - 这会导致我们的 ROS launch 文件或代码配置极其脆弱,每次重启或换 USB 口都要改代码。
udev就是为了解决这个问题而诞生的------它负责在用户层动态管理设备,实现"按硬件身份固定名字"。
2-2 udev 的工作机制
udev并不是一直在后台轮询(死循环检测)USB 口,它是事件驱动 的。核心流程如下:- 内核发现硬件:当你插入 USB 设备时,Linux 内核的驱动程序(如 ch341)会检测到这一物理动作。
- 发送 uevent:内核会通过 Netlink 套接字,向用户空间广播一个"设备变更事件"。
- udev 守护进程接单 :后台运行的
udevd(udev 守护进程)监听到这个事件,拿到设备的信息。 - 规则匹配 :
udevd会按照字典顺序,遍历/etc/udev/rules.d/目录下的所有.rules文件。 - 执行动作 :一旦匹配到某条规则(比如我们写的 99-stm32-ch341.rules),
udev就会执行规则里指定的动作(创建软链接、修改权限等)。
2-3 规则文件的底层逻辑
- 我们在
1-4节写的这行代码
bash
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", ATTRS{idProduct}=="7523", SYMLINK+="stm32", MODE="0666"
- 它可以分为两类关键字:匹配键(条件) 和 赋值键(动作)。
- 匹配键(用
==表示,必须全部满足才会触发) :SUBSYSTEM=="tty":告诉 udev "我只关心串口类设备"。如果你插了个 CH341 的鼠标(也是 1a86:7523,但属于 input subsystem),这条规则不会拦截它,防止误杀。ATTRS{idVendor}=="1a86"和ATTRS{idProduct}=="7523":精准狙击,对照前面讲的"设备树第三层",直接锁定这个具体的硬件。
- 赋值键(用
+=或=表示,触发后执行的操作) :SYMLINK+="stm32":+=的意思是"追加一个别名"。udev 会在/dev/下创建一个名为stm32的快捷方式,指向真实的ttyUSBx。(注意:它不会抹除系统原本生成的ttyUSB1,两者共存)。MODE="0666":=的意思是"强制赋值"。把该设备的权限改为0666(即所有用户可读可写)。这样我们的普通用户(比如运行 ROS 节点的用户)就不需要每次都加sudo才能打开串口了。
2-4 关于"设备树层级"与规则的避坑指南
- 结合我们在
1-3节看到的六层结构,udev在匹配ATTRS{}时,是允许跨层级向上查找的。 - 为什么不能匹配第四层(Hub)? 如果你把规则写成
ATTRS{idVendor}=="2109"(扩展坞的 ID),那么只要这个扩展坞一通电,或者你往扩展坞上插个 U 盘,udev 都会试图执行动作,导致系统混乱。 - 最佳实践 :写 udev 规则时,匹配条件要尽量贴近设备树的底层(即目标硬件本身那一层,通常包含
idVendor和idProduct的那一层),这样才能保证规则的唯一性和稳定性。
总结
- 本文系统讲解了如何彻底解决Linux系统中多串口设备(如STM32)因插拔顺序导致命名随机变化的痛点;通过演示使用
udevadm命令向上遍历设备树以精准提取芯片唯一硬件ID的全流程,并深入剖析udev基于事件驱动的底层匹配与赋值机制,最终实现了为设备创建固定的软链接与权限配置,确保了开发环境的绝对稳定。 - 如有错误,欢迎指出!
- 感谢你的观看
