x11vnc 启动报 XOpenDisplay failed 的完整排查与解决方案
2026/9/17 0:16:30 网站建设 项目流程

1. 故障现象:x11vnc 启动即退出,只有一行冷冰冰的报错

先说结论:x11vnc启动失败、终端只输出XOpenDisplay failed这种情况,绝大多数时候不是 x11vnc 本身坏了,而是它根本找不到、或者没有权限连接到你想要共享的那个 X display。我最初遇到这个问题时也走了弯路,一度怀疑是软件包版本冲突,折腾了快一个小时才反应过来。

当时的环境是这样的:一台 Ubuntu 20.04 的机器,装了 x11vnc,本地有物理显示器,想在局域网里用 VNC 客户端远程连过去操作桌面。执行x11vnc -forever -rfbauth ~/.vnc/passwd -rfbport 5900之后,终端里秒退,报错信息就一行:

XOpenDisplay failed.

没有多余的堆栈、没有提示尝试了哪个 display、没有权限相关的附加说明。干过运维的朋友都知道,越是这种"干净利落"的报错,越让人头疼,因为它把真正的原因全部藏起来了。真正排查起来,背后可能涉及 X server 的监听地址、DISPLAY环境变量、xauth授权机制、甚至 systemd 的用户会话权限,任何一个环节出问题,表现都可能完全一致。

这篇文章把我当时完整的排查链路、最终解决方案、以及对 x11vnc 连接机制的深入理解整理出来,希望能帮你少走几个小时的弯路。

2. XOpenDisplay 到底在打开什么:理解 X display 机制是排查的前提

既然报错信息指向XOpenDisplay,那就必须先搞清楚这个函数背后的机制,否则排查纯靠猜。X11 架构里的 display 并不是一个抽象概念,它是有具体地址的通信端点。

2.1 Display 的命名规则:从:0主机名:显示器.屏幕

在 X11 的世界里,一个 display 的完整标识符长这样:

