一、问题现象
在 Rocky Linux 上通过 Rocket Software Exceed TurboX(ETX)运行 EDA/GUI 软件时,启动 Qt 程序报错:
1
2
3
Maximum number of clients reached
qt.qpa.xcb: could not connect to display etxnode12.icinfra.cn:10.0
qt.qpa.plugin: Could not load the Qt platform plugin "xcb" in "" even though it was found.
第一眼容易把问题判断成 Qt 的 xcb 插件缺失。但结合第一行:
1
Maximum number of clients reached
更准确的判断是:
当前 ETX/X11 Display 已经达到 X client 连接数上限,新启动的 GUI 程序无法再连接 X server。
xcb报错是后续表现,不是优先根因。
在 EDA 场景中,Virtuoso、Calibre、Verdi、Firefox、Java GUI、Qt GUI 等工具长期堆积在同一个远程桌面会话里,比较容易触发这个问题。
二、先理解几个概念
1. X client 不是“窗口数量”
X11 里的 client slot 是按连接计数,不是按窗口计数。
一个 X client 可以是:
- 一个有可见窗口的 GUI 程序;
- 一个没有顶层窗口的后台 helper;
- 一个隐藏窗口、托盘组件、剪贴板组件;
- 一个 Qt/Java/GTK 内部辅助进程;
- 一个 EDA 工具启动出来的子 GUI 进程;
- 一个远程显示代理进程。
所以:
1
X client 数量 != 桌面上能看到的窗口数量
2. xlsclients 不等于真实连接数
xlsclients 主要通过 X11 窗口树和窗口属性识别 client application。它可能看不到:
- 没有顶层窗口的 client;
- 没有设置
WM_COMMAND/WM_CLIENT_MACHINE属性的 client; - hidden / withdrawn 状态窗口;
- 短生命周期 client;
- 某些 Qt、Java、Chromium、EDA helper;
- 某些 ETX/X11 proxy 辅助连接。
因此,xlsclients 通常会低估真实 X client connection 数量。
3. lsof/ss 更接近真实连接压力
对于:
1
Maximum number of clients reached
这种问题,应该优先看 socket connection 数量。
例如:
1
lsof -nP -iTCP:6010 -sTCP:ESTABLISHED
或者:
1
ss -tanp state established | grep ':6010'
这些命令看到的是连接到 X server / ETX display 的 TCP socket,更接近真实消耗 X client slot 的数量。
三、为什么 lsof 看到 501 个,而 xlsclients 只有 240 个?
实际排查中出现了这种现象:
1
2
3
4
5
lsof -nP -iTCP:6010 -sTCP:ESTABLISHED
# 看到 501 个连接
xlsclients -display "$DISPLAY" | wc -l
# 只看到 240 个
这个差异是正常的。
原因如下:
1. lsof 看 socket,xlsclients 看可识别的 client application
lsof 看到的是 TCP 连接。
xlsclients 看到的是能够从 X11 属性中识别出来的 client。很多占用 X client slot 的连接,不一定能被 xlsclients 显示出来。
2. 一个应用可能打开多个 X connection
例如:
1
2
3
4
5
6
7
Virtuoso
Calibre RVE
Verdi
Firefox / Chromium
Java GUI
Qt GUI
Python Qt/Tk GUI
表面上可能只是一个主窗口,但内部可能有多个进程、多个 helper、多个 X connection。
3. ETX 场景存在 proxy 链路
ETX 场景里经常不是简单的:
1
Application -> X server
而是类似:
1
2
3
4
5
6
7
8
9
10
11
12
EDA/Qt/Virtuoso/Calibre GUI
|
| local X11 Unix socket
v
/tmp/.X11-unix/X1
|
v
etxproxy
|
| TCP / ETX display channel
v
etxnode12:6010
因此,不同位置看到的连接数量并不相同。
如果要判断 Maximum number of clients reached,应以真正触达 X server / ETX display 的连接数为准。
四、确认 DISPLAY 与端口
先看当前 shell 的 DISPLAY:
1
echo "$DISPLAY"
例如:
1
etxnode12.icinfra.cn:10.0
其中 display number 是 10。
X11 TCP 端口通常是:
1
6000 + display_number
所以:
1
:10.0 -> TCP 6010
可以用下面命令自动解析:
1
2
3
4
5
6
7
8
9
DISPLAY_HOST=${DISPLAY%%:*}
DISPLAY_NUM=${DISPLAY#*:}
DISPLAY_NUM=${DISPLAY_NUM%%.*}
DISPLAY_PORT=$((6000 + DISPLAY_NUM))
echo "DISPLAY=$DISPLAY"
echo "DISPLAY_HOST=$DISPLAY_HOST"
echo "DISPLAY_NUM=$DISPLAY_NUM"
echo "DISPLAY_PORT=$DISPLAY_PORT"
如果需要解析 IP:
1
2
DISPLAY_IP=$(getent ahostsv4 "$DISPLAY_HOST" | awk 'NR==1{print $1}')
echo "$DISPLAY_IP"
五、查看当前 X client 连接数
1. 在 ETX Connection Node / Display Node 上查看
假设 display 是:
1
etxnode12.icinfra.cn:10.0
对应 TCP 端口是:
1
6010
在 etxnode12 上执行:
1
sudo ss -Htan state established '( sport = :6010 )' | wc -l
查看明细:
1
sudo ss -Htanp state established '( sport = :6010 )'
用 lsof 查看:
1
sudo lsof -nP -iTCP:6010 -sTCP:ESTABLISHED
统计数量:
1
sudo lsof -nP -iTCP:6010 -sTCP:ESTABLISHED | awk 'NR>1' | wc -l
2. 按来源主机统计
1
2
3
4
5
6
7
sudo ss -Htan state established '( sport = :6010 )' \
| awk '{print $5}' \
| sed -E 's/:[0-9]+$//' \
| sort \
| uniq -c \
| sort -nr \
| head -50
这可以判断 501 个连接主要来自哪台应用服务器、计算节点或远程主机。
3. 按远端 IP 统计
1
2
3
4
5
6
7
sudo lsof -nP -iTCP:6010 -sTCP:ESTABLISHED \
| awk 'NR>1{print $9}' \
| sed -E 's/.*->([^:]+):.*/\1/' \
| sort \
| uniq -c \
| sort -nr \
| head -50
4. 在应用主机侧找具体进程
在运行 EDA/GUI 程序的 Rocky Linux 应用主机上执行:
1
2
3
4
5
6
7
DISPLAY_HOST=${DISPLAY%%:*}
DISPLAY_NUM=${DISPLAY#*:}
DISPLAY_NUM=${DISPLAY_NUM%%.*}
DISPLAY_PORT=$((6000 + DISPLAY_NUM))
DISPLAY_IP=$(getent ahostsv4 "$DISPLAY_HOST" | awk 'NR==1{print $1}')
sudo lsof -nP -iTCP@"$DISPLAY_IP":"$DISPLAY_PORT" -sTCP:ESTABLISHED
按进程统计:
1
2
3
4
5
6
sudo lsof -nP -iTCP@"$DISPLAY_IP":"$DISPLAY_PORT" -sTCP:ESTABLISHED \
| awk 'NR>1{print $1, $2, $3}' \
| sort \
| uniq -c \
| sort -nr \
| head -50
如果看到某个进程占用异常多,例如:
1
2
3
4
120 java 12345 user1
80 virtuoso 23456 user1
60 firefox 34567 user1
40 python 45678 user1
就应该优先排查该进程是否在反复创建 X connection,或者是否有残留 GUI/helper 进程没有退出。
六、如何理解 /tmp/.X11-unix/X1 与 etxproxy
排查时还看到类似输出:
1
2
3
u_str LISTEN 0 128 @/tmp/.X11-unix/X1 ... users:(("etxproxy",pid=56012,fd=9))
u_str LISTEN 0 128 /tmp/.X11-unix/X1 ... users:(("etxproxy",pid=56012,fd=10))
u_str ESTAB 0 0 @/tmp/.X11-unix/X1 ... users:(("etxproxy",pid=56012,fd=6))
这里要区分:
1
2
LISTEN != 已经占用的 client connection
ESTAB = 已经建立的连接
两条 LISTEN 通常是正常的:
1
2
@/tmp/.X11-unix/X1 # Linux abstract socket
/tmp/.X11-unix/X1 # filesystem pathname socket
这表示 etxproxy 同时监听 abstract socket 和 pathname socket。
如果只有一条 ESTAB,说明本地 Unix socket 侧只看到一个已建立连接。它不能推翻 TCP 6010 上看到的 501 个连接。
避免 grep 误匹配
下面这种写法容易把 X1、X10、X11 都匹配出来:
1
ss -xap | grep -E '/tmp/.X11-unix/X1|@/tmp/.X11-unix/X1'
建议写得更精确:
1
sudo ss -xapn | grep -E '(@|/tmp/)\.X11-unix/X1([[:space:]]|$)'
统计 Unix socket established 连接数:
1
2
3
sudo ss -H -xapn state established \
| grep -E '(@|/tmp/)\.X11-unix/X1([[:space:]]|$)' \
| wc -l
七、如何查看当前 X server 最大值
1. 找到监听 6010 的进程
在 ETX node 上:
1
sudo ss -ltnp | grep ':6010'
或者:
1
sudo lsof -nP -iTCP:6010 -sTCP:LISTEN
假设 PID 是 12345,查看启动参数:
1
2
3
sudo tr '\0' ' ' < /proc/12345/cmdline
echo
ps -fp 12345 -ww
重点看是否有:
1
-maxclients 256
或者:
1
-maxclients 512
2. 查 Xorg 日志
如果底层是标准 Xorg,可以查:
1
grep -Rhi "Max clients allowed" /var/log/Xorg.*.log ~/.local/share/xorg/Xorg.*.log 2>/dev/null
也可以查 X server 相关进程:
1
ps -efww | egrep 'Xorg|Xvfb|Xvnc|Xdummy|Xwayland|etx' | grep -v grep
八、如何调高最大 X client 数量
1. 标准 X server 参数
标准 X server 支持:
1
-maxclients 64|128|256|512
常见可选值是:
1
64 / 128 / 256 / 512
如果当前是 256,可以尝试调整为:
1
-maxclients 512
例如:
1
Xorg :10 -maxclients 512 ...
或者:
1
Xvfb :10 -maxclients 512 ...
2. xorg.conf.d 配置方式
如果 ETX 底层使用标准 Xorg,并且读取系统 Xorg 配置,可以创建:
1
2
3
4
5
6
7
sudo mkdir -p /etc/X11/xorg.conf.d
sudo tee /etc/X11/xorg.conf.d/99-maxclients.conf >/dev/null <<'EOF'
Section "ServerFlags"
Option "MaxClients" "512"
EndSection
EOF
然后重启对应 X server / ETX session。
注意:
这通常只对新启动的 X server / 新建的 ETX session 生效。
已经运行中的 session 一般不会动态改变 MaxClients。
3. ETX 场景的注意点
Rocket Exceed TurboX 有自己的 Connection Node、Session、etxproxy 和 ThinX 传输链路。公开文档中可以看到 ETX 使用 etxproxy 与 ETX client 之间的 ThinX 协议,也可以看到 Connection Node 安装配置中有 X server 起始 display number 等参数。
但是,公开文档中并不容易直接找到一个明确叫:
1
2
3
MaxClients
maximum X clients per display
X server max clients
的 ETX 专用配置项。
因此,ETX 环境中不要直接假设 /etc/X11/xorg.conf.d/99-maxclients.conf 一定生效。更稳妥的顺序是:
- 先确认监听
6010的到底是Xorg、Xvfb、Xvnc,还是 ETX 专有进程; - 如果是标准 Xorg/Xvfb,再使用
-maxclients 512或 Xorg 配置; - 如果是 ETX 封装进程,应优先检查 ETX Server Manager / Session Profile / Node Profile / Runtime 参数;
- 如果界面或配置文件中找不到,应向 Rocket Support 确认当前版本是否支持调整 X client limit。
可以搜索 ETX 配置:
1
2
sudo grep -RniE 'maxclients|MaxClients|-maxclients|Xorg|Xvfb|Xvnc' \
/etc /opt /usr/local 2>/dev/null | head -200
查看 ETX 服务:
1
systemctl list-units --type=service | grep -Ei 'etx|exceed|rocket'
九、如果已经是 512,继续调大不是优先方向
如果当前已经达到:
1
-maxclients 512
或者实际连接数已经在 501 附近,那么继续追求更高上限通常不是最优解。
原因是:
- 很多 X server 的标准配置上限就是 512;
- 某些版本内部可能支持更大值,但不一定是 ETX 支持的稳定路径;
- EDA GUI 长时间运行时,连接数持续增长往往意味着 GUI/helper 进程堆积或连接泄漏;
- 单个远程桌面会话承载过多重型 GUI,本身不利于稳定性。
更合理的方向是:
1
2
3
4
5
找出谁占用最多连接
清理残留进程
拆分 ETX session
限制长期堆积
建立监控告警
十、临时恢复方案
1. 关闭不用的 GUI 窗口
优先让用户正常关闭:
1
2
3
4
5
6
7
Virtuoso
Calibre RVE
Verdi
Firefox / Chromium
Java GUI
Qt GUI
xterm
2. 找出占用最多连接的进程
在应用主机侧:
1
2
3
4
5
6
sudo lsof -nP -iTCP@"$DISPLAY_IP":"$DISPLAY_PORT" -sTCP:ESTABLISHED \
| awk 'NR>1{print $1, $2, $3}' \
| sort \
| uniq -c \
| sort -nr \
| head -50
确认后温和 kill:
1
kill <pid>
必要时再:
1
kill -9 <pid>
不要直接执行:
1
pkill -u $USER
这可能误杀用户正在运行的 shell、后台任务、EDA job 或其它关键进程。
3. 重启 ETX session
如果当前 session 已经无法正常操作,最干净的方式是:
- 退出当前 ETX session;
- 在 ETX Server Manager 中结束该用户异常 session;
- 重新创建一个干净 session。
十一、长期治理建议
1. 按工具类型拆分 session
不要把所有重型 GUI 都放在一个 ETX session 中。
推荐:
1
2
3
4
Virtuoso / Schematic / Layout 一个 session
Calibre DRC/LVS/RVE 一个 session
Verdi / Debug 一个 session
浏览器 / 文档 / help viewer 单独控制
2. 限制浏览器常驻
Firefox / Chromium 在远程 X11/ETX 场景里经常占用较多 X connection。
建议不要在 EDA 主 session 中长期挂多个浏览器窗口和标签页。
3. 定期清理空闲 session
建议制定策略:
1
2
3
空闲超过 N 小时提醒
空闲超过 N 天自动 suspend
长期 suspend 的 session 定期回收
4. 建立连接数监控
可以在 ETX node 上定期采样:
1
2
3
PORT=6010
COUNT=$(sudo ss -Htan state established "( sport = :$PORT )" | wc -l)
echo "$(date '+%F %T') port=$PORT established=$COUNT"
建议阈值:
1
2
3
4
>= 70%:观察
>= 80%:预警
>= 90%:通知用户清理
>= 95%:管理员介入
如果最大值是 512:
1
2
3
4
70% 约 358
80% 约 410
90% 约 461
95% 约 486
你看到的 501 已经非常接近上限。
5. 建立 Top offender 排查脚本
可以做一个日常诊断脚本:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env bash
set -euo pipefail
PORT="${1:-6010}"
echo "== X11 TCP established count on port $PORT =="
sudo ss -Htan state established "( sport = :$PORT )" | wc -l
echo
echo "== Top remote IPs =="
sudo ss -Htan state established "( sport = :$PORT )" \
| awk '{print $5}' \
| sed -E 's/:[0-9]+$//' \
| sort \
| uniq -c \
| sort -nr \
| head -30
echo
echo "== Listener =="
sudo ss -ltnp | grep ":$PORT" || true
echo
echo "== Process command line of listener =="
PID=$(sudo ss -ltnp | awk -v p=":$PORT" '$0 ~ p {print $0}' \
| sed -n 's/.*pid=\([0-9]\+\).*/\1/p' | head -1)
if [[ -n "${PID:-}" ]]; then
echo "PID=$PID"
sudo tr '\0' ' ' < "/proc/$PID/cmdline"
echo
cat "/proc/$PID/limits" | egrep 'open files|processes' || true
else
echo "No listener PID found."
fi
使用:
1
bash check_etx_x11_clients.sh 6010
十二、排查结论模板
遇到这类问题,可以用下面模板记录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
问题:Rocky Linux 通过 ETX 启动 Qt/EDA GUI 报 Maximum number of clients reached。
结论:
当前不是 Qt xcb 插件缺失优先,而是 ETX/X11 Display 达到 X client 连接上限。
证据:
1. DISPLAY=etxnode12.icinfra.cn:10.0,对应 TCP 6010。
2. lsof -nP -iTCP:6010 -sTCP:ESTABLISHED 看到约 501 个连接。
3. xlsclients 只有约 240 个,但 xlsclients 只能识别部分窗口型 client,不能代表真实连接数。
4. /tmp/.X11-unix/X1 上的 etxproxy LISTEN 只是本地 proxy 入口,不等于后端 6010 上的所有 client 连接。
5. 当 MaxClients 接近 512 时,新 GUI 程序无法获得 X client slot,触发 Maximum number of clients reached。
处理:
1. 临时清理异常 GUI/helper 进程,或重启用户 ETX session。
2. 查找占用连接最多的来源主机和进程。
3. 如果底层 X server 支持,可将 -maxclients 调整到 512。
4. 如果已是 512,应重点治理 session 堆积和连接泄漏。
十三、参考资料
- Rocket Software Exceed TurboX documentation:Connection Node、Session、
etxproxy、ThinX 相关说明。 - Xserver manual:
-maxclients 64|128|256|512用于设置 X server 允许连接的最大 client 数。 - xorg.conf manual:
ServerFlags中的Option "MaxClients" "512"可用于标准 Xorg 配置。 - Linux
ss、lsof、xlsclients、xrestop工具文档。