3. Linux图形栈全景
理解 DRM/KMS 之前,必须先建立一张“全景地图”。Linux 图形栈是一条从用户态应用到显示控制器寄存器的纵向分层链路,每一层只解决特定问题,并通过明确定义的接口把工作交给下一层。很多初学者把“显卡驱动”“显示驱动”“窗口系统”混为一谈,导致后续分析 KMS 初始化时找不到坐标。本节把整条链路拆开,说明各层职责、数据流向以及 DRM/KMS 与 Framebuffer 的真实关系。
一、核心原理:分层与职责边界
Linux 图形栈大致可分为用户态与内核态两大部分。用户态负责“画什么、怎么组织窗口”,内核态负责“把像素送到屏幕、管理显存与显示时序”。
- 应用程序:使用 OpenGL、Vulkan、GTK/Qt 等工具包绘制内容,它只关心自己的窗口或表面,不关心屏幕有几个、如何扫描输出。
- 窗口系统 / 显示服务器:如 X11(Xorg)、Wayland(Weston、KWin、Mutter)。它管理窗口层级、输入事件、合成,并决定最终哪块缓冲区被送到显示控制器。
- 图形 API 与用户态驱动:Mesa 提供 OpenGL/Vulkan 实现,把应用命令翻译成 GPU 可执行的命令流,并调用内核 DRM 接口提交。
- DRM/KMS 子系统:内核中的 Direct Rendering Manager 与 Kernel Mode Setting。DRM 管理 GPU 与显存对象(GEM/TTM),KMS 管理显示管线:CRTC、Encoder、Connector、Plane。
- GPU 驱动与显示驱动:如 i915、amdgpu、msm、rockchip-drm。它们把 DRM/KMS 的抽象对象映射到具体硬件寄存器。
- Framebuffer:一块内存缓冲区,保存屏幕像素。它既可以是 DRM 管理的 GEM 对象,也可以是传统 fbdev 的 /dev/fb0。
二、数据流与调用链
以 Wayland 合成器为例,一次典型显示刷新大致经历如下路径:应用渲染到 EGLSurface → Mesa 将命令提交给 GPU → 合成器把多个窗口合成为一张最终图像 → 通过 DRM 的 atomic 接口提交到 KMS → KMS 配置 CRTC 扫描该 Framebuffer → Encoder 转换为 HDMI/DP 信号 → Connector 输出到显示器。
应用 (OpenGL/Vulkan)
↓ EGL / Vulkan WSI
Mesa 用户态驱动
↓ DRM ioctl (GEM 创建、execbuffer)
内核 DRM 核心
↓
GPU 驱动 (i915/amdgpu/...)
↓
KMS: Plane → CRTC → Encoder → Connector
↓
显示接口 (HDMI/DP/eDP/MIPI-DSI)
↓
物理屏幕
这条链路中,KMS 的四个对象是理解初始化的关键:Plane 提供图像层,CRTC 负责扫描时序与像素输出,Encoder 把像素流转换为接口信号,Connector 表示物理输出端口及其连接状态。DRM 初始化时,驱动会枚举这些对象并建立它们之间的可能组合。
三、DRM/KMS 与 Framebuffer 的关系
传统 fbdev 只提供一个线性帧缓冲,应用直接写 /dev/fb0。它简单,但无法处理多图层、页面翻转、多显示器、GPU 渲染同步。DRM/KMS 则把 Framebuffer 抽象为 GEM 对象,并允许通过 ioctl 动态创建、映射和提交。KMS 的 atomic 模式进一步把“一次显示配置”打包成不可分割的事务,保证 Plane、CRTC、Connector 的状态一致切换。
| 层次 | 典型组件 | 核心职责 | 对外接口 |
|---|---|---|---|
| 应用层 | 浏览器、游戏、Qt/GTK | 产生绘制命令与窗口内容 | OpenGL/Vulkan/工具包 API |
| 显示服务器 | Xorg、Wayland 合成器 | 窗口管理、合成、输入分发 | X11 协议、Wayland 协议 |
| 用户态图形驱动 | Mesa、NVIDIA 用户态 | 命令翻译、内存管理、WSI | DRM ioctl、GBM |
| 内核 DRM/KMS | drm.ko、KMS 核心 | 显存对象、显示管线、模式设置 | /dev/dri/card0、sysfs |
| 硬件驱动 | i915、amdgpu、msm | 寄存器操作、时钟、电源 | 内核内部函数 |
| 显示硬件 | GPU、显示控制器、PHY | 扫描输出、信号编码 | HDMI/DP/eDP/MIPI-DSI |
四、典型场景:一次页面翻转
假设合成器要显示下一帧。它先通过 GBM 从 DRM 分配两个 Framebuffer(双缓冲),渲染完成后调用 drmModeAtomicCommit,把新的 Framebuffer 绑定到 Primary Plane,并请求页面翻转。内核在垂直消隐期切换扫描地址,旧缓冲被释放回合成器。整个过程用户态不直接碰寄存器,也不直接写 /dev/fb0。
# 查看当前 DRM 设备与连接器状态
ls /dev/dri/
cat /sys/class/drm/card0-HDMI-A-1/status
modetest -M rockchip -c # 列出 Connector/CRTC/Plane
如果此时用 modetest 看到 CRTC 已绑定 Framebuffer,说明 KMS 已完成模式设置;若 status 为 disconnected,则 Connector 没有检测到显示器,后续初始化会停在探测阶段。
五、常见误区与复盘
误区二:把 GPU 驱动等同于显示驱动。GPU 驱动负责渲染与显存,显示驱动负责 CRTC/Encoder/Connector 时序,两者在 DRM 中协作但职责不同。
误区三:认为 Wayland 取代了 DRM。Wayland 是用户态协议,最终仍通过 DRM/KMS 提交扫描缓冲。
复盘时可以用一句话定位问题:应用画不出 → 查 Mesa/GPU;窗口不显示 → 查合成器;屏幕无信号 → 查 KMS 的 Connector/Encoder;花屏或撕裂 → 查 Framebuffer 格式与页面翻转。建立这条纵向链路后,后续分析 DRM 设备初始化、KMS 对象创建和驱动 probe 顺序时,就能准确判断每一步发生在哪一层。