[协议://]主机名:显示器编号[.屏幕编号]

日常见到的:0:1localhost:10.0都属于这个格式。省略主机名时,默认指的是本机。x11vnc启动时会在内部调用XOpenDisplay,参数默认取环境变量DISPLAY的值;如果这个变量没设置,或者设置的值指向了一个不存在的 display,那就直接返回失败。

这里有一个非常容易踩的坑:很多教程会告诉你"设置export DISPLAY=:0",但忽略了:0这个编号并不是固定的。系统里有多个 X server 在跑、或者用过Xvfb这类虚拟显示服务,:0很可能已经被占了,你的桌面实际跑在:1:2上。如果盲目把DISPLAY设成:0,命令照样是XOpenDisplay failed

2.2 本地 display 和远程 display 的访问差异

XOpenDisplay打开:0和打开192.168.1.10:0是两条完全不同的路径。

  • 打开本地:0:需要通过/tmp/.X11-unix/X0这个 UNIX socket 去连接 X server。
  • 打开远程的host:0:走的是 TCP 6000 端口(display 编号 + 6000)。

实际上 X11 的 TCP 监听在很多现代发行版里默认是关闭的,这也是 x11vnc 这类工具存在的意义之一——它直接通过本地 UNIX socket 连接 X server,再把画面用 VNC 协议转发出去,绕开了 X11 本身的网络传输限制。

所以,x11vnc 在本地使用时,理论上应该连:0这样的本地 display。如果系统里有多个 display,或者无头服务器上根本没有启动 X server,那XOpenDisplay failed就非常合理了。

2.3 环境变量 DISPLAY 的"传递陷阱"

通过 SSH 远程执行 x11vnc 时,这个坑出现的概率极高。SSH 登录进来默认是非交互式 shell,DISPLAY环境变量往往不会被设置。你可能会说"我明明在图形界面终端里能跑起来啊",问题就在这——x11vnc 需要在有图形会话的环境里启动。

我自己习惯用 SSH 远程排查,第一遍顺手敲了x11vnc ...,报错之后先想到了DISPLAY,用echo $DISPLAY一查,果然空的。设成:0继续跑,结果还是同样的报错。这就涉及下一个更隐蔽的坑:xauth 授权。

3. 逐步排查链路:从环境变量到授权机制,我踩过的每个坑

这一节按我当时实际排查的顺序来写,每一步都是真实操作过的,不是理论推演。

3.1 第一步:确认 DISPLAY 环境变量是否设置

先做的第一件事:

echo $DISPLAY

正常情况下,在图形桌面环境里应该输出:0:1这样的值。如果输出是空的,那就直接设置:

export DISPLAY=:0

设置完再跑一次 x11vnc,如果成功,问题就解决了。但我的情况显然没这么简单——设置了还是报同样的错。

这里有个小技巧:判断 DISPLAY 是否真的有效,可以先随便跑一个图形程序试试,比如xclock或者glxinfo。如果它们也报display相关的错,说明 DISPLAY 值本身有问题,跟 x11vnc 无关。

3.2 第二步:检查 X server 到底在不在跑,监听在哪里

既然DISPLAY=:0配了还不行,那就得查这个 display 是否真实存在。

ls -la /tmp/.X11-unix/

这个目录下存放的是本机所有 X server 的 UNIX socket 文件。正常的输出长这样:

total 0 drwxrwxrwt 2 root root 80 Jan 15 10:23 . drwxrwxrwt 22 root root 460 Jan 15 10:23 .. srwxrwxrwx 1 root root 0 Jan 15 10:23 X0

srwxrwxrwx里的s表示 socket,X0对应 display:0。如果这个目录是空的,或者没有X0文件,那说明根本没有 display:0在跑,x11vnc 找不到目标也就合理了。

我当时的机器上确实有X0,说明 X server 活着。那问题就转移到权限上了。

如果显示的是X1X2之类的,说明的 display 编号不是 0,把DISPLAY改成对应的编号再试。

3.3 第三步:xauth 授权,最隐蔽的元凶

X11 的授权机制是通过~/.Xauthority文件管理的,里面保存了当前用户连接 X server 所需的 cookie。x11vnc 启动时默认会用当前用户的~/.Xauthority去认证。

问题来了:如果你是通过 SSH 远程登录的,SSH 会话里的用户虽然是同一个,但XAUTHORITY环境变量可能没被正确设置,x11vnc 读取不到正确的 cookie,就会在认证阶段失败。有意思的是,这种失败在 x11vnc 上的表现形式经常就是XOpenDisplay failed,而不是更明确的Xauthority报错,非常误导人。

用下面这个命令确认当前会话的授权文件路径:

echo $XAUTHORITY

图形桌面会话里,这个值通常指向~/.Xauthority;用 sudo 或者 root 身份执行时,HOME 变化可能导致路径不对。另一个经典场景是 Ubuntu 上用sudo x11vnc启动,结果它以 root 身份去读/root/.Xauthority,里面当然没有你普通用户的 cookie。

解决方式是指定授权文件:

x11vnc -display :0 -auth ~/.Xauthority ...

如果连~/.Xauthority里有没有 cookie 都不确定,可以用xauth list查看:

xauth list

输出里应该能看到类似HOSTNAME/unix:0 MIT-MAGIC-COOKIE-1 ...这样的条目。unix:0条目对应的正是本机 display:0的授权 cookie。看到这个才算落实了。

3.4 第四步:检查是否有其他 VNC 服务占用资源

排查过程中我还碰到过一个干扰项:之前用vino-server或者别的 VNC 方案时,系统里可能存在残留进程占用 5900 端口。x11vnc 如果用默认端口启动,可能因为端口被占用而失败。虽然这种失败一般会报binding port相关的错,但保险起见还是排查一遍:

ss -tlnp | grep 5900

有输出说明端口被占。换个端口跑 x11vnc,或者先把占用进程停了。

3.5 一个容易忽略的细节:X server 是否允许当前用户访问

X server 对连接请求有两种控制方式:一种就是上面说的xauthcookie 校验;另一种是老的xhost主机列表控制。如果 X server 是通过xhost +或者xhost +localhost开放的,那就不太依赖 cookie;但大多数现代桌面环境默认只用 cookie 授权模式。

测试当前用户是否有权限访问 X display,最直接的方式是跑一个依赖 X 的小程序:

xdpyinfo | head -20

如果这个命令正常输出 display 的信息,说明当前会话访问 X server 是没问题的。如果它都报unable to open display,那基本可以断定是授权层面的问题。

4. 核心问题的最终定位:GDM 的 Xauthority 路径和 headless 环境的坑

经过上面一轮排查,我终于定位到了自己这个案例的根本原因:系统用的是 GDM 显示管理器,X server 是在 GDM 启动阶段由 gdm 用户拉起的,所以 Xauthority 文件根本不在普通用户的 home 目录下,而是躺在/run/user/121/gdm/Xauthority或者/var/run/gdm3/这类系统路径下。

普通用户通过 SSH 登录后,XAUTHORITY环境变量并没有指向这个文件,所以 x11vnc 拿不到认证 cookie,XOpenDisplay自然失败。

ps aux | grep Xorg查看 X server 进程的运行参数,能看到启动时带了-auth /run/user/121/gdm/Xauthority之类的参数,那个路径才是真正有效的授权文件。

确认之后,直接指定这个路径启动:

sudo x11vnc -display :0 -auth /run/user/121/gdm/Xauthority -forever -rfbauth ~/.vnc/passwd -rfbport 5900

一次就起来了。这个坑在 GDM 环境的 Ubuntu、Debian 系统上特别常见,换 LightDM 或 SDDM 的系统路径又会不一样,所以写死某个路径的做法并不通用,解决问题的关键还是"找到 X server 进程真正使用的 auth 文件"。

4.1 为什么 headless 服务器上这个问题更突出

如果你是在一台没有物理显示器的服务器上装 x11vnc,那 X server 跑在虚拟显示上,比如 Xvfb,或者通过 dummy 显卡驱动模拟。这时候 display 编号、auth 文件路径都会更"随意",没有标准答案。

这种情况下建议专门用一个 systemd service 来管理虚拟显示和 VNC 进程,把 auth 文件路径固定下来,而不是依赖系统自动生成的路径。我用过的一个组合方案:

Xvfb :1 -screen 0 1920x1080x24 -auth /tmp/xvfb.auth & export DISPLAY=:1 x11vnc -display :1 -auth /tmp/xvfb.auth -forever ...

这样管理和排查都清晰很多。

4.2 Ubuntu 20.04/22.04 上sudo x11vnc的典型失败模式

sudo启动 x11vnc 是一个非常经典的误区来源。普通用户的XAUTHORITY~/.Xauthority,sudo 之后 HOME 变成/root,读的就是/root/.Xauthority,自然找不到用户会话的 cookie。很多人设置好了DISPLAY=:0仍然失败,往往就是因为这个。

解决办法有两个:要么不 sudo,直接以普通用户身份跑 x11vnc,让它读用户自己的~/.Xauthority;要么 sudo 时保留 X 相关环境变量:

sudo -E x11vnc -display :0 ...

-E参数会保留当前环境变量。但注意,这只对DISPLAY有效,XAUTHORITY指向的文件如果只有 gdm 用户能读,普通用户还是没权限,这种情况就得靠刚才说的指定 auth 路径的方式了。

5. 三种通用解决方案对比:按场景对号入座

既然同一个报错可能对应不同根因,我把自己试验过的三种方案整理成表格,方便你按实际情况对号入座。

方案适用场景启动命令优点缺点
方案 A:设置 DISPLAY 后直接启动本地桌面终端直接执行,Xauthority 路径正常x11vnc -display :0 -forever ...最简,不涉及权限问题依赖桌面环境和用户会话
方案 B:指定 auth 文件远程 SSH 启动,GDM 等显示管理器托管 X serversudo x11vnc -display :0 -auth /run/user/121/gdm/Xauthority -forever ...解决绝大多数 GDM 环境问题路径因系统而异,无头场景不适用
方案 C:配合 Xvfb 虚拟显示纯服务器无显示器环境先启动 Xvfb,再指定 DISPLAY 启动 x11vnc完全可控,路径固定需要额外维护 Xvfb 进程

方案 A 听起来最简单,但适用条件其实很苛刻:必须是在桌面会话的终端里执行,或者通过 SSH 但正确导出了DISPLAYXAUTHORITY。我的建议是:远程环境一律直接用方案 B,先ps aux | grep Xorg拿到实际 auth 路径再说。

方案 B 的 auth 路径还有个常见变体:某些系统上 GDM 的 Xauthority 在/var/run/gdm3/目录下。如果ps命令里显示的不是/run/user/...,那就以实际显示为准。

方案 C 适合云服务器、虚拟机等没有物理显卡的环境,配合x11vnc -noxdamage参数效果更佳,因为虚拟显示下 damage 事件的模拟偶尔会出问题,导致画面刷新异常。

6. 定位 auth 文件的几个实用命令:别再猜路径了

上面反复提到要"找到 X server 实际使用的 auth 文件",这里把最实用的几条命令整理出来,每个都验证过可以直接用。

6.1 通过进程参数直接获取

ps -ef | grep -i [X]org

输出里重点看-auth参数后面的路径:

/usr/lib/xorg/Xorg -core :0 -seat seat0 -auth /run/user/121/gdm/Xauthority -nolisten tcp vt2 -novtswitch

这个路径就是启动 X server 时指定的授权文件,用它作为-auth参数基本不会错。

6.2 通过 logind 会话获取当前用户的 Xauthority

较新的系统上还可以用:

loginctl show-session $(loginctl | grep $(whoami) | awk '{print $1}') -p Active

不过这条命令更多是查会话状态,真正拿到 Xauthority 路径最直接的手段还是看 X server 进程参数。

6.3 验证 auth 文件的正确性

拿到路径后别急着启动 x11vnc,先用 xauth 验证一下:

XAUTHORITY=/run/user/121/gdm/Xauthority xauth list

能看到unix:0 MIT-MAGIC-COOKIE-1类似的条目,说明路径和 cookie 都在。配合验证:

XAUTHORITY=/run/user/121/gdm/Xauthority xdpyinfo | grep "name of display"

输出name of display: :0确认无误后,再启动 x11vnc,基本就稳了。

6.4 环境变量覆盖 vs. 参数指定

x11vnc 支持两种方式指定 auth 文件:

  • 环境变量:启动前export XAUTHORITY=/path/to/auth
  • 命令行参数:-auth /path/to/auth

两者优先级不同,命令行参数的优先级更高。我在脚本里习惯两种都不写死,而是从 X server 进程参数里动态解析路径:

AUTH=$(ps -ef | grep '[X]org' | grep -oP '(?<=-auth )[^ ]+' | head -1)

这样脚本在不同机器上部署时能自动适应路径变化,省去每次手动改的麻烦。

7. 解决之后:x11vnc 稳定运行的正确姿势

启动问题解决只是第一步,如果你也打算把 x11vnc 作为长期方案,下面这些细节值得一并处理。

7.1 用 systemd service 托管 x11vnc,关掉终端也不怕

手动启动的 x11vnc 会挂在你当前终端下面,SSH 断开、终端关闭,进程就可能被干掉。更稳妥的方式是注册成 systemd 服务。

新建/etc/systemd/system/x11vnc.service

[Unit] Description=x11vnc remote access service After=multi-user.target display-manager.service Requires=display-manager.service [Service] Type=simple User=root ExecStartPre=/bin/sh -c 'AUTH=$(ps -ef | grep "[X]org" | grep -oP "(?<=-auth )[^ ]+" | head -1); echo "AUTH_PATH=$AUTH" > /tmp/x11vnc_auth_path' ExecStart=/bin/bash -c 'source /tmp/x11vnc_auth_path && AUTH_PATH=$(cat /tmp/x11vnc_auth_path | cut -d= -f2); x11vnc -display :0 -auth $AUTH_PATH -forever -shared -rfbauth /etc/x11vnc.passwd -rfbport 5900 -noxdamage' Restart=always [Install] WantedBy=multi-user.target

生成密码文件:

sudo x11vnc -storepasswd your_password /etc/x11vnc.passwd sudo chmod 600 /etc/x11vnc.passwd

启用服务:

sudo systemctl daemon-reload sudo systemctl enable --now x11vnc

这个写法会自动从正在运行的 Xorg 进程里提取 auth 路径,不同系统上迁移都通用。-shared允许多个客户端同时连接,-noxdamage避免部分显卡驱动下的刷新异常。

7.2 远程连接时的三个重要参数:-forever-shared-noxdamage

这三个参数几乎是生产环境的标配:

  • -forever:客户端断开后服务不退出,继续等待新连接。没有这个参数,VNC 客户端一断开 x11vnc 就直接退出,非常烦人。
  • -shared:允许多个客户端同时连接同一个桌面。默认模式是单连接,第二个客户端会直接把第一个挤掉。
  • -noxdamage:禁用 X damage 扩展的使用。在部分虚拟显示或老旧显卡驱动上,damage 事件会失效导致画面不更新,禁用它反而更稳定。

其他值得关注的参数还有:

  • -wait 50:调整屏幕轮询间隔,默认 50ms,降低系统负载时可以调大。
  • -ncache 10:开启客户端缓存,提升滚屏和窗口移动时的流畅度。
  • -localhost:只监听本机,配合 SSH 隧道使用更安全。

7.3 权限不够时的应对:TCP port 5900 绑定失败怎么办

非 root 用户启动时,如果 5900 端口被其他进程占用,或者端口绑定权限受限,可能报bind相关错误。两个解决思路:

  • 换高位端口,比如-rfbport 5901,避开可能的冲突;
  • 通过/etc/default/x11vnc或 systemd 服务模板动态指定端口。

用 root 身份启动不存在端口绑定权限问题,但要格外注意 auth 文件路径,因为 root 不会自动继承用户的XAUTHORITY

7.4 安全加固:网络暴露面收窄

最后提醒一句安全相关的建议。VNC 协议本身是明文传输的,密码也很容易暴力破解,所以绝对不要把 5900 端口直接暴露到公网。推荐的做法是:

  • 只监听本机回环地址:-localhost
  • 通过 SSH 隧道连接:本地执行ssh -L 5900:localhost:5900 user@remote,然后 VNC 客户端连接localhost:5900
  • 保持 x11vnc 密码复杂度,定期更换

这套组合下来,即使远端机器没有公网 IP,也能安全地完成远程桌面访问。

后记:后来我在另外几台新部署的机器上又遇到过几次同样的报错,每次排查步骤基本一致——看 DISPLAY、看 socket、看 auth 路径。如果你按上面的顺序走一遍还解决不了,大概率是 X server 本身服务状态异常了,systemctl status display-manager看一眼,必要时重启显示管理器再试。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询