3. DRM核心数据结构:drm_device、drm_driver、drm_file、drm_minor等核心对象关系

DRM(Direct Rendering Manager)子系统的核心架构围绕几个关键数据结构展开。理解 drm_device、drm_driver、drm_file 和 drm_minor 之间的关系,是掌握整个显示子系统的基础。这些对象共同构成了用户空间与内核空间交互的桥梁,并管理着从硬件抽象到文件操作的全生命周期。

3.1 核心对象全景图

下图展示了 DRM 核心对象之间的静态层次关系与动态交互路径。每个 drm_device 对应一个物理或虚拟显示控制器,通过 drm_driver 绑定具体硬件操作,通过 drm_minor 暴露给用户空间,而 drm_file 则代表一次打开的文件描述符会话。

DRM 核心数据结构关系全景图 drm_device 核心设备对象,管理整个显示控制器 drm_driver 驱动操作函数表与元数据 drm_minor 设备次编号,用户空间访问入口 drm_file 文件句柄私有数据,每个 open 对应一个 driver 指针 minors[] 数组 filelist 链表 dev 关联 用户空间 (User Space) 通过 /dev/dri/card0 等设备节点访问 内核空间 (Kernel Space) DRM 核心 + 具体驱动实现 系统调用
关键理解: 这四个对象构成了 DRM 的“四层架构”——drm_driver 定义能力,drm_device 管理状态,drm_minor 提供访问入口,drm_file 隔离会话。用户空间的每次 open() 调用都会在内核中创建一个 drm_file 实例,并通过 drm_minor 找到对应的 drm_device。

3.2 drm_device:核心设备对象

drm_device 是 DRM 子系统中最高层级的抽象,代表一个完整的显示控制器。它聚合了所有子组件,包括模式设置、帧缓冲、CRTC、编码器、连接器以及内存管理(GEM/TTM)等。

字段 类型 说明
driver struct drm_driver * 指向驱动操作函数表的指针
dev struct device * 内核通用设备结构,用于电源管理和设备模型
primary struct drm_minor * 主设备节点(/dev/dri/card0)
render struct drm_minor * 渲染节点(/dev/dri/renderD128)
filelist struct list_head 所有打开文件句柄的链表头
mode_config struct drm_mode_config 模式配置管理结构,包含所有 CRTC/Encoder/Connector
vblank struct drm_vblank_crtc * 垂直消隐中断管理数组

drm_device 的初始化通常发生在驱动探测阶段,调用 drm_dev_alloc() 分配内存,然后通过 drm_dev_register() 注册到系统。其生命周期与硬件设备绑定,当设备拔出或驱动卸载时,调用 drm_dev_unregister() 和 drm_dev_put() 释放。

3.3 drm_driver:驱动操作函数表

drm_driver 是 DRM 框架与具体硬件驱动之间的契约。它定义了一组回调函数,驱动开发者必须实现这些函数来提供硬件特定的功能。它同时携带驱动元数据,如名称、版本、支持的芯片组等。

核心回调函数分类:
  • 设备生命周期: load()、unload()、open()、postclose()、lastclose()
  • 内存管理: gem_create_object()、gem_prime_import()、dumb_create()、dumb_map_offset()
  • 文件操作: fops 字段指向 struct file_operations,包含 open、release、ioctl、mmap 等
  • 调试支持: debugfs_init()、show_fdinfo()

一个典型的 drm_driver 实例定义如下:

static const struct drm_driver my_driver = {
    .driver_features = DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
    .name           = "my_gpu",
    .desc           = "My GPU DRM Driver",
    .date           = "20250101",
    .major          = 1,
    .minor          = 0,

    .fops           = &my_drm_fops,

    .gem_create_object = my_gem_create_object,
    .dumb_create       = my_dumb_create,
    .dumb_map_offset   = my_dumb_map_offset,

    .prime_handle_to_fd = drm_gem_prime_handle_to_fd,
    .prime_fd_to_handle = drm_gem_prime_fd_to_handle,
};
注意: driver_features 标志位决定了驱动支持的功能集。例如,DRIVER_MODESET 表示支持模式设置,DRIVER_ATOMIC 表示支持原子提交,DRIVER_RENDER 表示支持渲染节点。这些标志必须在驱动初始化时正确设置,否则相关功能不可用。

