1. DRM框架概述

1.1 DRM架构组成

DRM(Direct Rendering Manager)是Linux内核中负责图形显示和渲染管理的核心框架。它位于内核空间,向上提供统一的系统调用接口,向下抽象各类GPU和显示控制器硬件。DRM框架由以下核心组件构成:

组件 职责 关键数据结构
DRM Core 框架核心,管理设备注册、文件操作、ioctl分发 struct drm_device
GEM 图形内存管理器,负责buffer分配、共享、GPU访问 struct drm_gem_object
KMS 内核模式设置,控制显示输出、分辨率、连接器、编码器 struct drm_crtc、struct drm_connector
Framebuffer 帧缓冲区抽象,关联GEM对象与显示扫描输出 struct drm_framebuffer
Driver 硬件厂商实现层,填充回调函数钩子 struct drm_driver
架构要点: DRM框架采用分层设计——上层ioctl统一入口,中间层提供通用逻辑(如atomic check/commit),底层驱动实现硬件操作。这种分层使得驱动开发者只需关注硬件差异,而无需重复实现状态管理、并发控制等通用机制。

1.2 DRM软件架构分层图

下图展示了DRM框架在内核空间中的分层结构,以及用户空间通过系统调用与内核交互的路径:

DRM 软件架构分层图 用户空间 (Userspace) libdrm / Mesa / Wayland / Xorg / 应用程序 系统调用 (ioctl / mmap) DRM Core (drm_core) 设备注册 · 文件操作 · ioctl分发 · 状态管理 GEM 图形内存管理 分配/共享/GPU访问 KMS 内核模式设置 CRTC/Encoder/Connector Framebuffer 帧缓冲区抽象 扫描输出关联 硬件驱动层 (Driver) i915 · amdgpu · msm · vc4 · rockchip · mediatek · ... 硬件 (GPU / Display Controller / HDMI / DP / DSI) 用户空间通过 open/ioctl/mmap访问

1.3 GEM:图形内存管理器

GEM(Graphics Execution Manager)是DRM框架中负责图形缓冲区管理的子系统。它解决了多个进程共享GPU内存、高效分配与回收、以及CPU与GPU之间数据同步等核心问题。

  • 缓冲区分配: 通过drm_gem_object_init()创建GEM对象,驱动可自定义底层内存类型(VRAM、CMA、System RAM)。
  • 句柄与命名: 每个GEM对象在进程内通过handle引用,跨进程通过flink或dma-buf共享。
  • 内存映射: 支持mmap将GEM对象映射到用户空间地址,实现CPU直接访问。
  • GPU访问: 驱动通过GEM对象获取物理地址或DMA地址,供GPU引擎执行渲染指令。
GEM vs TTM: 早期DRM同时存在GEM和TTM两套内存管理器。GEM设计更简洁,逐渐成为主流;TTM(Translation Table Maps)主要用于复杂GPU(如radeon),提供更精细的显存管理。现代驱动(如amdgpu)已统一使用GEM+TTM混合方案。

1.4 KMS:内核模式设置

KMS(Kernel Mode Setting)将显示模式设置的控制权从用户空间移回内核空间,解决了传统X11模式设置带来的闪烁、撕裂和权限问题。KMS的核心对象模型如下:

KMS对象 内核数据结构 功能描述
CRTC struct drm_crtc 显示控制器,负责从framebuffer读取像素数据并生成视频时序信号
Encoder struct drm_encoder 编码器,将CRTC输出的并行像素数据编码为串行信号(如TMDS、LVDS、eDP)
Connector struct drm_connector 物理连接器,代表HDMI、DP、VGA、DSI等实际接口,检测热插拔和EDID
Plane struct drm_plane 硬件图层,支持叠加多个framebuffer实现合成(如光标层、视频层)
Bridge struct drm_bridge 桥接芯片,用于连接Encoder与Connector之间的信号转换(如HDMI转DP)

1.5 KMS对象关系图

下图展示了KMS核心对象之间的连接关系,以及它们如何协同完成显示输出:

