4. udev 与设备节点管理:规则编写、动态创建与权限控制

摄像头插上 USB 口,系统怎么知道它来了?怎么给它一个名字?怎么让普通用户也能访问它?

这些事,都是 udev 在背后干的。说白了,udev 就是 Linux 内核和用户空间之间的一个“设备管家”。

我刚开始做嵌入式 Linux 时,总觉得 udev 规则是玄学。后来被坑了几次,才老老实实把这块啃透。今天咱们就把 udev 的规则编写、节点创建、权限控制一次讲清楚。

4.1 udev 是什么?为什么需要它?

udev 是 Linux 的设备管理器。它负责:

  • 检测设备的热插拔事件
  • 在 /dev 下动态创建设备节点
  • 设置设备节点的权限、属主、属组
  • 根据规则执行自定义脚本

你想想看,如果没有 udev,每次插个摄像头都得手动 mknod 创建设备文件,还得算主次设备号。那得多痛苦?

核心概念: udev 基于 netlink 机制监听内核的 uevent 事件。设备插入时,内核发送 uevent,udev 收到后匹配规则,执行对应动作。

4.2 udev 规则文件的结构

规则文件放在 /etc/udev/rules.d/ 目录下。文件名以数字开头,数字越小优先级越高。我习惯用 99- 开头,确保我的规则最后加载,覆盖系统默认行为。

一条规则由两部分组成:

  • 匹配键(match keys):用来识别设备,比如 SUBSYSTEM、ATTRS、KERNEL
  • 赋值键(assignment keys):指定要执行的动作,比如 NAME、SYMLINK、MODE、OWNER

基本语法长这样:

# 匹配条件, 动作
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", MODE="0666"

注意:匹配键用 ==,赋值键用 =。这个别搞反了,我见过有人写错导致规则死活不生效。

4.3 如何获取设备的属性信息?

写规则之前,得先知道设备长什么样。我常用的命令是 udevadm info:

# 查看所有属性
udevadm info -a -n /dev/video0

# 查看设备路径
udevadm info -q path -n /dev/video0

# 监控热插拔事件
udevadm monitor --property

举个例子,插上 USB 摄像头后,执行 udevadm info -a -n /dev/video0,你会看到类似这样的输出:

looking at device '/devices/pci0000:00/.../video4linux/video0':
  KERNEL=="video0"
  SUBSYSTEM=="video4linux"
  ...

looking at parent device '/devices/pci0000:00/.../usb1/1-1':
  ATTRS{idVendor}=="046d"
  ATTRS{idProduct}=="0825"
  ATTRS{serial}=="ABCD1234"
  ...

这些 ATTRS 就是你的匹配依据。我一般用 idVendor + idProduct 组合,基本不会误匹配。

4.4 实战:为摄像头编写 udev 规则

假设我们有两款摄像头:

摄像头型号 idVendor idProduct 期望设备名
罗技 C920 046d 0825 /dev/cam_front
奥尼 A30 0c45 6366 /dev/cam_rear

规则文件 /etc/udev/rules.d/99-camera.rules:

# 罗技 C920 - 前摄像头
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", ATTRS{idProduct}=="0825", \
  SYMLINK+="cam_front", MODE="0666"

# 奥尼 A30 - 后摄像头
SUBSYSTEM=="video4linux", ATTRS{idVendor}=="0c45", ATTRS{idProduct}=="6366", \
  SYMLINK+="cam_rear", MODE="0666"

写完后,重载规则:

sudo udevadm control --reload-rules
sudo udevadm trigger

再插拔摄像头,ls -l /dev/cam_* 就能看到符号链接了。

我的习惯: 用 SYMLINK 而不是 NAME。因为 NAME 会覆盖内核默认的设备名,容易搞乱系统。符号链接既保留了原始节点,又给了你一个友好的名字。

4.5 权限控制:让普通用户也能用摄像头

默认情况下,/dev/video0 的权限是 660,属主是 root:video。普通用户不在 video 组里,就打不开设备。

