udev rules 的 ACTION 限制
udev rules 只能对特定的、有限的硬件事件触发,不能对所有事件响应。
udev 支持的 ACTION(有限枚举)
| ACTION | 触发条件 | 例子 |
|---|---|---|
| add | 硬件设备被检测到/出现 | USB 设备插入、新磁盘识别 |
| remove | 硬件设备被移除 | USB 设备拔出、磁盘卸载 |
| change | 硬件设备状态改变 | 网卡状态变化、电池百分比变化 |
| bind | 驱动绑定到设备 | 加载驱动时 |
| unbind | 驱动从设备解绑 | 卸载驱动时 |
就这 5 种,没有其他的。
为什么有限?
因为 udev 的设计目的很明确:
内核硬件事件(有限)
↓
udev 守护进程(systemd-udevd)
↓
udev rules(只能对这些有限事件响应)
↓
执行相应操作
udev 只监听内核 uevent,而内核报告的硬件事件就只有这 5 种。
各种硬件事件对应的 ACTION
1. add – 硬件出现
# USB 设备插入
ACTION=="add", SUBSYSTEM=="usb", ...
# 磁盘被识别
ACTION=="add", SUBSYSTEM=="block", ...
# 网卡被识别
ACTION=="add", SUBSYSTEM=="net", ...
2. remove – 硬件移除
# USB 设备拔出
ACTION=="remove", SUBSYSTEM=="usb", ...
# 磁盘被移除(虚拟磁盘卸载)
ACTION=="remove", SUBSYSTEM=="block", ...
3. change – 硬件状态改变
# 磁盘分区表改变
ACTION=="change", SUBSYSTEM=="block", ...
# 电池状态改变(电量变化)
ACTION=="change", SUBSYSTEM=="power_supply", ...
# 网卡状态改变(但注意:这里的"改变"是有限的)
ACTION=="change", SUBSYSTEM=="net", ...
重点:network subsystem 的 change 事件能检测:
– ✓ 网卡驱动加载/卸载
– ✓ 网卡启用/禁用
– ❌ 网线物理插拔(不能检测)
网线插拔为什么不能触发 udev?
因为网线物理插拔没有对应的 ACTION:
网线插拔
↓
网卡芯片检测到信号变化
↓
内核网络驱动报告链路状态(但不生成 uevent)
↓
❌ udev 没有收到任何事件
↓
❌ udev rules 无法触发
vs
USB 设备插入
↓
USB 控制器检测到新设备
↓
内核生成 uevent: ACTION=add, SUBSYSTEM=usb
↓
✓ udev 收到事件
↓
✓ udev rules 可以触发
udev 能检测的事件 vs 不能检测的事件
✓ udev 能检测(ACTION 对应)
物理硬件层面的变化:
├─ 设备插入/拔出 (add/remove)
├─ 设备内部状态改变 (change)
│ ├─ 电池电量变化
│ ├─ 网卡启用/禁用
│ └─ 磁盘分区表改变
└─ 驱动绑定/解绑 (bind/unbind)
❌ udev 不能检测(没有 ACTION)
链路层/应用层变化:
├─ 网线物理插拔(链路层事件,不是硬件设备事件)
├─ IP 地址获取/丢失(应用层,DHCP)
├─ WiFi 连接/断开(驱动层,但不生成 uevent)
├─ 打印任务到来(应用层)
├─ 文件系统挂载/卸载(应用层)
└─ 进程启动/停止(应用层)
对比:udev vs 其他监控方案
| 监控对象 | udev | 其他方案 |
|---|---|---|
| 硬件设备出现/消失 | ✓ udev | – |
| 网线物理插拔 | ❌ | ethtool / ip monitor |
| 文件修改 | ❌ | inotify / fanotify |
| 进程启动/停止 | ❌ | systemd / audit |
| 系统日志事件 | ❌ | journalctl |
| 网络连接 | ❌ | netstat / ss / NetworkManager |
| 磁盘 I/O | ❌ | blktrace / iotop |
实际验证:网卡 ACTION 事件是有限的
# 监听网卡相关的 udev 事件
udevadm monitor --subsystem-match=net
# 启用网卡:会显示 ACTION=change
# ethtool -s eth0 autoneg on
# 禁用网卡:会显示 ACTION=change
# ip link set eth0 down
# 但拔出网线(网线物理拔出):❌ 什么都不会显示
那怎样在网线事件上做自动化?
既然 udev 不行,有以下选择:
选项 1:用链路监控替代
# 创建系统单元持续监控
[Unit]
Description=Network Cable Monitor
After=network.target
[Service]
Type=simple
ExecStart=/usr/local/bin/monitor_link.sh
Restart=always
[Install]
WantedBy=multi-user.target
# monitor_link.sh
ip monitor link | grep -oP 'eth0.*' | while read event; do
if [[ $event =~ LOWER_UP ]]; then
/usr/local/bin/cable_in.sh
elif [[ $event =~ NO-CARRIER ]]; then
/usr/local/bin/cable_out.sh
fi
done
选项 2:用 D-Bus 信号
# NetworkManager 通过 D-Bus 发送信号
dbus-monitor --system "interface='org.freedesktop.NetworkManager'" | \
grep -E "StateChanged|PropertiesChanged"
选项 3:定时轮询
# cron 定时检查
*/1 * * * * /usr/local/bin/check_cable.sh
# check_cable.sh
#!/bin/bash
STATE=$(ethtool eth0 | grep "Link detected" | awk '{print $NF}')
# 与之前状态比较,如果改变就执行操作
总结
关键认识:
udev 的设计目的:监听和响应硬件设备的出现/消失/状态改变
udev 的限制:只能对内核定义的 5 种 ACTION 响应
结论:应用层和链路层的事件需要用其他专门的监听工具
简单说:udev 就像一个硬件事件分发系统,而不是通用的事件系统。
