Linux 设备管理器:udev、mdev 与现代嵌入式根文件系统构建系统的设备管理方案
Linux 设备管理器:udev、mdev 与现代嵌入式根文件系统构建系统的设备管理方案
适用内核版本:Linux 2.6.13+(udev),Linux 2.6+(mdev via BusyBox)
覆盖范围:设备管理器(udev / mdev / eudev / procd-hotplug)及 Buildroot、Yocto、OpenWrt 等根文件系统构建系统的设备管理方案
目录
- 概述
- 历史演进:从 devfs 到 udev、mdev 与现代嵌入式方案
- udev 详解
- mdev 详解
- 现代嵌入式根文件系统构建系统下的设备管理器
- 核心机制对比
- 配置方式对比
- 版本演进与关键里程碑
- 使用场景选择指南
- 实际配置示例
- 从 udev 迁移到 mdev 的实践
- 附录:常见问题
一、概述
在 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-mdev或systemd;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 中,根文件系统通常由 Buildroot、Yocto Project、OpenWrt 等构建系统生成。它们对 /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_managerUSE_DEVFSIMAGE_DEVICE_TABLES |
工业级构建系统,默认走完整 udev |
| OpenWrt | procd + hotplug(不是 mdev/udev) |
可额外安装 eudev 等包,但默认不走 |
/etc/hotplug.d/<subsystem>/ 脚本 |
自研轻量事件分发,设备节点由 devtmpfs 提供 |
5.2 Buildroot
Buildroot 在 System configuration → /dev management 中提供 4 种主要 /dev 管理方式:
- Static using device table:构建时固定生成节点,不支持动态插拔。
- Dynamic using devtmpfs only:仅依赖内核
devtmpfs自动创建/删除节点,用户空间不参与。 - Dynamic using devtmpfs + mdev:在 devtmpfs 基础上叠加 BusyBox mdev,支持热插拔脚本、固件加载和自定义权限。
- 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 嵌入式构建系统方案对比
前面主要对比了 udev 与 mdev 两个具体实现。但在实际嵌入式项目中,很少单独选择 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 only到mdev再到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 monitor、udevadm 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 生态开发 | 需要与 netifd、ubus、procd 等组件协同 |
| ✅ 资源受限且需要网络事件处理 | 不需要额外运行 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。
参考资源
- BusyBox mdev 官方文档:
docs/mdev.txt(BusyBox 源码内) - udev 官方文档:
man udev、man udevadm、man udev.conf - 《Linux Device Drivers, 3rd Edition》 — Jonathan Corbet 等
- Gentoo Wiki - Mdev:https://wiki.gentoo.org/wiki/Mdev
- Alpine Linux Wiki - Mdev:https://wiki.alpinelinux.org/wiki/Mdev
- Yocto Project - Device Manager:https://docs.yoctoproject.org/dev/dev-manual/device-manager.html
- Yocto Project - Initialization Manager:https://docs.yoctoproject.org/dev/dev-manual/init-manager.html
- Buildroot User Manual - /dev management:https://buildroot.org/downloads/manual/manual.html
- 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)是标准选择。
更多推荐
所有评论(0)