Linux DRM显示驱动开发与fbdev兼容集成 - 第1节

1.1 Linux 图形栈的演进历史:从 FBdev 到 DRM/KMS

Linux 图形子系统在近三十年的演进中,经历了从简单粗糙的帧缓冲驱动框架(FBdev)到现代、统一的直接渲染管理器/内核模式设置(DRM/KMS)的深刻变革。理解这一演进过程,是掌握 Linux 图形开发的基础。

1.1.1 早期时代:FBdev 框架

在 Linux 2.2 至 2.6 早期内核版本中,FBdev(Frame Buffer Device) 是唯一的显示输出框架。它提供了一个简单的字符设备 /dev/fb*,用户空间通过 mmap 直接操作帧缓冲内存。

FBdev 核心特征:
  • 单一平面:仅支持单个显示层,无法实现硬件合成。
  • 无模式设置抽象:分辨率、刷新率等设置通过 FBIO_SET_VAR ioctl 直接操作寄存器,缺乏原子性。
  • 无 GPU 加速:所有 2D/3D 渲染必须通过用户态驱动或软件模拟。
  • 多显示器支持差:扩展桌面需依赖 Xorg 的多个 /dev/fb* 实例,无法实现无缝克隆或扩展。

FBdev 的主要问题在于它把显示控制与帧缓冲内存管理耦合在一起,且缺乏对现代 GPU 硬件(如硬件光标、多图层、垂直同步控制)的抽象能力。随着 X.Org 和 Wayland 对合成、原子更新、GPU 共享的需求增长,FBdev 逐渐成为瓶颈。

FBdev 架构:用户空间直接操作帧缓冲 用户空间 (Xorg / 应用程序) mmap(/dev/fb0) → 直接写像素 内核空间 (FBdev 驱动) fb_ops: fb_read/fb_write/fb_ioctl/fb_mmap 硬件 (GPU / 显示控制器 / 帧缓冲内存) 无模式设置抽象 无 GPU 合成

1.1.2 现代框架:DRM/KMS 的诞生

DRM(Direct Rendering Manager) 最初是为 3D 加速设计的,但 Linux 2.6.29 引入的 KMS(Kernel Mode Setting) 组件彻底改变了显示控制方式。KMS 将模式设置、平面管理、垂直同步控制等关键功能移入内核,并提供统一的 API。

对比维度 FBdev DRM/KMS
设备节点 /dev/fb0 (字符设备) /dev/dri/card0 (DRM 主设备)
模式设置 ioctl 直接操作寄存器,无原子性 atomic API,支持事务性提交
显示平面 单平面,无硬件合成 多平面 (primary/overlay/cursor),硬件合成
GPU 加速 无内核抽象,依赖用户态 GEM/Dumb Buffer,支持 GPU 共享内存
多显示器 多个 /dev/fb* 实例,难以同步 单个 DRM 设备管理多个 CRTC/Encoder/Connector
垂直同步 无标准接口 Page Flip + VBlank 事件
关键里程碑: 2012 年 Linux 3.9 内核开始逐步弃用 FBdev 的 KMS 支持,要求所有新显卡驱动必须使用 DRM/KMS 框架。截至 Linux 6.x,FBdev 仅作为兼容层存在,核心显示功能完全由 DRM/KMS 接管。

1.2 用户空间与内核空间的交互模型

DRM/KMS 采用 客户端-服务器 模型,用户空间通过系统调用与内核 DRM 驱动通信。核心交互路径包括:设备打开与内存分配、模式设置与平面配置、渲染提交与页面翻转。

1.2.1 核心交互流程

  1. 打开 DRM 设备: 用户空间调用 open("/dev/dri/card0") 获取文件描述符。
  2. 查询资源: 通过 drmModeGetResources() 获取 CRTC、Encoder、Connector、Plane 列表。
  3. 分配帧缓冲: 使用 drmModeAddFB() 或 GEM 接口创建 dumb buffer。
  4. 配置模式: 通过 drmModeSetCrtc() 或 atomic ioctl 设置分辨率、刷新率。
  5. 渲染与提交: 用户空间渲染到 GEM 对象,通过 drmModePageFlip() 或 atomic commit 提交显示。
  6. 等待同步: 通过 drmWaitVBlank() 等待垂直消隐期,避免撕裂。
