Linux 设备管理器:udev、mdev 与现代嵌入式根文件系统构建系统的设备管理方案

适用内核版本:Linux 2.6.13+(udev),Linux 2.6+(mdev via BusyBox)
覆盖范围:设备管理器(udev / mdev / eudev / procd-hotplug)及 Buildroot、Yocto、OpenWrt 等根文件系统构建系统的设备管理方案


目录

  1. 概述
  2. 历史演进:从 devfs 到 udev、mdev 与现代嵌入式方案
  3. udev 详解
  4. mdev 详解
  5. 现代嵌入式根文件系统构建系统下的设备管理器
  6. 核心机制对比
  7. 配置方式对比
  8. 版本演进与关键里程碑
  9. 使用场景选择指南
  10. 实际配置示例
  11. 从 udev 迁移到 mdev 的实践
  12. 附录:常见问题

一、概述

在 Linux 系统中,设备管理器负责动态创建/删除 /dev 下的设备节点,响应内核的热插拔事件,并执行相应的权限设置、符号链接创建、驱动加载等操作。

设备管理器/机制 定位 体量 依赖 典型场景
udev 桌面/服务器级标准设备管理器 ~1MB+ systemd(现代)或独立运行 PC、服务器、复杂嵌入式
mdev BusyBox 内置轻量级设备管理器 ~10KB BusyBox 资源受限且使用 BusyBox 的嵌入式系统
eudev udev 的 systemd 无关分支 ~1MB 无 systemd 依赖 不使用 systemd 的桌面/服务器
procd + hotplug OpenWrt 自研热插拔事件分发机制 ~几十KB procd / ubus / devtmpfs OpenWrt 路由器/网关
devfs 早期内核态设备文件系统 内核内 内核内置 2.4 内核时代(已废弃)

核心口诀:udev 大而全,mdev 小而快;udev 用 netlink,mdev 用 hotplug。

补充:在构建系统层面,Buildroot 可在 devtmpfs only / mdev / eudev / systemd-udev 之间切换;Yocto 默认使用 udev(eudev),可选 busybox-mdevsystemd;OpenWrt 默认使用 procd + hotplug 而非 mdev/udev。


二、历史演进:从 devfs 到 udev、mdev 与现代嵌入式方案

2.1 六个阶段

在这里插入图片描述

上图:Linux /dev 管理从静态节点、devfs、udev、devtmpfs、mdev 一路演进,到现代嵌入式构建系统时代分化为 Buildroot、Yocto、OpenWrt 三条典型路线。

时间轴解读

阶段 关键事件 解决的问题
静态节点 mknod 手动创建 /dev/xxx 早期 Linux 无动态设备管理能力
devfs 2.4.x 内核态动态设备文件系统 首次支持动态创建设备节点,但命名僵化、Bug 多
udev 2.6.13 引入用户态守护进程 将设备管理迁出内核,规则灵活,支持持久化命名
devtmpfs 2.6.32 内核自动创建基本节点 解决启动早期 /dev 为空的问题
mdev BusyBox 内置轻量方案 资源受限嵌入式场景的最小化选择
构建系统分化 ~2010+ 后 Buildroot/Yocto/OpenWrt 各成体系 同一硬件在不同构建系统中可采用不同 /dev 管理策略

2.2 为什么 devfs 被废弃?

问题 说明
命名不灵活 设备名称由内核硬编码,无法自定义
Bug 未修复 作者停止维护,存在 race condition 等问题
内核内存占用 大量设备时消耗显著内存
错误加载驱动 对不存在的设备节点也会尝试加载驱动,浪费资源
内核态实现 设备管理逻辑应在用户空间,devfs 违背这一原则

udev 的设计正是为了解决 devfs 的上述问题:将设备管理从内核态迁移到用户态,通过规则文件实现灵活配置。

2.3 嵌入式时代的方案分化

随着 devtmpfs 成熟,现代嵌入式系统不再只有“udev 或 mdev”二选一,而是根据构建系统和资源约束出现了多条路线:

阶段 代表方案 核心特征
静态节点时代 mknod + device table 构建时固定节点,无动态插拔能力
内核自治时代 devfs / devtmpfs 内核自动创建/删除基本节点
全功能用户态时代 udev / eudev / systemd-udevd netlink 异步事件、丰富规则、持久化命名
极简用户态时代 mdev BusyBox 内置、uevent_helper 同步调用
自研框架时代 OpenWrt procd + hotplug 不依赖 udev/mdev,基于 ubus 的事件分发

