
去年做一款工业网关,硬件是块 ARM 核心板,连个 HDMI 接口都没引出来。产品需求偏偏要在上面跑一个 Web 管理界面,后端还依赖 Chromium 无头渲染。更麻烦的是,为了调试,图形团队需要在一台“无头”设备上跑 Weston 合成器并截取屏幕。
当时我脑子里只有一个念头:能给它凭空变出一块显示器吗?
答案是:能,而且 Linux 内核早就给你准备好了。这就是今天要聊的——虚拟显示控制器。
首先我们需要对齐一个认知:在 Linux 下,“显示”并不需要一块物理显示器。
我们熟悉的物理显示路径是:

而虚拟显示,就是把这条路的后半段直接搬进内存:

在 DRM/KMS 框架下,显示设备被抽象为一个个 DRM 设备,每个设备可以提供多个 CRTC、Encoder、Connector。物理显示设备当然有这些,但其实它们并不关心最终的目标是物理接口还是内存缓冲区。只要向 KMS 注册一个“假”的 connector,并且提供一个可以读写的 framebuffer,对用户空间来说,这就是一个完全可用的显示设备。
于是,虚拟显示控制器 应运而生:它是一个没有物理输出,只在内存中维护显示缓冲区的 DRM 驱动。
这个画面看起来很抽象,就好比一台打印机,本来该把文档打印到纸上,虚拟打印却把结果存成了 PDF。纸是看得见摸不着的物理实体,而 PDF 是存储于内存的虚拟载体——虚拟显示也是如此。

内核配置项:CONFIG_DRM_VKMS
VKMS 全称 Virtual Kernel Mode Setting,它完全依靠 CPU 进行软件渲染(CRTC 用 drm_crtc_helper 和 CPU 合成),不需要任何 GPU 硬件。它模拟出一个标准的 DRM 设备,提供连接器、CRTC、编码器,还支持 vsync 中断模拟和 page flip。
这个驱动最初是为 DRM 子系统的自测试而生的,但很快就被开发者玩出了花:比如在没有 GPU 的系统中提供图形界面环境、在 CI 中运行图形测试套件等。
启用 VKMS 后,你会看到一个 /dev/dri/cardX 设备,使用 modetest 等工具可以查看它的属性,甚至可以往上面输出画面,虽然你看不到——但可以把 framebuffer 导出成图像文件,就像屏幕截图一样。
另一个不得不提的方案是 virtio-gpu,这是为虚拟化场景设计的半虚拟化 GPU 驱动。它在 Guest 端提供一个 virtio 接口,Host 端通过 virglrenderer 等组件提供 OpenGL 加速。它也能在没有物理显示器的虚拟机里提供 DRM 设备,并且支持 3D 加速,性能远超纯 CPU 的 VKMS。
对比一下:
对于绝大多数嵌入式无头设备,我们更关注 VKMS,因为它不依赖任何宿主机,真正做到了“纯软”。
现代的 Wayland 合成器(Weston、Sway 等)需要一个 DRM 主设备才能启动后端。物理屏缺失时,Weston 直接报错退出。启用 VKMS 后,系统有一个 DRM 设备可用,Weston 就可以开心地在上面创建 surface 并合成,一切运行逻辑与接上真实显示器一模一样。
对于依赖图形服务才能正常启动的应用程序(比如 Flutter、Chromium Embedded),这就是救星。
在持续集成环境中,我们经常需要运行应用程序,截取界面,然后和基准图像对比。没有显示器怎么办?VKMS 允许你在容器或 headless 虚拟机里运行 Wayland 会话,然后使用 wayshot、grim 等工具对输出进行截图。
一切都在内存中发生,速度快,不需要任何外设,同时还能模拟多种分辨率,一次测试多套 UI。
你甚至可以通过加载多个 VKMS 实例模拟出多显示器环境,测试应用的扩展屏行为。结合 wayvnc 或 NoMachine,把虚拟屏幕的内容推流到远程桌面,就可以在笔记本电脑上操作无屏嵌入式设备的完整图形界面。
ffmpeg 的 kmsgrab 可以直接抓取 DRM framebuffer,即使显示是虚拟的,也能以 60fps 录制成视频,用于产品演示、bug 现场回放等。
下面我们在 Ubuntu 22.04(其他发行版类似)上实操一遍。
大多数桌面发行版的内核已经编译了 VKMS,但默认没有加载:
sudo modprobe vkms如果找不到模块,检查内核配置:
grep CONFIG_DRM_VKMS /boot/config-$(uname -r)# 期望输出:CONFIG_DRM_VKMS=m 或 =y如果是 =m 就说明已编译为模块,可以直接加载。如果没有,需要重新编译内核,在 Device Drivers → Graphics support 中启用 Virtual KMS (EXPERIMENTAL)。
加载后查看 DRM 设备:
ls /dev/dri/# 通常多出一个 card1 或 card2sudo modetest -M vkms你会看到类似输出:
Connectors:id encoder status name size (mm) modes encoders65 64 connected Virtual-1 400x270 1 64 modes: name refresh (Hz) hdisp hss hse htot vdisp vss vse vtot) 1024x768 60 1024 1048 1184 1344 768 771 777 806 65000 flags: phsync, pvsync; type: preferred, driver...这里有一个 Virtual-1 的 connector,状态为 connected,最大支持 1024x768 分辨率。这说明虚拟显示器已经在线。
安装 Weston:
sudo apt install weston使用 DRM 后端启动,指定输出为虚拟显示器:
weston --backend=drm-backend.so --output=Virtual-1如果没有自动加载 virtual 连接器,可以设置环境变量指定设备路径,但默认 weston 会选中 VKMS 提供的 card。
现在 Weston 在后台运行,虽然没有物理屏,但你可以在另一个终端用 weston-info 查看输出状态。此时 Weston 的 compositor 完全正常工作,可以启动客户端了。
启动一个简单的客户端,比如 weston-terminal:
# 在一个 weston 启动的终端里,或者通过远程方式:WAYLAND_DISPLAY=wayland-0 weston-terminal &然后使用 weston-screenshooter 截图:
weston-screenshooter --output=/tmp/virtual_screen.png(这里注意 weston 需要启用 screenshooter 插件,默认 weston.ini 里已包含)
那么这里的 /tmp/virtual_screen.png就是你的截图了,终端窗口的画面清晰地截取下来了,根本不需要物理显示器。
如果想要实时操作这个虚拟桌面,可以安装 wayvnc:
sudo apt install wayvncwayvnc 0.0.0.0 5900然后在你的笔记本上用 VNC 客户端连接 开发板IP:5900,就能看到一个完整的 Weston 桌面,所有操作都在内存中的虚拟显示器上渲染。
以上最好是不用apt安装,对于嵌入式linux系统提前把相关软件包安装好~
可以说虚拟显示控制器打破了我们对“显示”的刻板印象。它让我们意识到,显示的本质其实是内存中的像素缓冲区和合理的时序模拟。一旦把显示从物理中解放出来,嵌入式设备的调试、测试、远程操作就拥有了全新的自由度。
持续获取嵌入式实战干货,关注、标星 公众号不错过每一篇技术解析~
推荐好文点击蓝色字体即可跳转
☞专辑|Linux应用程序编程大全 ☞ 专辑|学点网络知识 ☞ 专辑|手撕C语言 ☞ 专辑|手撕C++语言
☞ 专辑|经验分享 ☞ 专辑|从单片机到Linux ☞ 专辑|电能控制技术 ☞ 专辑|嵌入式必备数学知识 ☞ MCU进阶专辑
☞ 经验分享