我有一台 Ubuntu 主机,常年放在机柜里,显示器早就不接了,远程桌面是唯一入口。问题很快暴露:Windows 上用 RDP 连过去,要么直接黑屏,要么只有 800x600 这种远古分辨率,UI 被拉伸得没法看。第一次遇到时,我差点下单一个 HDMI 显卡欺骗器,后来想想这东西本质就是一个伪装成显示器的“EDID 发射器”,既然它能用硬件骗显卡,软件理论上也能做到。实际折腾下来,Ubuntu 无显示器远程桌面的黑屏/低分辨率问题,完全可以不花一分钱解决,而且方案不止一种。
1. 无显示器黑屏的根源:显卡在等一份“报告”
1.1 EDID 和显卡输出初始化
要彻底理解这个问题的来龙去脉,得先说清楚一块显卡在没有显示器时到底在干什么。所有显示接口(HDMI、DP、DVI、VGA 等)都遵循一个比较原始的逻辑:显卡向接口发送探测信号,接口那头如果接了一个显示器,显示器会通过 DDC/CI 通道回传一份二进制数据,这份数据就是 EDID(Extended Display Identification Data)。
EDID 里记录着这个显示器的厂商、型号、物理尺寸、支持的分辨率列表、刷新率、像素时钟等关键参数。显卡拿到 EDID 后,才知道该给这个接口分配最高多少分辨率、用什么刷新率。换句话说,EDID 就是显示器写给显卡的“能力报告”。没有显示器插入时,显卡读不到这份报告,就会把这个显示接口标记为 disconnected,对应的输出管线完全关闭。
Xorg、Wayland 这类显示服务器启动时,会遍历所有显示输出设备。如果所有输出都处于 disconnected 状态,系统就找不到一个可以正常工作的虚拟显示设备。这时候表现最典型的是 Xorg:直接起不来,或者用最低的通用 VESA 模式硬撑。GNOME/Xorg 很多黑屏问题,根源就在这里——不是桌面环境坏了,而是显卡根本没有可用的输出目标。
1.2 远程桌面为什么被殃及
远程桌面的工作方式,本质上是在远端机器上创建一个真实的图形会话,然后把画面编码传输到客户端。无论你用的是 XRDP、VNC、GNOME Remote Desktop 还是 TeamViewer,远端都必须有一个“活着的”显示平面,这个显示平面依赖显卡驱动和显示服务器之间的输出协商。
没有显示器时,显卡驱动通常不会去主动初始化输出。Xorg 尝试创建 root window 时,可能只能得到 800x600 或者 1024x768 的默认帧缓冲;Wayland 环境下更尴尬,合成器没有可用的输出设备时,桌面甚至不渲染,RDP 客户端连上去看到的就是黑屏。你远程连接时看到的所谓“显示器”,其实是远端 GPU 虚构出来的一个软件输出对象,它没有任何物理形态,但依然需要一份可用的显示模式来做尺寸参考。
1.3 显卡欺骗器到底做了什么,我们能不能用软件替代
显卡欺骗器的原理,恰恰是针对 EDID 这一环下手。硬件欺骗器本身没有屏幕,但内置了一颗存储芯片,里面烧录了一份 EDID 数据。把它插到显卡的 HDMI/DP 口上,显卡就会认为有一台 1080P(甚至 4K)的显示器在线,于是正常启用输出,远程桌面的分辨率自然跟着恢复正常。
既然问题核心是“显卡读不到 EDID / 没有可用输出”,那软件方案就可以走两条路:
- 想办法让内核和驱动认为某个显示接口是连接状态,并给它提供一份虚拟 EDID;
- 绕过物理显卡输出,用虚拟显示驱动直接创建一块帧缓冲,假装有一个显示器。
前者适合 Intel/AMD/NVIDIA 集成或独显,后者则完全不依赖 GPU 型号,兼容性最广泛。下面就把两种路线分别展开,最后再说说现在 Ubuntu 桌面版自带的 GNOME 远程桌面方案为什么也值得先试一次。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内核级模拟:GRUB 强制输出 + EDID 固件
2.1 先确认显示接口和当前状态
内核级方案适合 GPU 本身没有异常、只是缺显示器的情况。开机后先通过 SSH 登录到 Ubuntu 主机,查看当前显示接口状态。Linux DRM 子系统把所有显示接口暴露在 /sys/class/drm/ 目录下,命名格式通常是 card0-HDMI-A-1、card0-DP-1、card0-VGA-1 这样。逐个查看状态:
bash复制for f in /sys/class/drm/card*/card*-*/status; do
echo "$f: $(cat $f)"
done
正常无显示器时,输出大概是:
code复制/sys/class/drm/card0/card0-DP-1/status: disconnected
/sys/class/drm/card0/card0-HDMI-A-1/status: disconnected
这里记录下你的接口名。比如你的显卡是 HDMI 口,可能叫 HDMI-A-1;如果是 Intel 核显,可能是 HDMI-1 或 DP-1。不同驱动命名略有差异,后面配置时要用到真实名字。
2.2 修改 GRUB:给接口一个默认分辨率
内核有一个 video 参数,可以在启动阶段给指定接口强制指定分辨率。修改 /etc/default/grub,找到 GRUB_CMDLINE_LINUX_DEFAULT 这一行,在里面加入强制模式:
bash复制GRUB_CMDLINE_LINUX_DEFAULT="quiet splash video=HDMI-A-1:1920x1080@60"
这里的语法是 video=<接口名>:<水平分辨率>x<垂直分辨率>@<刷新率>。如果你的接口是 DP,就换成 video=DP-1:1920x1080@60。执行:
bash复制sudo update-grub
sudo reboot
重启后再次查看接口状态,如果内核按参数理解了你的意图,xrandr 里就能看到 HDMI-A-1 connected 1920x1080,即使物理上并没有插显示器。这个方法在很多 Intel 核显机器上有效,但它依赖驱动是否遵守强制模式参数。实际经验是:部分 AMD 和 NVIDIA 驱动不一定买账,尤其是新平台,强制模式可能只是改变了模式列表,但接口状态仍然显示 disconnected。
2.3 直接“喂” EDID 固件给内核
比 video 参数更接近硬件欺骗器的做法,是让内核在启动时直接加载一份 EDID 固件,分配给指定接口。Linux 内核在 lib/firmware/edid/ 目录下预置了几种标准 EDID 固件,比如 1280x1024.bin、1024x768.bin。在多数 Ubuntu 发行版中,这些固件由 linux-firmware 包提供。确认一下:
bash复制ls /lib/firmware/edid/
如果只有标准分辨率,也可以先用 1280x1024 测试。在 GRUB 配置里加上:
bash复制GRUB_CMDLINE_LINUX_DEFAULT="quiet splash drm_kms_helper.edid_firmware=HDMI-A-1:edid/1280x1024.bin"
这个参数的含义是:内核在初始化 HDMI-A-1 接口时,不从物理显示器读取 EDID,而是直接使用固件里对应的 EDID 内容。驱动会认为这个接口已经连接了一台显示器,并且支持 EDID 中列出的所有分辨率。
如果不想受限于预置的 1280x1024,也可以手动生成一个 1920x1080 的自定义 EDID 固件。常见做法是用 edid-generator(GitHub 上有现成项目)在本地生成 1920x1080.bin,然后拷贝到 /lib/firmware/edid/ 目录。生成后重启,一般就能在 Xorg/Wayland 里看到完整的 1080P 输出选项。
2.4 什么情况下这条路走不通
内核参数 + EDID 固件,适合那些 GPU 输出管线本身能正常工作的机器。有一个很典型的失败场景:某些 NVIDIA 卡在无显示器时,驱动默认选择关闭所有输出,video 参数和 EDID 固件并不能强制让 NVIDIA 的私有驱动打开某个输出。这时候 xrandr 里可能只有 HDMI-A-1 这个名字,但分辨率列表仍为空,或者干脆连接口都看不到。
遇到这种情况不用死磕,直接上 Xorg Dummy 驱动方案。它有更大的兼容面,因为根本不依赖真实 GPU 输出。
3. Xorg Dummy 驱动:最广兼容的软件虚拟显示器
3.1 安装驱动并写一个最小配置
xserver-xorg-video-dummy 是 Xorg 的一个经典虚拟显示驱动,它的思路很直接:不驱动任何真实硬件,而是在内存里创建一块帧缓冲,模拟一个完整的显示器输出。对系统来说,这块虚拟显示器没有物理形态,但它能被 Xorg 正常枚举、分配分辨率、渲染桌面,完全满足远程桌面的需求。
安装命令:
bash复制sudo apt update
sudo apt install xserver-xorg-video-dummy
然后在 /etc/X11/xorg.conf.d/ 下新建一个配置文件,比如 10-headless-dummy.conf:
plaintext复制Section "Device"
Identifier "DummyGFX"
Driver "dummy"
VideoRam 204800
EndSection
Section "Monitor"
Identifier "DummyMonitor"
HorizSync 28.0-80.0
VertRefresh 43.0-70.0
EndSection
Section "Screen"
Identifier "DummyScreen"
Device "DummyGFX"
Monitor "DummyMonitor"
DefaultDepth 24
SubSection "Display"
Depth 24
Modes "1920x1080" "1280x1024" "1024x768"
Virtual 1920 1080
EndSubSection
EndSection
这里面 VideoRam 以 KB 为单位,204800KB 约等于 200MB,足够支撑 1920x1080 x 32bit 色深加双缓冲。Virtual 表示最大帧缓冲尺寸,如果你打算把桌面扩到 2K,这里也要相应调整。
需要注意的是:这个配置放在 /etc/X11/xorg.conf.d/ 下会全局生效,本机如果有物理屏幕插着,也会被 Dummy 驱动接管。它是一台纯无头机器的话没问题,但如果你偶尔还会接显示器调试,建议只在远程会话的 Xorg 实例里加载这个配置,或者用完就移除,避免来回拔插显示器时出现奇怪现象。
3.2 加入自定义 Modeline,把分辨率从 800x600 拉到 2K
Dummy 驱动的默认模式确实不高,很多人在没写 Modeline 时,远程桌面只有 1024x768。想要 1080P 甚至 2K,需要手动添加 Modeline。Modeline 是一行描述显示时序的参数,它告诉 Xorg 这个分辨率对应多少像素时钟、多少消隐区,格式比较长。一个常见的 1920x1080@60Hz 的 Modeline:
plaintext复制Modeline "1920x1080" 148.50 1920 2008 2052 2200 1080 1084 1089 1125 +hsync +vsync
把它放进 Section "Monitor" 里,然后 Modes 里写上 "1920x1080"。如果以后想上 2560x1440,可以额外加一条:
plaintext复制Modeline "2560x1440" 241.50 2560 2608 2640 2720 1440 1443 1448 1481 +hsync +vsync
生成 Modeline 最常用的命令是 cvt:
bash复制cvt 2560 1440 60
输出结果直接复制到配置文件的 Modeline 后面。重启 Xorg 后,xrandr 里就会出现对应分辨率。
3.3 接入 xrdp / x11vnc 的两种姿势
Dummy 驱动只是让 Xorg 有了一块虚拟显示,真正把它变成远程桌面的,还是需要接一个传输服务。
最常见的是 xrdp。安装:
bash复制sudo apt install xrdp
sudo systemctl enable --now xrdp
xrdp 默认启动后会监听 3389 端口,Windows 远程桌面客户端直接连。如果连接后发现分辨率仍然很低,往往不是 Dummy 驱动的问题,而是 xrdp 会话默认创建了一个新的 Xorg 实例,这个实例没有读到 Dummy 配置。这种情况可以手动测试 Dummy 配置是否生效:
bash复制sudo X :1 -config /etc/X11/xorg.conf.d/10-headless-dummy.conf
X :1 会在显示器编号为 1 的位置启动一个 Xorg 服务,如果启动过程没有报错,xrandr 就能看到 1920x1080。另一种姿势是用 x11vnc,适合不想用 RDP 协议的场景:
bash复制sudo apt install x11vnc
sudo X :1 -config /etc/X11/xorg.conf.d/10-headless-dummy.conf &
sleep 3
x11vnc -display :1 -rfbport 5900 -forever -shared
VNC 客户端连到 5900 端口,就能看到一个完整的虚拟桌面。我自己最常用的是 xrdp,因为 Windows 自带客户端不需要额外软件,连接体验也流畅一些。
4. GNOME 远程桌面的无头虚拟输出:官方路线
4.1 用 grdctl 在没有显示器的情况下开启 RDP
除了 Dummy 驱动,Ubuntu 22.04 之后的桌面版还内置了 GNOME Remote Desktop,也就是系统设置里那个“远程桌面”开关。很多人以为这个功能必须在有物理显示器时才能用,实际上它有一套独立于物理屏幕的虚拟输出机制。在无头机器上,我们需要用命令行工具 grdctl 来操作,而不是 GUI 设置。
安装组件:
bash复制sudo apt install gnome-remote-desktop
然后查看当前状态:
bash复制grdctl status
启用 RDP 并设置连接凭据:
bash复制grdctl rdp enable
grdctl rdp set-credentials yourusername yourpassword
grdctl rdp set-port 3389
不同 Ubuntu 版本 grdctl 的子命令略有差异,比如新版本可能要求指定 TLS 证书。你可以在执行前用 grdctl rdp --help 看一下实际的子命令清单。设置成功后,Windows RDP 客户端直接连接这台机器的 IP 和端口,输入刚才的账号密码即可。
GNOME Remote Desktop 底层走的是 Wayland 的虚拟输出,它能把远程客户端的画面尺寸动态告诉合成器,客户端调整窗口大小时,远端分辨率也会跟着变化。这是 Xorg Dummy 方案做不到的体验,因为 Dummy 的分辨率是固定的,想换分辨率得改配置重启 Xorg。
4.2 客户端黑屏的典型处理顺序
如果你用 GNOME Remote Desktop 连上去还是黑屏,不用急着卸载重装,按顺序排查:
- 确认 gnome-remote-desktop 服务在运行:
systemctl --user status gnome-remote-desktop,如果没有运行,执行systemctl --user enable --now gnome-remote-desktop。 - 确认当前确实是 Wayland 会话。GNOME Remote Desktop 对 Xorg 会话的支持不如 Wayland 会话完善。查看
echo $XDG_SESSION_TYPE,如果是x11,在 GDM 登录界面选择 Ubuntu on Wayland 再试。 - 确认锁屏状态。很多黑屏是锁屏界面没有正确渲染造成的,可以在客户端连接后在登录界面输入密码回车,或者暂时关闭锁屏测试:
gsettings set org.gnome.desktop.screensaver lock-enabled false。 - 检查分辨率协商。Windows RDP 客户端在“显示”选项卡里可能会强制使用“全屏”或者某个目标分辨率,如果远端虚拟输出不支持当前目标,黑屏就可能出现。手动把客户端分辨率调成 1920x1080,通常能明显缓解。
4.3 和 Dummy 驱动方案的选择逻辑
我的经验是:能走 GNOME Remote Desktop 就优先走它,因为它不影响物理 GPU 驱动,也不损失硬件加速。Dummy 驱动把整个图形栈放到软件渲染上,桌面动画、视频播放都会吃力,适合临时运维或者轻量操作。
如果 GNOME Remote Desktop 在你机器上不稳定,或者你用的是 XFCE、KDE 这样的非 GNOME 桌面,再退回 Dummy 驱动方案。Dummy 驱动的兼容性最好,几乎任何 Xorg 场景都能用,但代价是性能上限低,这是必须接受的。
5. 踩坑实录与最终取舍
5.1 黑屏到底怎么快速定位
很多人在无头 Ubuntu 上折腾远程桌面,最怕的就是黑屏。黑屏不是单一原因,可能出现在好几个环节。这里给一个我自己反复用的排查链路:
bash复制# 1. 看图形会话到底起没起
journalctl -b | grep -E "drm|dummy|gdm|xorg|wayland" | tail -50
# 2. 看 X socket 是否存在
ls /tmp/.X11-unix/
# 3. 看当前登录会话类型
loginctl list-sessions
如果 /tmp/.X11-unix 里没有 X socket,说明 Xorg 都没起来,那问题大概率出在显卡输出或 Dummy 配置上。如果 X socket 存在但远程桌面黑屏,问题通常在远程桌面服务本身。
手动启动 Xorg 是最直接的探针。命令行执行:
bash复制sudo X :2
如果这条能正常启动并保持运行,说明基本图形环境没问题;如果直接报 no devices detected,那就确认是显卡输出或驱动配置的问题。此时把 Dummy 配置放进去再试,能很快分清楚是不是缺显示设备。
5.2 xrdp 会话冲突和权限问题
xrdp 有个常见问题:同一个用户已经有一个 GNOME 桌面会话时,xrdp 再创建新会话会因为权限和会话冲突失败,表现就是连上后闪退或黑屏。因为 xrdp 默认不允许同一个用户同时开多个图形会话,它会去找已经存在的会话数据。
解决办法是先清掉已有会话:
bash复制sudo loginctl terminate-user yourusername
然后重新连接。如果你想避免这种冲突,也可以给 xrdp 配置一个独立用户,只用来远程登录,和本机管理员用户分开。这样的话即便本机有会话也不会打架。
还有一类权限问题是 /tmp/.X11-unix 和 /tmp/.Xauthority 的权限不对,导致远程会话无法读写认证文件。遇到这种问题,直接 sudo chmod 1777 /tmp/.X11-unix 再试。实际经验是,xrdp 用户目录下的 .Xauthority 或 .ICEauthority 所有权不对,也会导致登录后黑屏,用 chown username:username ~/.Xauthority 修正即可。
5.3 我的建议:三种方案如何搭配
折腾完这些方案后,我现在的固定搭配是这样的:如果机器上的 Ubuntu 是桌面版且使用默认 GNOME,优先用 GNOME Remote Desktop 作为主力远程入口,它的虚拟输出和 Wayland 结合得最好,动态分辨率体验远远优于 Xorg 方案。
如果 GNOME Remote Desktop 因为某些原因不可用,或者上层跑的是 XRDP、VNC 这类传统服务,就用 Xorg Dummy 驱动死死兜底。它虽然不够“漂亮”,但胜在稳定,几乎不存在“显卡不认”的问题。至于内核参数和 EDID 固件,我会把它当成锦上添花的选项——在 Intel 核显和部分 AMD 平台上挺好用,但和 NVIDIA 私有驱动配合时表现不稳定,不必花太多时间去死磕。
最后再说一个小技巧:Dummy 驱动配置里的 Virtual 参数可以开得比当前桌面大,比如设成 3840x2160,然后远程桌面里用 xrandr --fb 2560x1440 按需切换工作区域。这意味着你不需要为了某个临时需求反复改配置文件重启 Xorg,日常运维效率能高不少。无头远程桌面这件事,了解底层逻辑后其实不难,有了这三个方案,至少不会再被“没有显示器”卡住。
