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. netlink broadcast(主流):通过
NETLINK_KOBJECT_UEVENT 协议族的内核 socket 广播,用户态 udevd 常驻监听,这是目前唯一实际生效的路径。 - 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 状态变化上报链路。这是显示器插拔检测的核心机制。
两条检测路径并存
- 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. 轮询兜底(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 枚举流程,决定要不要重新配置输出。