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框架在内核空间中的分层结构,以及用户空间通过系统调用与内核交互的路径:
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核心对象之间的连接关系,以及它们如何协同完成显示输出:
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 用户空间与内核空间交互流程
下图展示了一个典型的显示提交流程中,用户空间与内核空间之间的交互步骤:
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对象的状态变更,避免闪烁和撕裂。