当前位置:首页>Linux>Linux Kernel Uevent 机制与软件架构

Linux Kernel Uevent 机制与软件架构

  • 2026-09-10 11:00:00
Linux Kernel Uevent 机制与软件架构

1. 整体架构:kobject → uevent → netlink

uevent 的核心思路是:内核对象(kobject)状态变化时,通过一条"事件消息"广播给用户态,用户态(udev/systemd-udevd)据此做设备节点创建、权限设置、驱动加载等动作。

内核侧关键路径

  • • kset/kobject 层:每个 kobject 挂在某个 kset 下,kset 持有 uevent_ops(filter/name/uevent 三个回调),决定该 kobject 是否生成 uevent、事件名怎么给、附加哪些环境变量。
  • • 触发点:kobject_uevent(kobj, action) 或带环境变量的 kobject_uevent_env(kobj, action, envp),action 是 KOBJ_ADD/REMOVE/CHANGE/MOVE/ONLINE/OFFLINE 等。
  • • 环境变量组装:kobject_uevent_env 内部调用 kobject_uevent_net_broadcast,先拼 ACTION=、DEVPATH=、SUBSYSTEM= 等标准字段,再把调用者传入的自定义 envp(比如 DRM 传的 HOTPLUG=1、connector 信息)append 进去。
  • • 两条分发路径:
    1. 1. netlink broadcast(主流):通过 NETLINK_KOBJECT_UEVENT 协议族的内核 socket 广播,用户态 udevd 常驻监听,这是目前唯一实际生效的路径。
    2. 2. uevent_helper(/proc/sys/kernel/hotplug 配置的可执行文件路径,历史遗留的 /sbin/hotplug fork/exec 方式)——早期 initramfs 阶段 udev 还没起来时用,现代发行版基本不用,内核该逻辑仍保留但很少配置。
  • • coldplug 补充:设备已经存在但用户态需要重新枚举时(比如 udevadm trigger),会读 /sys/.../uevent 文件,写入触发一次 synthetic uevent,这条路径直接调用 kset 的 uevent 生成逻辑而不经过真实状态变化。

用户态侧

  • • udevd 通过 NETLINK_KOBJECT_UEVENT socket(libudev/sd-device 封装)收到事件后,读取 /sys 下对应 sysfs 路径的属性,匹配 /etc/udev/rules.d、/usr/lib/udev/rules.d 规则,执行 RUN、SYMLINK、加载固件等动作。
  • • 每条 uevent 消息本质是一段以 \0 分隔的 key=value 字符串序列,第一行是 ACTION@DEVPATH 形式的头部(用于旧协议兼容),后面跟环境变量。

2. DRM Hotplug uevent的实现

DRM HPD(Hot Plug Detect)是基于边沿触发中断 驱动的 connector 状态变化上报链路。这是显示器插拔检测的核心机制。

否
是
物理 HPD pin 电平跳变
边沿触发 GPIO/GPU IRQ
显卡驱动 IRQ handler
如 i915/amdgpu/nouveau
drm_helper_hpd_irq_event
或驱动自定义 hotplug work
遍历 connector,调用
connector_funcs->detect
connector status 是否变化
忽略,不上报
更新 connector->status
drm_sysfs_hotplug_event
kobject_uevent_env
drm_class_device kobj
netlink 广播
HOTPLUG=1 环境变量
systemd-logind / Xorg / Wayland compositor
监听 uevent 重新枚举 connector

两条检测路径并存

  1. 1. 真正的 HPD 中断(edge-triggered):显示接口(DP/HDMI)有专用 HPD 物理引脚,插拔时电平跳变触发 GPU 内部中断控制器的边沿触发中断。驱动的中断处理函数里区分是哪个 connector 的 HPD 变化(i915 是 intel_hpd_irq_handler,通过 hotplug_status 寄存器判断触发的 pin),然后 schedule 一个 workqueue(因为 detect 过程涉及 I2C/AUX 读 EDID,不能在中断上下文做)。
  2. 2. 轮询兜底(poll):对于没有可靠 HPD 信号的接口(比如某些 VGA、老式 DVI),DRM 提供 drm_kms_helper_poll_enable(),起一个定时 work(默认 10 秒一次)主动调用每个 connector 的 detect() 回调,效果等价,只是没有中断触发,靠轮询发现状态变化。

关键函数链

  • • drm_helper_hpd_irq_event(dev):核心入口,遍历所有 registered connector,调用 connector->funcs->detect()(该回调通常会读 DDC/EDID 判断是否有显示器接入),比对新旧 connector->status(connected/disconnected/unknown),有变化则标记。
  • • drm_kms_helper_hotplug_event(dev):状态确实变化后调用,内部会:
    • • 唤醒等待 dev->mode_config.connector_list 变化的用户态 poll(drm_sysfs_hotplug_event)
    • • 通过 kobject_uevent_env 向 drm_class(/sys/class/drm/)对应设备发 HOTPLUG=1 的 uevent
  • • 用户态合成器(Xorg 的 modesetting driver、Wayland 里的 wlroots/mutter 等)监听这条 uevent(通常通过 udev monitor 或者直接读 DRM fd 上的 drmHandleEvent 处理 DRM_EVENT_...),收到后重新走一遍 drmModeGetResources / drmModeGetConnector 枚举流程,决定要不要重新配置输出。

最新文章

随机文章