3.4 drm_minor:设备节点抽象

drm_minor 是 DRM 设备在用户空间的访问入口。每个 drm_device 可以拥有多个 drm_minor,分别对应不同的设备节点类型。Linux 内核通过次设备号来区分不同的 minor 实例。

类型 设备节点路径 次设备号范围 用途
DRM_MINOR_PRIMARY /dev/dri/card0 0-63 主设备节点,支持模式设置和渲染
DRM_MINOR_CONTROL /dev/dri/controlD64 64-127 控制节点,用于权限管理(已废弃)
DRM_MINOR_RENDER /dev/dri/renderD128 128-191 渲染节点,仅用于 GPU 计算,无模式设置能力

drm_minor 结构体包含以下关键字段:

struct drm_minor {
    int index;                      /* 次设备号索引 */
    int type;                       /* 类型:PRIMARY / CONTROL / RENDER */
    struct device *kdev;            /* 内核设备结构 */
    struct drm_device *dev;         /* 指向所属的 drm_device */
    struct drm_mode_group mode_group; /* 模式设置组 */
};

当用户空间打开 /dev/dri/card0 时,VFS 层根据次设备号找到对应的 drm_minor,然后通过 drm_minor->dev 找到 drm_device,最终调用 drm_driver->fops->open() 完成初始化。

3.5 drm_file:文件句柄私有数据

drm_file 是每个用户空间打开操作(open)在内核中的私有数据。它隔离了不同进程之间的状态,确保并发访问的安全性。每个 drm_file 实例都包含该会话特有的上下文信息。

字段 说明
authenticated 是否通过权限认证(仅主节点需要)
master 指向主控节点(master)的指针,用于权限管理
pid 打开该文件描述符的进程 PID
lhead 链表节点,用于挂入 drm_device->filelist
object_idr IDR 树,管理该会话创建的 GEM 对象句柄
event_list 待发送的 DRM 事件链表(如 VBLANK 事件)
prime PRIME 缓冲区共享相关数据

drm_file 的生命周期与文件描述符绑定:

  1. open():内核分配 drm_file,初始化 IDR 树和事件链表,挂入 filelist,调用驱动 open() 回调。
  2. ioctl():通过 drm_file 查找句柄对应的 GEM 对象,执行具体操作。
  3. mmap():通过 drm_file 中的地址空间信息完成内存映射。
  4. release():清理所有 GEM 对象句柄,释放事件,从 filelist 移除,调用驱动 postclose() 回调。

3.6 对象关系总结

下表总结了四个核心对象之间的主要关系:

关系 方向 实现方式 说明
drm_device → drm_driver 1:1 指针引用 每个设备绑定一个驱动,驱动可被多个设备共享
drm_device → drm_minor 1:N 数组(primary/render) 一个设备可暴露多个设备节点
drm_device → drm_file 1:N 链表(filelist) 多个进程可同时打开同一个设备
drm_minor → drm_device N:1 指针引用 所有 minor 指向同一个设备
drm_file → drm_device N:1 指针引用 通过 minor 间接关联
drm_file → drm_minor N:1 指针引用 每个文件句柄对应一个 minor
实战要点: 在调试 DRM 驱动时,可以通过 /sys/kernel/debug/dri/ 下的 debugfs 接口查看这些对象的实时状态。例如,cat /sys/kernel/debug/dri/0/name 可以查看 drm_device 的名称,cat /sys/kernel/debug/dri/0/clients 可以查看所有 drm_file 列表。