解决方案有三种:

  1. 改 MODE:MODE="0666",所有用户可读写。简单粗暴,但不安全。
  2. 改 GROUP:GROUP="video",然后把用户加到 video 组。推荐做法。
  3. 用 ACL:RUN+="/usr/bin/setfacl -m u:myuser:rw /dev/video0",更精细。

我个人推荐第二种:

SUBSYSTEM=="video4linux", ATTRS{idVendor}=="046d", MODE="0660", GROUP="video"

然后执行 sudo usermod -aG video $USER,退出重新登录即可。

曾经踩过的坑: 我试过用 RUN+= 执行脚本,结果脚本里调用了 sleep,导致 udev 事件处理超时,设备节点创建失败。记住,udev 规则里的 RUN 要尽量轻量,别搞复杂逻辑。

4.6 多摄像头场景下的设备节点管理

多摄像头同时接入时,内核分配 video0、video1、video2 的顺序是不确定的。今天插 A 摄像头是 video0,明天可能就变成 video1 了。

怎么固定?用 物理端口路径 来区分。

查看 KERNELS 属性:

udevadm info -a -n /dev/video0 | grep KERNELS

输出类似:

KERNELS=="1-1.3:1.0"
KERNELS=="1-1.3"

这个 1-1.3 表示 USB 总线 1、端口 1、子端口 3。只要摄像头插在同一个 USB 口,这个路径就是固定的。

规则可以写成:

SUBSYSTEM=="video4linux", KERNELS=="1-1.3", SYMLINK+="cam_fixed_left", MODE="0666"
SUBSYSTEM=="video4linux", KERNELS=="1-1.4", SYMLINK+="cam_fixed_right", MODE="0666"

这样,左边 USB 口插的永远是 cam_fixed_left,右边是 cam_fixed_right。换摄像头也不影响。

4.7 udev 规则调试技巧

规则不生效?别慌,按这个流程排查:

  1. 检查语法:udevadm verify /etc/udev/rules.d/99-camera.rules(新版本支持)
  2. 查看匹配:udevadm test $(udevadm info -q path -n /dev/video0),模拟规则匹配过程
  3. 看日志:journalctl -f -t udev,实时观察 udev 事件
  4. 手动触发:udevadm trigger -v -n /dev/video0

我曾经遇到一个诡异问题:规则写对了,但就是不创建符号链接。查了半天,发现是文件名没以 .rules 结尾。嗯,这种低级错误,犯过一次就记住了。

4.8 本章知识结构图

下面这张图总结了 udev 设备节点管理的核心流程:

udev 设备节点管理核心流程 内核 uevent 事件 udevd 守护进程 匹配规则文件(/etc/udev/rules.d/*.rules) 创建设备节点 /dev/video0 创建符号链接 /dev/cam_front 设置权限/属主 MODE=0666 GROUP=video 调试命令 udevadm info -a -n /dev/video0 | udevadm test | journalctl -f -t udev udevadm control --reload-rules | udevadm trigger

4.9 常见问题与避坑指南

  • 规则不生效:检查文件名是否以 .rules 结尾,检查 == 和 = 是否用对。
  • 符号链接冲突:两个设备匹配到同一个 SYMLINK 名,后插的会覆盖先插的。用 SYMLINK+="unique_name" 避免。
  • 权限改了还是打不开:确认用户是否在对应组里,groups $USER 查看。改完组要重新登录。
  • 热插拔不触发:检查内核是否支持 CONFIG_UEVENT_HELPER,或者 udev 服务是否在运行:systemctl status systemd-udevd。
重要提醒: 不要在 udev 规则里执行长时间运行的脚本。udev 有超时机制(默认 30 秒),超时后事件会被丢弃。如果需要做复杂初始化,考虑用 systemd 服务来触发。

udev 规则写好了,设备节点管理就稳了。多摄像头同步方案里,这一步是基础中的基础。节点名字固定了,权限对了,后面的应用层才能安心干活。