DRM/KMS 用户空间与内核空间交互模型 用户空间 (libdrm / Mesa / Wayland / Xorg) 1. open("/dev/dri/card0") 2. drmModeGetResources() 3. drmIoctl(DRM_IOCTL_MODE_ADDFB) 4. drmModeAtomicCommit() 5. drmModePageFlip() 6. drmWaitVBlank() 内核空间 (DRM Core + 驱动) drm_open() → drm_file 分配 drm_mode_getresources() drm_mode_addfb() → GEM handle drm_atomic_commit() → 验证+提交 drm_mode_page_flip_ioctl() drm_wait_vblank_ioctl() 硬件 (GPU / Display Controller / Monitor) 关键设计:原子操作与事件机制 • atomic commit 保证多个平面/CRTC 配置同时生效,避免中间状态 • page flip 通过 DRM 事件队列异步通知用户空间,支持 VBlank 同步 • GEM 提供 dma-buf 机制,实现跨进程/跨驱动共享显存

1.2.2 关键数据结构与 ioctl 映射

DRM 内核侧维护一组核心对象,用户空间通过 ioctl 操作这些对象。下表列出最常用的 ioctl 及其对应内核处理函数:

用户空间调用 ioctl 命令 内核处理函数 功能描述
drmModeAddFB() DRM_IOCTL_MODE_ADDFB drm_mode_addfb() 创建帧缓冲对象,关联 GEM handle
drmModeSetCrtc() DRM_IOCTL_MODE_SETCRTC drm_mode_setcrtc() 设置 CRTC 连接模式与帧缓冲
drmModeAtomicCommit() DRM_IOCTL_MODE_ATOMIC drm_mode_atomic_ioctl() 原子提交多个属性变更
drmModePageFlip() DRM_IOCTL_MODE_PAGE_FLIP drm_mode_page_flip_ioctl() 请求在下一个 VBlank 切换帧缓冲
drmWaitVBlank() DRM_IOCTL_WAIT_VBLANK drm_wait_vblank_ioctl() 等待垂直消隐期

1.2.3 现代用户空间栈:libdrm 与 Mesa

用户空间程序通常不直接调用 ioctl,而是通过 libdrm 库封装。libdrm 提供类型安全的 C API,隐藏了 ioctl 的细节。对于 3D 渲染,Mesa 使用 DRM 的 GEM 和调度接口实现 OpenGL/Vulkan 驱动。

典型调用链(Wayland + Mesa 合成器):
// 1. 打开 DRM 设备
int fd = open("/dev/dri/card0", O_RDWR);
drmModeRes *res = drmModeGetResources(fd);

// 2. 分配 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);

// 3. 添加帧缓冲
uint32_t fb_id;
drmModeAddFB(fd, create.width, create.height, 24, 32,
             create.pitch, create.handle, &fb_id);

// 4. 原子提交:设置 CRTC 连接该帧缓冲
drmModeAtomicReq *req = drmModeAtomicAlloc();
drmModeAtomicAddProperty(req, crtc_id, crtc->mode_id, mode_blob_id);
drmModeAtomicAddProperty(req, crtc_id, crtc->active, 1);
drmModeAtomicCommit(fd, req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
drmModeAtomicFree(req);

1.2.4 事件与异步通知

DRM 支持异步事件机制,用户空间通过 poll() 或 epoll() 等待内核事件。常见事件类型包括:

  • VBlank 事件: 每次垂直消隐期触发,用于帧率控制和撕裂避免。
  • Page Flip 完成事件: 异步页面翻转完成后通知用户空间,可释放旧帧缓冲。
  • 属性变更事件: 连接器热插拔、显示器 EDID 更新等异步通知。
注意: 在 atomic API 中,所有属性变更必须通过一次 DRM_IOCTL_MODE_ATOMIC 提交,不允许混合使用旧版 setCrtc/setPlane 与 atomic 调用。内核会严格校验提交的原子性,违反将返回 -EINVAL。

1.3 本章小结

Linux 图形子系统从 FBdev 演进到 DRM/KMS,核心变化在于:

  • 抽象层次提升: 从直接操作帧缓冲内存,升级为对象化的 CRTC/Encoder/Connector/Plane 模型。
  • 原子性保证: atomic API 确保多平面配置同时生效,消除撕裂和闪烁。
  • GPU 协同: GEM 和 dma-buf 实现显存共享,支持合成器与 GPU 渲染无缝协作。
  • 事件驱动: VBlank 和 Page Flip 事件机制为现代合成器(Wayland)提供低延迟、无撕裂的显示体验。