分类
devops

systemd-udevd

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 就像一个硬件事件分发系统,而不是通用的事件系统。