KMS 核心对象关系图 Framebuffer 像素数据存储 Plane 硬件图层合成 CRTC 显示时序控制器 Encoder 信号编码器 Connector 物理接口 + 热插拔 绑定 扫描 输出 连接 一个CRTC可绑定多个Plane 一个Encoder连接一个Connector

1.6 用户空间与内核空间交互

用户空间的显示服务(如Wayland Compositor、Xorg)通过DRM提供的系统调用接口与内核交互。核心交互机制包括:

交互方式 系统调用 用途
设备打开 open("/dev/dri/card0") 获取DRM设备文件描述符
查询能力 ioctl(DRM_IOCTL_GET_CAP) 查询驱动支持的特性(如是否支持atomic)
资源枚举 ioctl(DRM_IOCTL_MODE_GETRESOURCES) 获取CRTC、Encoder、Connector、Framebuffer列表
内存分配 ioctl(DRM_IOCTL_GEM_CREATE) 创建GEM缓冲区对象
内存映射 mmap(..., fd, ...) 将GEM对象映射到用户空间地址
模式设置 ioctl(DRM_IOCTL_MODE_SETCRTC) 传统方式设置显示模式(Legacy API)
原子提交 ioctl(DRM_IOCTL_MODE_ATOMIC) 原子方式提交多个KMS对象状态变更
页面翻转 ioctl(DRM_IOCTL_MODE_PAGE_FLIP) 无撕裂的帧缓冲区切换

1.7 用户空间与内核空间交互流程

下图展示了一个典型的显示提交流程中,用户空间与内核空间之间的交互步骤:

用户空间与内核空间交互流程 用户空间 (Compositor) 内核空间 (DRM Core + Driver) 1. open("/dev/dri/card0") 返回 fd,初始化 drm_file 3. ioctl(GEM_CREATE) 分配buffer 分配GEM对象,返回handle 5. mmap() 映射到用户空间 建立页表映射,返回用户地址 7. ioctl(MODE_ATOMIC) 提交显示 atomic check + commit 生效 所有ioctl调用通过DRM Core分发,最终由具体驱动回调处理硬件操作 原子提交是KMS的核心API,将在后续章节详细讲解

1.8 libdrm:用户空间封装层

libdrm是DRM内核接口的用户空间封装库,为应用程序提供更友好的编程接口。它封装了底层的ioctl调用,并提供类型安全的API:

// libdrm 使用示例:打开设备并获取资源
#include <xf86drm.h>
#include <xf86drmMode.h>

int fd = drmOpen("i915", NULL);
drmModeRes *res = drmModeGetResources(fd);

// 遍历连接器
for (int i = 0; i < res->count_connectors; i++) {
    drmModeConnector *conn = drmModeGetConnector(fd, res->connectors[i]);
    if (conn->connection == DRM_MODE_CONNECTED) {
        printf("Connector %d: %s\n", conn->connector_id, 
               drmModeGetConnectorTypeName(conn->connector_type));
    }
    drmModeFreeConnector(conn);
}
drmModeFreeResources(res);
drmClose(fd);
注意事项: 直接使用ioctl系统调用需要自行处理数据结构填充和错误码解析,极易出错。生产环境应始终使用libdrm库,它提供了参数校验、内存管理和向后兼容性保障。libdrm是Mesa、Wayland、Xorg等所有主流图形栈的基础依赖。

1.9 本章小结

  • DRM架构 采用分层设计:用户空间 → 系统调用 → DRM Core → 子模块(GEM/KMS) → 驱动 → 硬件。
  • GEM 负责图形缓冲区管理,提供分配、共享、映射和GPU访问能力。
  • KMS 管理显示输出,核心对象包括CRTC、Encoder、Connector、Plane和Bridge。
  • 用户空间交互 通过open/ioctl/mmap系统调用完成,libdrm提供封装层简化开发。
  • 原子提交 是KMS的现代API,支持一次性校验和提交多个KMS对象的状态变更,避免闪烁和撕裂。