1. DRM 框架概述:Linux 图形栈中的位置与核心组件
DRM(Direct Rendering Manager)是 Linux 内核中负责图形显示与渲染管理的核心框架。它位于内核空间,向上为用户态的图形栈(如 Mesa、Wayland、Xorg)提供标准接口,向下直接操作 GPU 和显示控制器硬件。理解 DRM 在 Linux 图形栈中的位置及其核心组件,是进行显示性能调优与帧率配置的基础。
1.1 DRM 在 Linux 图形栈中的位置
Linux 图形栈从用户态到内核态通常分为三层:应用层、合成/窗口管理层、内核驱动层。DRM 处于最底层的内核驱动层,直接与硬件交互。
1.2 DRM 核心组件详解
DRM 框架由四个核心子系统构成:DRM Core、KMS、GEM 和 Dumb Buffer。它们各司其职,共同完成从内存管理到显示输出的完整链路。
| 组件 | 全称 | 核心职责 | 用户态接口 |
|---|---|---|---|
| DRM Core | Direct Rendering Manager Core | 提供基础框架、设备管理、文件操作、IOCTL 分发 | open() / ioctl() / mmap() |
| KMS | Kernel Mode Setting | 显示模式设置、分辨率/刷新率配置、页面翻转、VBlank 控制 | drmModeGetResources() / drmModeSetCrtc() |
| GEM | Graphics Execution Manager | 显存分配与管理、GPU 缓冲区共享、同步对象 | drmIoctl(DRM_IOCTL_GEM_CREATE) |
| Dumb Buffer | Dumb Buffer | 为无需 GPU 加速的简单应用提供连续物理内存帧缓冲 | drmIoctl(DRM_IOCTL_MODE_CREATE_DUMB) |
1.3 DRM Core:框架的基石
DRM Core 是整个 DRM 框架的入口和基础设施。它负责注册 DRM 设备节点(/dev/dri/card0)、管理打开文件的上下文、分发 IOCTL 请求到对应的子模块(KMS、GEM 等)。
- 设备注册: 每个 GPU 或显示控制器对应一个
drm_device结构体,通过drm_dev_register()注册到系统。 - IOCTL 路由: 根据命令号将用户态请求分发到 KMS、GEM 或 Dumb Buffer 的处理函数。
- 驱动模型: 提供
struct drm_driver回调接口,硬件厂商只需实现这些回调即可适配 DRM 框架。
ioctl() 调用(如每帧一次页面翻转)会引入上下文切换开销。在调优时,应关注 perf 统计中 drm_ioctl 的耗时占比。
1.4 KMS:显示模式设置与帧率控制
KMS(Kernel Mode Setting)是 DRM 中直接控制显示输出参数的子系统。它负责配置显示控制器的时序、分辨率、刷新率,并管理页面翻转(Page Flip)操作。
KMS 的核心对象包括:
- CRTC: 显示控制器,负责生成视频时序信号。每个 CRTC 对应一个独立的显示管道。
- Encoder: 编码器,将 CRTC 输出的像素数据转换为特定接口信号(如 HDMI、DP、LVDS)。
- Connector: 物理连接器,代表实际的显示接口(如 HDMI-A-1、DP-2)。
- Plane: 硬件图层,支持叠加多个帧缓冲区进行合成,减少 CPU 合成开销。
1.5 GEM:显存管理与缓冲区共享
GEM(Graphics Execution Manager)是 DRM 的显存管理子系统。它提供了一套统一的 API 用于分配、释放和共享 GPU 缓冲区对象(BO)。
- 缓冲区创建: 通过
DRM_IOCTL_GEM_CREATE分配指定大小的显存,返回一个句柄(handle)。 - 内存映射: 通过
mmap()将 GPU 缓冲区映射到用户态地址空间,供 CPU 读写。 - 共享与导出: 通过
DRM_IOCTL_GEM_FLINK或 dma-buf 机制,在不同进程或不同 DRM 设备间共享缓冲区。 - 同步对象: 支持
drm_syncobj用于 GPU 与 CPU 之间的同步,避免帧撕裂。
GEM_CREATE 和 mmap 操作会引入延迟。生产环境中应使用缓冲区池(Buffer Pool)复用已分配的缓冲区,避免每帧重新分配。
1.6 Dumb Buffer:轻量级帧缓冲方案
Dumb Buffer 是 DRM 为不需要 GPU 加速的简单应用(如嵌入式设备的 framebuffer console、轻量级 UI)提供的帧缓冲机制。它分配的是连续的物理内存,CPU 可以直接访问。
| 特性 | Dumb Buffer | GEM Buffer |
|---|---|---|
| 内存类型 | 连续物理内存 | GPU 显存(可能不连续) |
| GPU 加速 | 不支持 | 支持 |
| 适用场景 | 简单 2D 显示、控制台 | 3D 渲染、视频播放、合成器 |
| 分配接口 | DRM_IOCTL_MODE_CREATE_DUMB |
DRM_IOCTL_GEM_CREATE |
| 性能特征 | 低延迟、低吞吐 | 高吞吐、支持 DMA |
Dumb Buffer 的典型使用流程如下:
// 1. 创建 Dumb Buffer
struct drm_mode_create_dumb create = {0};
create.width = 1920;
create.height = 1080;
create.bpp = 32;
ioctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &create);
// 2. 获取偏移量用于 mmap
struct drm_mode_map_dumb map = {0};
map.handle = create.handle;
ioctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &map);
// 3. 映射到用户态
uint32_t *fb = mmap(NULL, create.size, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, map.offset);
// 4. 将 Dumb Buffer 添加为 Framebuffer
uint32_t fb_id;
drmModeAddFB(fd, 1920, 1080, 24, 32, create.pitch, create.handle, &fb_id);
// 5. 设置 CRTC 显示
drmModeSetCrtc(fd, crtc_id, fb_id, 0, 0, &connector_id, 1, &mode);
1.7 组件协作流程:从帧缓冲区到屏幕显示
理解四个组件如何协同工作,是后续调优的前提。一个典型的显示帧提交流程如下:
- 缓冲区分配: 用户态通过 GEM 或 Dumb Buffer 分配帧缓冲区。
- 渲染: CPU 或 GPU 将内容渲染到缓冲区。
- 提交: 用户态调用
drmModeSetCrtc()或drmModePageFlip()将缓冲区提交给 KMS。 - 页面翻转: KMS 在 VBlank 期间切换 CRTC 的扫描缓冲区指针,实现无撕裂显示。
- 同步: 通过 VBlank 事件或同步对象通知用户态下一帧可以开始渲染。
1.8 本章小结
本章介绍了 DRM 框架在 Linux 图形栈中的核心位置,并详细剖析了四大核心组件:
- DRM Core 提供基础框架和 IOCTL 路由。
- KMS 负责显示模式设置和帧率控制。
- GEM 管理 GPU 显存和缓冲区共享。
- Dumb Buffer 提供轻量级连续内存帧缓冲方案。