1. 课程导入
在嵌入式 Linux、车载座舱、工业 HMI 与移动终端中,显示子系统始终是连接 GPU、显示控制器与上层图形栈的关键枢纽。早期 Linux 内核用 FrameBuffer(fbdev) 提供了一套极简的显示抽象:一块线性显存、一组固定参数、一个 /dev/fb0 设备节点,应用通过 mmap 写入像素即可上屏。它简单、直接、稳定,在单图层、低分辨率、无合成需求的年代几乎无可替代。然而,随着多图层叠加、硬件光标、页面翻转、垂直同步、多屏异显、GPU 合成与安全显示等需求爆发,fbdev 的“单缓冲 + 全局状态”模型逐渐力不从心。于是,DRM(Direct Rendering Manager) 从内核图形子系统中成长起来,成为现代 Linux 显示的事实标准。
本课程的目标不是简单罗列 API,而是带你完成一次真实的工程迁移:理解 fbdev 的边界,掌握 DRM 的 KMS 模型,最终把旧驱动、旧应用、旧调试习惯平滑迁移到 DRM 体系。下面这张图概括了本节要建立的整体认知:从应用层到内核层,fbdev 与 DRM 在抽象层次、资源管理和显示控制上的根本差异。
一、核心原理:fbdev 与 DRM 的本质差异
fbdev 的设计哲学是“一块显存解决所有问题”。它把显示控制器抽象成一个字符设备,应用通过 ioctl(FBIOGET_VSCREENINFO) 获取分辨率、像素格式和时序,再 mmap 到用户空间直接写像素。这种模型在单图层场景下效率极高,但一旦需要多图层叠加,应用只能自己用 CPU 或 GPU 把多个图层“压平”到同一块缓冲,既浪费带宽又难以保证同步。
DRM 则把显示管线拆解为可组合的对象:CRTC 负责扫描输出与时序,Plane 代表可叠加的图层,Encoder 把像素流转换为 HDMI、MIPI-DSI 等信号,Connector 表示物理接口与 EDID 能力。帧缓冲不再是一块固定显存,而是通过 GEM 或 DMA-BUF 管理的可共享对象。提交方式也从“写寄存器”升级为 Atomic Commit:一次 ioctl 原子地更新多个 Plane、CRTC 和属性,内核在 VSync 边界统一生效,避免撕裂。
一句话总结:fbdev 是“单缓冲 + 全局状态 + 直接写屏”,DRM 是“多对象 + 原子提交 + 同步翻转”。迁移的本质,是把显示控制从“应用自己管”变成“内核统一管”。
二、方法流程:迁移的四个阶段
真实项目中的迁移不是重写一切,而是分层推进。建议按以下四个阶段执行:
- 现状盘点:确认当前使用的是 fbdev 驱动还是 DRM 驱动,列出应用依赖的
/dev/fb*、ioctl 和自定义扩展。 - 驱动层切换:优先启用内核已有的 DRM 驱动(如
msm、i915、rockchip、sun4i),保留 fbdev 兼容层fbdev emulation作为过渡。 - 应用层适配:把直接操作
/dev/fb0的代码替换为 libdrm 的drmModeSetCrtc、drmModePageFlip或 Atomic API;图形栈迁移到 EGL/GBM 或 Wayland。 - 调优与验证:用
modetest、kmscube、drm_info验证图层、时序、VSync 和功耗,回归多屏与热插拔场景。
# 查看当前显示设备与驱动
ls -l /dev/fb* /dev/dri/*
cat /sys/class/drm/card0/device/driver/module/drivers
# 使用 modetest 枚举 DRM 资源
modetest -M rockchip -c
modetest -M rockchip -p
# 查看 fbdev 兼容层是否启用
dmesg | grep -i "fbdev\|drm"
三、案例场景:车载 HMI 的多图层需求
某车载座舱项目早期使用 fbdev 驱动,仪表盘和中控屏各自占用一个 /dev/fb。当产品要求“仪表盘背景 + 报警弹窗 + 视频倒车影像”三层叠加时,fbdev 方案只能由 CPU 逐帧合成,导致 CPU 占用率飙升到 70% 以上,倒车影像延迟超过 200ms。迁移到 DRM 后,三个图层分别绑定到不同 Plane,由显示控制器硬件完成叠加,CPU 占用降到 12%,延迟稳定在 33ms 以内。这个案例说明:迁移的收益不只是“新”,而是把不可能变成可能。
四、误区与复盘
常见误区一:认为 DRM 只是“fbdev 的替代品”,把 drmModeSetCrtc 当成 FBIOPUT_VSCREENINFO 用。实际上 DRM 的资源生命周期、引用计数和提交语义完全不同,必须理解对象模型。
常见误区二:忽略 fbdev 兼容层的存在,直接删除旧接口,导致依赖 /dev/fb0 的遗留应用无法启动。正确做法是保留 CONFIG_DRM_FBDEV_EMULATION 作为过渡。
常见误区三:只改应用不改驱动,或只改驱动不改应用。迁移是双向的,必须同时梳理内核态与用户态的依赖链。
| 对比维度 | FrameBuffer(fbdev) | DRM / KMS |
|---|---|---|
| 设备节点 | /dev/fb0 |
/dev/dri/card0 |
| 缓冲模型 | 单线性显存,mmap 直写 | GEM / DMA-BUF,可共享、可导入 |
| 图层能力 | 无硬件图层,需软件合成 | 多 Plane 硬件叠加 |
| 同步机制 | 无 VSync,易撕裂 | Page Flip + Atomic Commit + VSync |
| 多屏支持 | 多个 fb 设备,独立管理 | 统一 CRTC/Connector 拓扑 |
| 典型调试工具 | fbset、cat /dev/urandom > /dev/fb0 |
modetest、kmscube、drm_info |
五、学习路线图
本课程后续将沿着“认知—驱动—应用—调优”的主线展开:先建立 DRM 对象模型与 KMS 管线认知,再深入驱动层的 drm_driver、atomic_helper 与 drm_panel,接着完成用户态 libdrm 与 EGL/GBM 的适配,最后进入多屏、热插拔、功耗与性能调优的实战。你不需要一次记住所有 API,但需要先建立一张清晰的“显示管线地图”——这正是本节 SVG 想帮你完成的事。
学习建议:在动手改代码之前,先用 modetest 在你的开发板上跑一遍,观察 CRTC、Plane、Connector 的实际数量和属性。真实硬件的拓扑,比任何文档都更有说服力。