这种分化使得:同样的嵌入式硬件,在 Buildroot 中可能选 mdev,在 Yocto 中默认用 udev,在 OpenWrt 中则走 procd + hotplug


三、udev 详解

3.1 定位与架构

udev 是一个用户空间守护进程udevd),通过监听内核通过 netlink 发送的 uevent 事件来动态管理设备。

在这里插入图片描述

上图:udev 从内核检测设备变化开始,经 netlink 广播 uevent,由 udevd 监听、规则匹配引擎处理,最终执行创建设备节点、设置权限、加载固件等用户态动作。

3.2 核心组件

组件 作用 路径
udevd 守护进程,事件处理主循环 /usr/lib/systemd/systemd-udevd/sbin/udevd
udevadm 管理工具:查看规则、触发事件、监控日志 /usr/bin/udevadm
libudev 用户空间库,供应用程序查询设备信息 /usr/lib/libudev.so
规则文件 定义匹配条件和执行动作 /etc/udev/rules.d/*.rules
硬件数据库 设备属性数据库 /usr/lib/udev/hwdb.d/

3.3 事件类型

事件 触发时机 典型动作
add 设备注册到内核 创建 /dev/xxx 节点、设置权限、加载固件
remove 设备注销 删除节点、清理符号链接
change 设备状态变化 更新属性、重新评估规则
bind 驱动绑定设备 创建符号链接、触发服务
unbind 驱动解绑 移除符号链接
move 设备重命名 更新节点名

3.4 持久化命名

udev 的核心优势之一是持久化设备命名,解决设备名随插拔顺序变化的问题:

命名方式 路径示例 用途
by-id /dev/disk/by-id/usb-Brand_1234-0:0 基于设备唯一标识(序列号)
by-path /dev/disk/by-path/pci-0000:00:14.0-usb-0:1 基于物理拓扑位置
by-uuid /dev/disk/by-uuid/xxxx-xxxx 基于文件系统 UUID
by-label /dev/disk/by-label/MyDisk 基于文件系统卷标

四、mdev 详解

4.1 定位与架构

mdev 是 BusyBox 内置的轻量级设备管理器,专为资源受限的嵌入式系统设计。它不是独立项目,而是 BusyBox 的一个 applet。

在这里插入图片描述

上图:mdev 基于 uevent_helper 机制,内核产生事件时直接调用 /sbin/mdev,读取 /etc/mdev.conf 后同步执行创建/删除节点、设置权限或自定义命令等动作。

4.2 两种工作模式

模式 命令 用途
热插拔模式 echo /sbin/mdev > /proc/sys/kernel/hotplug 内核产生 uevent 时自动调用 mdev
扫描模式 mdev -s 系统启动时扫描 /sys/class/sys/block,批量创建设备节点

4.3 与 BusyBox 的关系

mdev 不是独立软件包,而是 BusyBox 编译时可选的一个功能:

# 配置 BusyBox 时启用 mdev
make menuconfig
# → Linux System Utilities → [*] mdev

编译后,mdev 作为 busybox 的符号链接存在:

ls -la /sbin/mdev
# lrwxrwxrwx 1 root root 7 Jan  1  1970 /sbin/mdev -> busybox

五、现代嵌入式根文件系统构建系统下的设备管理器

在嵌入式 Linux 中,根文件系统通常由 BuildrootYocto ProjectOpenWrt 等构建系统生成。它们对 /dev 管理的设计理念、默认实现和可配置空间各不相同,不能简单地认为“用了 BusyBox 就用 mdev”。本章详细分析这三个主流构建系统的设备管理方案,并给出选择建议。

5.1 总体对比

构建系统 默认 /dev 方案 可选项 关键配置入口 说明
Buildroot 通常 Dynamic using devtmpfs only
(很多板级 defconfig 会选 devtmpfs + mdev
Static device table
devtmpfs only
devtmpfs + mdev
devtmpfs + eudev
systemd-udev(若 init 选 systemd)
System configuration → /dev management 官方推荐先用 devtmpfs only,按需升级到 mdev/eudev
Yocto Project (Poky) SysVinit + udev
(实际提供者为 eudev)
udev / busybox-mdev / systemd / static pre-populated VIRTUAL-RUNTIME_dev_manager
USE_DEVFS
IMAGE_DEVICE_TABLES
工业级构建系统,默认走完整 udev
OpenWrt procd + hotplug
(不是 mdev/udev)
可额外安装 eudev 等包,但默认不走 /etc/hotplug.d/<subsystem>/ 脚本 自研轻量事件分发,设备节点由 devtmpfs 提供

5.2 Buildroot

Buildroot 在 System configuration → /dev management 中提供 4 种主要 /dev 管理方式:

  1. Static using device table:构建时固定生成节点,不支持动态插拔。
  2. Dynamic using devtmpfs only:仅依赖内核 devtmpfs 自动创建/删除节点,用户空间不参与。
  3. Dynamic using devtmpfs + mdev:在 devtmpfs 基础上叠加 BusyBox mdev,支持热插拔脚本、固件加载和自定义权限。
  4. Dynamic using devtmpfs + eudev:使用独立的 eudev 守护进程,规则能力接近桌面 udev。

若 Init system 选择 systemd/dev 管理会自动由 systemd 自带的 systemd-udevd 接管。

官方建议:先用 devtmpfs only,直到出现以下需求再升级:

  • 需要用户态感知设备插入/移除
  • 需要自动加载固件
  • 需要自定义设备节点权限/属组

此时优先选择 devtmpfs + mdev;若规则复杂或需要持久化命名,再选 eudev

典型配置片段(.config):

BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_MDEV=y

5.3 Yocto Project

Yocto Project 默认使用 devtmpfs + udev(在 Poky 参考发行版中实际提供者为 eudev)。它通过 VIRTUAL-RUNTIME_dev_manager 变量来选择设备管理器:

# 默认
VIRTUAL-RUNTIME_dev_manager = "udev"

# 轻量方案,使用 BusyBox mdev
# VIRTUAL-RUNTIME_dev_manager = "busybox-mdev"

# systemd 方案
# VIRTUAL-RUNTIME_dev_manager = "systemd"

同时可通过 USE_DEVFS 控制是否使用动态 /dev

# 默认:动态 devtmpfs + 设备管理器
USE_DEVFS = "1"

# 静态预填充 /dev
USE_DEVFS = "0"
IMAGE_DEVICE_TABLES = "device_table-mymachine.txt"

Yocto 的灵活性在于:同一套 recipes 可以通过 distro/machine 配置切换设备管理器,而无需修改上层应用包。

5.4 OpenWrt

OpenWrt 默认不使用 mdev 也不使用 udev,而是采用自研的 procd 进程管理器处理热插拔事件:

内核 uevent
    ↓
procd(PID 1 的一部分)
    ↓
/sbin/hotplug-call <subsystem>
    ↓
/etc/hotplug.d/<subsystem>/<script>
  • 设备节点仍由内核 devtmpfs 自动创建。
  • 启动阶段的“冷插拔”(coldplug)由 procd 自带的 udevtrigger 触发。
  • 热插拔脚本按子系统组织,如 /etc/hotplug.d/block//etc/hotplug.d/net//etc/hotplug.d/usb/

只有在运行某些特殊软件(如 ModemManager、容器工具链)时,才会额外安装 eudev 等包来补充 udev 规则能力。

5.5 选择建议

场景 推荐方案
极简资源受限系统,仅需基本设备节点 Buildroot: devtmpfs only;Yocto: busybox-mdev;OpenWrt: 默认 procd
需要热插拔脚本、固件加载、简单权限控制 Buildroot: devtmpfs + mdev;Yocto: busybox-mdev
需要持久化命名、复杂规则、硬件数据库 Buildroot: devtmpfs + eudev;Yocto: udev/systemd
与 systemd 深度集成 Yocto: VIRTUAL-RUNTIME_dev_manager = "systemd"

六、核心机制对比

6.1 实现机制差异

维度 udev mdev
事件接收机制 netlink socket(内核 ↔ udevd 守护进程) uevent_helper(内核调用 /proc/sys/kernel/hotplug 指定的程序)
进程模型 守护进程(udevd),长期运行 无守护进程,每次事件触发新进程
事件处理 异步(非阻塞,事件队列) 同步(阻塞式,顺序执行)
并发处理 支持并行处理多个事件 串行处理
内存占用 ~1MB+(守护进程常驻) ~10KB(仅 busybox 片段)
启动依赖 需要早期启动 udevd 仅需 busybox 和 /etc/mdev.conf

6.2 功能对比

功能 udev mdev
创建设备节点
设置权限/属主
符号链接 ✅(有限)
持久化命名(by-id/by-path)
固件自动加载 ⚠️(需手动配置)
热插拔管理 ✅ 完整 ⚠️ 基础
复杂规则匹配 ✅(丰富的键值匹配) ⚠️(简单正则)
网络设备管理
与 systemd 集成
D-Bus 通信
硬件数据库(hwdb)
设备快照
驱动模块自动加载 ⚠️(有限)

6.3 性能对比

指标 udev mdev
内存占用 高(守护进程常驻) 极低(无常驻进程)
启动时间 较长(需启动 udevd + 扫描规则) 极短(mdev -s 瞬间完成)
事件响应延迟 低(异步队列) 中等(同步 fork/exec)
大量事件处理 高效(netlink + 并行) 低效(串行同步)
CPU 占用 较低 极低

6.4 嵌入式构建系统方案对比

前面主要对比了 udevmdev 两个具体实现。但在实际嵌入式项目中,很少单独选择 udev/mdev,而是根据所采用的根文件系统构建系统来决定 /dev 管理方案。

维度 Buildroot Yocto Project OpenWrt
定位 轻量根文件系统生成器 工业级 Linux 发行版构建系统 路由器/网关固件发行版
默认 /dev 方案 devtmpfs only / devtmpfs + mdev(视 defconfig) devtmpfs + udev(实际为 eudev) procd + hotplug
事件机制 依选择而定:netlink(eudev)或 uevent_helper(mdev) netlink(udev/eudev 或 systemd-udevd) uevent_helper 分发给 hotplug 脚本
常驻进程 无(mdev)或 eudev/systemd-udevd udevd / systemd-udevd procd(PID 1)
配置入口 System configuration → /dev management VIRTUAL-RUNTIME_dev_manager /etc/hotplug.d/<subsystem>/
规则/脚本能力 mdev.conf 简单正则 / eudev rules 完整 udev rules hotplug shell 脚本
典型场景 极简嵌入式、单板产品 复杂嵌入式、工业产品 路由器、AP、网关
包管理 无(静态镜像) rpm/deb/ipk 可选 opkg

关键结论

  • Buildroot 是“轻量方案超市”,从 devtmpfs onlymdev 再到 eudev 都可配置,按资源约束选择即可。
  • Yocto Project 默认提供完整的 udev(eudev)能力,适合需要持久化命名、复杂规则和硬件数据库的工业级系统。
  • OpenWrt 不依赖 udev/mdev,而是用 procd + hotplug 完成事件分发,设备节点由 devtmpfs 提供,适合路由器这类有自研生态的场景。

七、配置方式对比

7.1 udev 规则文件

路径/etc/udev/rules.d/*.rules(自定义)+ /lib/udev/rules.d/*.rules(系统默认)

# /etc/udev/rules.d/99-my.rules
# 语法:匹配条件, 匹配条件, ... , 动作, 动作, ...

# 示例1:USB 设备匹配
SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", \
    SYMLINK+="my_device", MODE="0660", GROUP="plugdev", \
    RUN+="/usr/local/bin/setup_device.sh"

# 示例2:串口设备权限
KERNEL=="ttyUSB[0-9]*", OWNER="root", GROUP="dialout", MODE="0660"

# 示例3:块设备自动挂载
SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", ENV{DEVTYPE}=="partition", \
    RUN+="/usr/bin/systemd-mount --no-block --automount=yes /dev/%k /mnt/%k"

# 示例4:systemd 服务触发
SUBSYSTEM=="tty", KERNEL=="ttyACM[0-9]*", \
    TAG+="systemd", ENV{SYSTEMD_WANTS}="gps-setup.service"

udev 规则关键字

关键字 说明
SUBSYSTEM 子系统匹配(usb, block, tty 等)
KERNEL 内核设备名匹配
ATTR{...} sysfs 属性匹配
ENV{...} 环境变量匹配/设置
SYMLINK+="..." 创建符号链接
MODE="..." 设置权限
OWNER="..." 设置属主
GROUP="..." 设置属组
RUN+="..." 执行外部命令
TAG+="..." 添加标签(如 systemd)

7.2 mdev 配置文件

路径/etc/mdev.conf

# /etc/mdev.conf
# 格式:设备正则  [用户:组]  [权限]  [@|$*命令]
#   @ = 创建设备前执行
#   $ = 创建设备后执行
#   * = 创建设备后执行(传递环境变量)

# 示例1:SD 卡分区自动挂载
mmcblk[0-9]p[0-9]* 0:0 660 $ /etc/mdev/sdhotplug.sh

# 示例2:USB 串口权限
ttyUSB[0-9]* root:uucp 660

# 示例3:视频设备
video[0-9]* root:video 660

# 示例4:执行自定义脚本
sd[a-z][0-9]* 0:0 660 * /etc/mdev/automount.sh

mdev 环境变量(脚本中可用):

变量 说明
$MDEV 设备名(如 sda1)
$ACTION 动作(add/remove)
$FIRMWARE 固件名(如需加载固件)
$DEVPATH sysfs 中的设备路径

7.3 配置复杂度对比

特性 udev mdev
规则文件语法 复杂(键值对,多条件匹配) 简单(正则 + 权限 + 命令)
规则文件数量 多文件(按优先级排序) 单文件
调试工具 udevadm monitorudevadm test 无专用工具,靠日志
规则重载 udevadm control --reload-rules 修改即生效(无守护进程)

7.4 嵌入式构建系统中的配置入口

与直接在运行系统中编辑规则文件不同,Buildroot/Yocto/OpenWrt 的设备管理方案大部分在构建阶段决定,配置入口也各不相同。

构建系统 配置入口 典型配置示例
Buildroot make menuconfig → System configuration → /dev management BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_MDEV=y
Yocto Project local.conf / distro / machine 配置文件 VIRTUAL-RUNTIME_dev_manager = "udev" / "busybox-mdev" / "systemd"
OpenWrt /etc/hotplug.d/<subsystem>/ 脚本 + procd /etc/hotplug.d/block/10-mount

Buildroot 通过 Kconfig 菜单一次性选定 /dev 管理方式,生成镜像后基本不再变更;需要调整时重新编译即可。

Yocto Project 通过 VIRTUAL-RUNTIME_dev_manager 等变量在 recipe 层抽象设备管理器, distro 或 machine 配置切换方便,同一套上层 recipes 不需要改动。

OpenWrt 没有统一的“规则文件”,热插拔逻辑分散在 /etc/hotplug.d/ 下的 shell 脚本中,灵活性高但规则能力取决于脚本自身,调试也主要靠日志和手动触发。


八、版本演进与关键里程碑

8.1 时间线

在这里插入图片描述

上图:从静态节点、devfs、udev、devtmpfs、mdev,到 systemd-udevd、eudev,再到当前构建系统分化的时间线。

8.2 关键版本对照表

机制/项目 首次引入 成熟/强制版本 当前状态
静态设备节点 Linux 0.01 仅特殊场景使用
devfs 2.3.46 (2001) 2.4 2.6.13 移除,已废弃
udev 2.6.13 (2005) 2.6.15 主流桌面/服务器标准
udev 持久化命名 2.6.15 (2006) 2.6.30 沿用至今
devtmpfs 2.6.32 (2009) 3.0+ 现代系统标配
mdev (BusyBox) ~2006 ~2009 嵌入式标准
systemd-udevd ~2012 (systemd 183+) 3.x+ systemd 发行版标配
eudev ~2012 4.x+ 非 systemd 发行版选择

九、使用场景选择指南

9.1 选择 mdev 的场景

场景 原因
✅ 内存 < 64MB 的嵌入式系统 mdev 仅占用 ~10KB,无守护进程常驻
✅ 使用 BusyBox 的 initramfs/initrd mdev 已内置于 BusyBox,无需额外依赖
✅ 要求极简启动(< 2 秒) mdev -s 瞬间完成,无需启动守护进程
✅ 设备管理需求简单 仅需创建节点、设置权限,无复杂规则
✅ 无热插拔复杂需求 偶尔插拔 USB/SD 卡,mdev 足够处理
✅ 无桌面环境 无 KDE/GNOME/Xorg 等依赖 udev 的组件
✅ 容器/轻量级虚拟化 资源敏感,mdev overhead 极低

典型使用 mdev 的发行版/项目

  • Alpine Linux(默认使用 mdev,可选 eudev)
  • Buildroot 生成的嵌入式系统(常选 Dynamic using devtmpfs + mdev
  • Yocto 的 busybox-mdev 方案(非 Poky 默认,需显式配置)
  • 各类定制嵌入式 Linux

注意:OpenWrt 默认并不使用 mdev,而是使用自研的 procd + hotplug 体系,详见第五章

9.2 选择 udev 的场景

场景 原因
✅ 桌面/服务器系统 需要完整的设备管理、持久化命名、硬件数据库
✅ 复杂的热插拔处理 USB 设备频繁插拔,需要稳定设备名
✅ 多用户环境 需要动态权限管理、属组分配
✅ 与 systemd 深度集成 systemd 依赖 udev 进行设备触发和服务管理
✅ 需要网络设备管理 udev 支持网络接口重命名、MAC 绑定
✅ 需要高级功能 固件自动加载、D-Bus 通信、设备快照
✅ 存储设备多 需要 by-id/by-path/by-uuid 持久化命名

典型使用 udev 的发行版/项目

  • Ubuntu/Debian/Fedora/Arch(systemd-udevd)
  • RHEL/CentOS/Rocky Linux(systemd-udevd)
  • Gentoo(可选 udev 或 eudev)
  • Yocto Project / Poky(默认使用 eudev,也可切换为 systemd-udev)
  • 使用 systemd 的嵌入式发行版

9.3 选择 eudev 的场景

场景 原因
✅ 不使用 systemd 但需要完整 udev 功能 eudev 是 udev 的 systemd 无关分支
✅ 非 systemd 桌面/服务器系统 Gentoo、Alpine(可选)等发行版
✅ 嵌入式系统拒绝 systemd 依赖 Buildroot 可选 devtmpfs + eudev,Yocto 也可配置 eudev

9.4 选择 OpenWrt procd + hotplug 的场景

场景 原因
✅ 路由器 / AP / 网关设备 OpenWrt 原生为此类设备设计,procd 同时管理进程与热插拔
✅ 已基于 OpenWrt 生态开发 需要与 netifdubusprocd 等组件协同
✅ 资源受限且需要网络事件处理 不需要额外运行 udev/mdev 守护进程
✅ 偏好 shell 脚本处理设备事件 /etc/hotplug.d/ 下直接写脚本,无需学习 udev rules
✅ 设备节点由 devtmpfs 足够提供 不需要 udev/mdev 额外创建或管理节点

典型使用 procd + hotplug 的项目

  • OpenWrt 官方及衍生固件
  • 各类路由器、网关、AP 定制发行版

十、实际配置示例

10.1 场景:USB 摄像头插入处理

mdev 方式

# /etc/mdev.conf
video[0-9]* root:video 660 @/etc/mdev/camera_setup

# /etc/mdev/camera_setup
#!/bin/sh
if [ "$ACTION" = "add" ]; then
    v4l2-ctl --set-fmt-video=width=640,height=480
    /usr/bin/start_streaming
fi

udev 方式

# /etc/udev/rules.d/99-camera.rules
SUBSYSTEM=="video4linux", ATTR{index}=="0", \
    TAG+="systemd", ENV{SYSTEMD_WANTS}="camera-stream.service"

# systemd 服务自动管理(/etc/systemd/system/camera-stream.service)

10.2 场景:SD 卡自动挂载

mdev 方式

# /etc/mdev.conf
mmcblk[0-9]p[0-9]* 0:0 660 $ /etc/mdev/sdhotplug.sh

# /etc/mdev/sdhotplug.sh
#!/bin/sh
case $ACTION in
    add)    mount /dev/$MDEV /mnt/sd ;;
    remove) umount /mnt/sd ;;
esac

udev 方式

# /etc/udev/rules.d/99-sd.rules
SUBSYSTEM=="block", ENV{ID_TYPE}=="disk", ENV{DEVTYPE}=="partition", \
    RUN{program}+="/usr/bin/systemd-mount --no-block --automount=yes /dev/%k /mnt/%k"

10.3 场景:串口设备权限

mdev 方式

# /etc/mdev.conf
ttyACM[0-9]* root:dialout 660
ttyUSB[0-9]* root:dialout 660

udev 方式

# /etc/udev/rules.d/50-serial.rules
SUBSYSTEM=="tty", KERNEL=="ttyACM[0-9]*", OWNER="root", GROUP="dialout", MODE="0660"
SUBSYSTEM=="tty", KERNEL=="ttyUSB[0-9]*", OWNER="root", GROUP="dialout", MODE="0660"

10.4 场景:OpenWrt 下 USB 存储自动挂载

OpenWrt 不依赖 udev/mdev,而是通过 /etc/hotplug.d/block/ 下的 shell 脚本处理块设备热插拔。

插入时自动挂载

# /etc/hotplug.d/block/10-mount
#!/bin/sh

[ "$ACTION" = "add" ] || exit 0
[ "$DEVTYPE" = "partition" ] || exit 0

mkdir -p /mnt/$DEVNAME
mount /dev/$DEVNAME /mnt/$DEVNAME

移除时自动卸载

# /etc/hotplug.d/block/20-umount
#!/bin/sh

[ "$ACTION" = "remove" ] || exit 0

umount -l /mnt/$DEVNAME
rmdir /mnt/$DEVNAME 2>/dev/null

说明$ACTION$DEVNAME$DEVTYPE 等环境变量由 procd/sbin/hotplug-call 在调用脚本前注入。

10.5 场景:Yocto 下为自定义设备添加 udev 规则

Yocto Project 默认使用 udev(实际提供者为 eudev)。自定义设备规则通常通过单独 recipe 打包到镜像中,而不是手动修改目标系统。

1. 确保设备管理器为 udev

# build/conf/local.conf
VIRTUAL-RUNTIME_dev_manager = "udev"

2. 创建 udev rules recipe

# meta-mylayer/recipes-core/my-udev-rules/my-udev-rules.bb
SUMMARY = "Custom udev rules for my device"
LICENSE = "MIT"

SRC_URI = "file://99-my-device.rules"

S = "${WORKDIR}"

do_install() {
    install -d ${D}${sysconfdir}/udev/rules.d
    install -m 0644 ${WORKDIR}/99-my-device.rules ${D}${sysconfdir}/udev/rules.d/
}

FILES:${PN} += "${sysconfdir}/udev/rules.d/99-my-device.rules"

3. 编写规则文件

# meta-mylayer/recipes-core/my-udev-rules/files/99-my-device.rules
# 为指定 VID/PID 的 USB 设备创建固定符号链接并设置权限
SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", \
    SYMLINK+="my_device%n", MODE="0660", GROUP="plugdev"

4. 将规则包加入镜像

# image recipe 或 local.conf
IMAGE_INSTALL:append = " my-udev-rules"

说明:这样构建出的镜像启动后会自动加载 /etc/udev/rules.d/99-my-device.rules,无需在目标板上手动维护规则文件。

10.6 场景:Buildroot 下切换 /dev 管理方式

Buildroot 在构建阶段通过 Kconfig 菜单决定 /dev 管理方式,无需在目标系统中手动安装或切换。

菜单路径

make menuconfig
    → System configuration
        → /dev management
            → Dynamic using devtmpfs + mdev

等价 .config 片段(使用 mdev):

BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_MDEV=y

若需要完整 udev 能力,改为 eudev

BR2_ROOTFS_DEVICE_CREATION_DYNAMIC_EUDEV=y

重新执行 make 后,生成的根文件系统镜像即使用选定的设备管理器。目标板上无需再修改 /etc/mdev.conf/etc/udev/rules.d/ 之外的构建配置。


十一、从 udev 迁移到 mdev 的实践

11.1 迁移检查清单

# 1. 检查哪些包依赖 udev
equery depends udev        # Gentoo
apt-cache rdepends udev    # Debian/Ubuntu
rpm -q --whatrequires udev # RHEL

# 2. 检查是否使用 LVM(mdev 对 LVM 支持有限)
lvscan

# 3. 检查是否使用桌面环境(KDE/GNOME 依赖 udev)
# 如果有,不建议迁移到 mdev

11.2 迁移步骤(以 Gentoo 为例)

# 1. 确保 BusyBox 编译了 mdev
make menuconfig
# → Linux System Utilities → [*] mdev

# 2. 安装 busybox(静态链接 + mdev 支持)
echo 'sys-apps/busybox static mdev -pam' >> /etc/portage/package.use
emerge -av busybox

# 3. 配置 mdev
cp /opt/mdev/mdev.conf /etc/mdev.conf

# 4. 设置 uevent_helper
echo "/sbin/mdev" > /proc/sys/kernel/hotplug

# 5. 系统启动时扫描创建设备
mdev -s

# 6. 添加 mdev 到启动级别
rc-update add mdev sysinit

# 7. 移除 udev
rc-update del udev sysinit
rc-update del udev-mount sysinit
emerge --unmerge -a sys-fs/udev virtual/udev

# 8. 屏蔽 udev(防止被重新安装)
echo "sys-fs/udev" >> /etc/portage/package.mask/udev

11.3 规则迁移对照

mdev 规则 udev 等效规则
sda[0-9]* root:disk 660 KERNEL=="sda[0-9]*", OWNER="root", GROUP="disk", MODE="0660"
ttyACM[0-9]* 0:0 660 @/usr/local/bin/gps_setup.sh SUBSYSTEM=="tty", KERNEL=="ttyACM[0-9]*", RUN+="/usr/local/bin/gps_setup.sh"
mmcblk[0-9]p[0-9]* 0:0 660 $ /etc/mdev/sdhotplug.sh SUBSYSTEM=="block", KERNEL=="mmcblk[0-9]p[0-9]*", RUN+="/etc/mdev/sdhotplug.sh"

11.4 常见问题

问题 原因 解决
/dev/shm 仅 root 可写 无 udev-mount 脚本 启动脚本添加 chmod 1777 /dev/shm
lsusb 不工作 依赖 udev 的 usbids 数据库 安装 usbutils 或使用 cat /sys/kernel/debug/usb/devices
网络接口未重命名 mdev 无网络设备管理 使用 nameif + /etc/mactab 基于 MAC 重命名
设备节点权限不对 mdev.conf 规则未配置 检查 /etc/mdev.conf 正则匹配和权限设置
Xorg 输入设备异常 Xorg 依赖 udev 检测输入设备 手动配置 /etc/X11/xorg.conf.d/ 的 InputDevice 段

十二、附录:常见问题

Q1:mdev 能完全替代 udev 吗?

不能。mdev 是 udev 的功能子集,适合简单场景。以下功能 mdev 无法提供:

  • 持久化设备命名(by-id/by-path/by-uuid)
  • 硬件数据库(hwdb)查询
  • 与 systemd 的服务触发集成
  • 复杂的规则匹配逻辑
  • 网络设备管理

Q2:devtmpfs 和 udev/mdev 是什么关系?

互补关系

  • devtmpfs(内核态):设备注册时自动创建基本节点(root:root, 0600),确保启动早期 /dev 不为空
  • udev/mdev(用户态):后续调整权限、属组、符号链接,执行自定义动作

现代 Linux 系统中,devtmpfs 是 /dev 的基础,udev/mdev 在其之上做增强管理。

Q3:为什么 Alpine Linux 默认使用 mdev?

Alpine Linux 的设计哲学是极小化、安全、简单。mdev 完美契合:

  • 无守护进程,攻击面小
  • 内存占用极低
  • 无需额外依赖(已内置于 busybox)
  • 容器和嵌入式场景足够

Alpine 也提供 eudev 作为可选包,供需要完整设备管理的用户安装。

Q4:如何查看当前系统使用哪种设备管理器?

# 方法1:检查进程
ps aux | grep -E "udev|mdev"

# 方法2:检查 uevent_helper
cat /proc/sys/kernel/hotplug
# 输出 /sbin/mdev → 使用 mdev
# 输出为空 → 使用 udev(netlink 机制)

# 方法3:检查服务
rc-status | grep -E "udev|mdev"      # OpenRC
systemctl status systemd-udevd        # systemd

# 方法4:检查配置文件
ls -la /etc/mdev.conf      # 存在 → 可能使用 mdev
ls -la /etc/udev/          # 存在 → 使用 udev
ls -la /etc/hotplug.d/     # 存在且 procd 运行 → OpenWrt 方案

# 方法5:OpenWrt 专用
ubus call service list     # 查看 procd 服务
ls /etc/hotplug.d/block/   # 查看 block 子系统热插拔脚本

Q5:容器(Docker)中需要设备管理器吗?

通常不需要。容器共享宿主机的 /dev,设备节点由宿主机管理。但如果容器需要独立 /dev(如 --privileged 或特殊 init 系统),可以使用 devtmpfs 或极简 mdev。


参考资源

  1. BusyBox mdev 官方文档docs/mdev.txt(BusyBox 源码内)
  2. udev 官方文档man udevman udevadmman udev.conf
  3. 《Linux Device Drivers, 3rd Edition》 — Jonathan Corbet 等
  4. Gentoo Wiki - Mdev:https://wiki.gentoo.org/wiki/Mdev
  5. Alpine Linux Wiki - Mdev:https://wiki.alpinelinux.org/wiki/Mdev
  6. Yocto Project - Device Manager:https://docs.yoctoproject.org/dev/dev-manual/device-manager.html
  7. Yocto Project - Initialization Manager:https://docs.yoctoproject.org/dev/dev-manual/init-manager.html
  8. Buildroot User Manual - /dev management:https://buildroot.org/downloads/manual/manual.html
  9. OpenWrt - procd:https://openwrt.org/docs/techref/procd

说明:本文覆盖的 Linux 设备管理器包含从 devfs 到 udev、mdev,再到 Buildroot/Yocto/OpenWrt 等嵌入式构建系统方案的完整演进,适用于 Linux 2.6.13+ 至当前 6.x 主线内核。对于资源受限的嵌入式系统,mdev 是轻量首选;对于工业级复杂嵌入式系统,Yocto 默认的 udev/eudev 更合适;对于路由器/网关,OpenWrt 的 procd + hotplug 是原生方案;对于桌面/服务器,udev(或 eudev)是标准选择。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