很多人在 Ubuntu 服务器上折腾过这样一件事:机器明明没有接显示器,却非要跑一个需要图形界面的程序,或者要在 CI 环境里做界面自动化测试。装好系统后打开 VNC 一看,要么分辨率小得可怜,要么直接黑屏。这时候就该轮到虚拟显示驱动上场了。
虚拟显示驱动说白了就是给“没有物理屏幕的机器”装一块虚拟显卡,让系统以为有一台显示器连着。这样 X Server 能正常启动,桌面环境能加载,各种图形应用也有地方渲染。最常用的两个工具是xserver-xorg-video-dummy和Xvfb,这俩我都实际用过,踩过不少坑,今天把 Ubuntu 下安装虚拟显示驱动的完整过程、配置细节和排查经验一次性讲清楚。
1. 虚拟显示驱动到底解决什么问题
1.1 没接屏幕的服务器为什么还需要“显卡”
很多人一开始不理解:服务器本来就不需要屏幕,直接 SSH 进去跑命令行不就行了?问题是很多软件默认就离不开显示环境。比如 Chrome 做自动化截屏、Selenium 跑网页测试、Qt 应用做渲染验证、图像处理工具链需要调用 X11,这些程序启动时都会检测 DISPLAY 变量,找不到显示服务就直接崩溃退出。
物理显卡在这种情况下也没法帮你。服务器通常没有独立显卡,就算有,没接显示器时很多驱动会进入省电模式,或者干脆不再输出画面。再加上机房环境根本不可能给你接一台真实显示器,于是虚拟显示驱动就成了唯一可行的路。
它的原理不复杂:在内核和 X Server 之间模拟一个 frame buffer 设备,X Server 向这块“虚拟显卡”写像素数据,数据保存在内存里而不是真正输出到物理屏幕。后续通过 VNC、x11vnc、ffmpeg 甚至直接截图工具拿到的画面,都来自这块内存区域。
1.2 常见使用场景梳理
根据我实际经手的项目,虚拟显示驱动最有价值的场景大概有这么几类:
- 无头服务器跑图形化 CI 测试:代码仓库里跑 Playwright、Selenium 之类的端到端测试,浏览器不启动图形环境根本没法工作。虚拟显示驱动给了浏览器一个稳定的“显示器”。
- 远程桌面与 VNC 服务:用 x11vnc 共享桌面时,没有真实显示器就会黑屏或报错。虚拟显示驱动保证桌面始终有输出。
- 多分辨率并发测试:移动端、PC 端 UI 自动化测试经常需要不同分辨率,物理显示器要手动调,虚拟驱动直接写配置文件就能快速切换。
- 视频编码与流媒体推流:无头机器需要在黑场状态下渲染视频或推流,虚拟显示驱动可以提供固定分辨率、固定帧率的渲染空间。
- 深度学习与渲染农场节点:一些可视化验证脚本需要 OpenGL 上下文,虚拟显示驱动配合 Mesa 的软件渲染可以跑起来。
一句话总结:凡是程序要求“你必须有屏幕”而你手上又没有真屏幕的场景,虚拟显示驱动都是最省事的解法。
2. 方案选型:dummy、Xvfb、Weston headless 到底选哪个
2.1 xserver-xorg-video-dummy:最接近真实环境的 X11 方案
xserver-xorg-video-dummy是 Xorg 官方维护的驱动,安装后会在 X Server 启动时加载一个叫dummy的显卡驱动模块。它最大的特点是系统会以为存在一块物理显卡和一台物理显示器,X11 协议里的显示模式、屏幕尺寸、刷新率都会正常上报。
我之所以在日常工作中优先选它,是因为很多图形程序会检测硬件信息。用 software rendering 时部分程序会主动降级,而 dummy 驱动配合 Mesa 的 llvmpipe 渲染,几乎所有 OpenGL 应用都能正常跑起来。它的稳定性和兼容性比 Xvfb 好得多,尤其在跑 Electron、Chromium 这类重度图形应用时特别明显。
代价就是配置略繁琐。需要手写 xorg.conf,指定驱动类型、显示器参数、分辨率模式,文件写得不对分分钟踩坑。
2.2 Xvfb:轻量但别指望它能跑满 OpenGL
Xvfb(X Virtual Framebuffer)是另一个方案,它直接在内存里模拟帧缓冲,不需要写 xorg.conf,一条命令就能拉起一个虚拟显示:
Xvfb :99 -screen 0 1920x1080x24 & export DISPLAY=:99这个方案确实简单粗暴,适合快速跑测试脚本。但它有几个先天短板:
- 不支持 DRI 3 相关的硬件加速接口,虽然能跑 OpenGL,但路径较绕,性能也受限。
- 某些对显示驱动有强校验的软件会报错,因为它不会报告具体的显卡型号和驱动名。
- 没有虚拟显示器的 EDID 信息,部分应用读取显示器参数时会返回空值。
所以我的经验是:临时调试用 Xvfb,长期稳定跑服务用 dummy。如果你要配置开机自启、多分辨率切换、或者跑生产环境的自动化任务,dummy 是更靠谱的选择。
2.3 选型对照表
| 对比项 | xserver-xorg-video-dummy | Xvfb |
|---|---|---|
| 配置复杂度 | 需要手写 xorg.conf | 一条命令即可启动 |
| 虚拟硬件信息 | 完整上报显卡与显示器 | 无硬件信息上报 |
| OpenGL 兼容性 | 较好,配合 Mesa 稳定 | 可用,但部分应用兼容性差 |
| 多显示器支持 | 支持配置多个虚拟显示器 | 通过多个 screen 模拟,较麻烦 |
| 分辨率切换 | 改配置重载 X Server | 重启 Xvfb 进程 |
| 适合场景 | 长期服务、生产任务、桌面共享 | 临时测试、脚本调用 |
Weston headless 我也简单试过,它是 Wayland 协议下的无头渲染方案。如果你整个环境已经切到 Wayland,可以考虑,但对大多数 Ubuntu 用户来说 X11 体系下的 dummy 更成熟、社区资料更多,也不容易踩 Wayland 特有的兼容坑。
3. Ubuntu 下完整安装与配置过程
3.1 安装驱动与依赖
如果你用的是 Ubuntu 20.04 以上的版本,Xorg 服务本身基本预装,但 dummy 驱动可能需要手动安装。执行:
sudo apt update sudo apt install xserver-xorg-video-dummy xorg x11-utils mesa-utilsx11-utils主要提供xdpyinfo和xrandr这两个查询工具,验证虚拟显示是否生效离不开它们。mesa-utils里有glxinfo和glxgears,用来验证 OpenGL 渲染是否正常,这个后面会用到。
有些极简系统连 X Server 本体都没有,那还需要额外装xserver-xorg-core。装完后输入Xorg -version能看到版本号就说明 X Server 可以用了。
3.2 手工编写 dummy 驱动 xorg.conf
这是整个流程里最核心的一步。xorg.conf 是 X Server 的主配置文件,dummy 驱动的所有参数都靠它控制。我的习惯是先建一个独立配置文件:
sudo mkdir -p /etc/X11/xorg.conf.d sudo vim /etc/X11/xorg.conf.d/10-dummy.conf内容如下,这份配置我在多台机器上验证过,直接复用它基本能支撑 1920x1080 的虚拟桌面:
Section "Device" Identifier "DummyDevice" Driver "dummy" VideoRam 256000 EndSection Section "Monitor" Identifier "DummyMonitor" HorizSync 10.0 - 100.0 VertRefresh 10.0 - 200.0 Modeline "1920x1080" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +hsync +vsync EndSection Section "Screen" Identifier "DummyScreen" Device "DummyDevice" Monitor "DummyMonitor" DefaultDepth 24 SubSection "Display" Depth 24 Modes "1920x1080" EndSubSection EndSection几个参数值得解释一下。
VideoRam是给虚拟显卡分配的显存,单位是 KB。这里设 256000,即约 250 MB。1920x1080x24bit 的画面一帧约为 1920 * 1080 * 3 = 6220800 字节,约 6.2 MB,就算开双缓冲也绰绰有余。如果你的分辨率更高,比如 4K,建议直接按 512000 设置。
HorizSync和VertRefresh是显示器行频和场频范围,dummy 驱动没有真实显示器可以探测,所以必须手动给一个范围。10.0 到 200.0 已经能覆盖绝大多数 Modeline 的需求,设小了你后续想加新分辨率时会遇到“Mode out of range”的报错。
Modeline是重点。这一行定义了显示模式的具体时序参数,包括像素时钟、前肩、后肩、同步脉冲宽度。很多人不理解为什么要手动算这个,其实 dummy 驱动不读 EDID,就没有显示器主动上报时序,X Server 也不知道该输出什么格式,只能靠 Modeline 告诉它。上面这一条实际是对应标准 HDMI 1080p 的 CEA-861 时序:像素时钟 148.5 MHz,水平有效像素 1920,同步参数依次是 2008、2052、2200,垂直方向是 1080、1084、1089、1125。这是行业内约定俗成的标准值,直接抄没问题。
3.3 自定义分辨率的计算方法
这里得岔开讲一下 Modeline 的计算逻辑,否则你换个非标分辨率就只能瞎试了。
Modeline 格式是:
Modeline "名称" 像素时钟 水平有效 水平前肩 水平同步 水平总计 垂直有效 垂直前肩 垂直同步 垂直总计 [标志]像素时钟的公式约等于:
像素时钟 = 水平总像素 × 垂直总行数 × 刷新率以 1920x1080@60 为例,水平总计是 2200,垂直总计是 1125,60 Hz 帧率,那么像素时钟就是 2200 * 1125 * 60 = 148.5 MHz,跟上面标准值完全一致。
如果你想做 2560x1440@60,用手算比较费劲,建议直接用cvt命令自动生成:
cvt 2560 1440 60输出里会有一整行 Modeline,把它复制进配置文件替换原来的 Modeline 即可。如果是 4K,可以先跑cvt 3840 2160 60,但如果刷新率超过 60 得注意像素时钟不能过大,否则虚拟显卡可能吃不下。
在Screen部分的 SubSection 里填Modes "2560x1440",名称必须和 Modeline 的名称一致。系统会默认匹配第一个可用的模式,以后想切分辨率就靠xrandr --size 2560x1440。
配置完成后重启 X Server 或者重启机器,然后检查是否生效:
xdpyinfo | grep dimensions如果输出为dimensions: 1920x1080 pixels就说明虚拟显示驱动加载成功了。
4. 让虚拟显示开机自启并稳定运行
4.1 systemd 服务该怎么写
只启动一次还不够,生产环境需要开机自动拉起,不然服务器一重启你的自动化任务就全部瘫在那里。我在多数 Ubuntu 服务器上采用如下 systemd 单元配置:
sudo vim /etc/systemd/system/xvfb-dummy.service[Unit] Description=Xorg with dummy driver After=systemd-user-sessions.service After=network.target [Service] Type=simple Environment=DISPLAY=:1 ExecStart=/usr/bin/Xorg :1 -config /etc/X11/xorg.conf.d/10-dummy.conf -keeptty -noreset -nolisten tcp vt7 Restart=always RestartSec=5 User=root [Install] WantedBy=multi-user.target启动参数里几个值得注意的点:
-keeptty让 Xorg 占用当前虚拟终端而不是主动切走,vt7指定使用 tty7,这是传统图形界面所在的终端。-nolisten tcp禁掉 TCP 监听,避免无意义的网络暴露。
Restart=always配合RestartSec=5能应对 X Server 偶发崩溃,这个选项帮我避免过不少次凌晨跑挂的尴尬。
然后执行:
sudo systemctl daemon-reload sudo systemctl enable xvfb-dummy.service sudo systemctl start xvfb-dummy.service注意一个细节:如果你把DISPLAY=:1写进了服务文件,后续其他程序要想连接这个显示,也得在环境变量里带上DISPLAY=:1。很多人配置完服务发现程序还是连不上,大概率就是 DISPLAY 没对上。
4.2 VNC 远程桌面里的注意事项
dummy 驱动本身不提供远程画面传输能力,它只负责“渲染画面到内存”。要真正看到画面,得搭配x11vnc或Xvnc。我这里最常用的是 x11vnc:
sudo apt install x11vnc x11vnc -display :1 -forever -shared -rfbport 5900 -rfbauth ~/.vnc/passwd-forever让连接断开后服务不退出,-shared允许多个客户端同时连接。密码文件要先通过x11vnc -storepasswd生成一次。
这里有个经验:x11vnc 的输出帧缓冲依赖 Xorg 的画面,dummy 驱动本身没有硬件加速,远程桌面在高分辨率下画面刷新会偏慢,如果只用于鼠标键盘操作没多大问题,但看视频或者拖动画布会有卡顿。所以我建议虚拟显示分辨率不要盲目拉满,够用就行。
4.3 DISPLAY 变量和常见环境坑
在 bash 环境里,每次启动图形程序前要确保 DISPLAY 指向正确的显示编号。可以在全局配置里直接写:
echo 'export DISPLAY=:1' >> ~/.bashrc source ~/.bashrc如果程序是通过 supervisor 或 cron 启动的,注意这些工具默认不会加载用户的.bashrc,所以一定要在启动命令前显式加上环境变量,写成类似这种形式:
DISPLAY=:1 python3 /opt/test.py另一个常见坑是 Wayland 和 X11 的冲突。Ubuntu 22.04 默认 GNOME 桌面用的登录会话可能是 Wayland,但 Xorg 的 DISPLAY 变量仍然指向 X11 端口。如果你在一个没有真实桌面的服务器上只跑了 Xorg,那所有 Wayland 程序根本不会认DISPLAY。解决方法是优先保证虚拟显示是 X11 会话,Wayland 程序如果要强行跑,要么加--ozone-platform=x11之类参数,要么就用专门为 Wayland 准备的 headless 方案。这个不是今天的主角,先不展开。
5. 常见问题排查手册
5.1 分辨率不生效,始终只有 1024x768
这是最典型的坑。dummy 驱动刚启动时如果没有读取到正确的 Modeline,X Server 会退回内部默认模式,通常是 1024x768。
优先检查 xorg.conf 里的Modes "1920x1080"是否和 Modeline 名称严格一致,大小写、空格都可能造成不匹配。
接着用xdpyinfo查看所有可用显示模式:
xdpyinfo -display :1 | grep -A 100 "screen #0"如果模式列表里没有 1920x1080,说明 Modeline 没被正确加载。改用cvt重新生成 Modeline 并替换再试一次。
还有一种隐蔽情况:Xorg 启动时用了多个配置文件,且后面加载的配置覆盖了你的 dummy 配置。可以用Xorg -configure生成当前配置的完整 dump 看看实际生效参数。
5.2 启动时报错 failed to load module dummy
这种通常就是驱动包没装好,或者软件源里没有对应版本。先确认:
ls /usr/lib/xorg/modules/drivers/dummy_drv.so如果文件不存在,回到第 3.1 步重新安装xserver-xorg-video-dummy。Ubuntu 某些精简定制版本可能把 xorg-modules 整体阉割掉,直接执行:
sudo apt install xserver-xorg-video-dummy xserver-xorg-core装完重建模块索引,重新启动 Xorg。个别情况下还需要卸载后重装一次,清理残留的模块加载状态。
5.3 程序能启动但 OpenGL 渲染报错
报错信息通常是libGL error: failed to load driver: swrast或Mesa: warning: Failed to open swrast。
dummy 驱动本身不带渲染能力,OpenGL 靠 Mesa 的软件实现层 llvmpipe 完成。这个链路如果断掉,通常是系统里 Mesa 库版本不完整。安装修复:
sudo apt install libgl1-mesa-dri libglx-mesa0 mesa-utils装好后用glxinfo验证:
glxinfo -display :1 | grep "OpenGL renderer"能看到llvmpipe字样就说明软件渲染链路正常。如果验证通过但特定应用还报错,优先怀疑应用自己编译时禁用了软件渲染路径。
5.4 问题速查表
| 现象 | 优先排查点 | 常用解决手段 |
|---|---|---|
| 分辨率固定 1024x768 | Modeline 是否加载 | 用 cvt 重新生成,检查 Modes 名称 |
| 启动失败 failed to load dummy | 驱动包缺失 | 重装 xserver-xorg-video-dummy |
| OpenGL 渲染报错 | Mesa 软件渲染链路 | 安装 libgl1-mesa-dri |
| 程序连接不上 DISPLAY | 环境变量未生效 | 显式指定 DISPLAY=:1 |
| VNC 显示灰屏 | x11vnc 和 Xorg 端口不匹配 | 确认 x11vnc 的 -display 指向 :1 |
| systemd 启动后 X 崩溃 | 配置文件语法不合法 | Xorg -config离线测试配置 |
| 重启后服务没起来 | systemd 未 enable | sudo systemctl enable 服务名 |
排查的时候建议养成一个习惯:先看日志。Xorg 的日志默认在/var/log/Xorg.0.log,如果换了显示编号就是Xorg.1.log。这里面的信息远比“黑屏”“灰屏”这类表象直观,每次改完配置启动后都去扫一眼有没有大写的(EE)行,基本能定位八成问题。
6. 我能分享的实用经验
用了快三年的虚拟显示驱动,最大的体会是“先想清楚用途再选方案”。临时跑个测试脚本,Xvfb 一条命令搞定,谁也不会闲到配置 xorg.conf。但只要这个虚拟屏幕需要稳定存在、需要长时间供多个程序连接、需要自动恢复,dummy 驱动就是绕不开的正确选择。
还有一点容易被忽略:虚拟显示驱动只解决“有没有屏幕”的问题,它不解决“图形性能”的问题。CPU 软件渲染再优化也有上限,如果你真需要 GPU 加速,还是得走物理显卡透传那套路子,虚拟显示驱动不适合硬扛 3D 大场景。
最后再分享一个我自己常用的技巧:准备两份配置模板,一份是 1920x1080,一份是 3840x2160,需要切换分辨率时直接替换配置文件再重启服务,几分钟就能完成整套切换,比在真实显示器上还快。适合做多分辨率 UI 回归测试的同学。把这套配置纳入你的自动化基础设施,省下来的不只是时间,更是半夜被测试失败提示吵醒的命。