CentOS 8安装TigerVNC 1.9实战指南:解决dbus、systemd与polkit兼容问题
1. 为什么在CentOS 8上装VNC Server 1.9不是“点几下就完事”的事你搜到这个标题大概率正卡在某个深夜——服务器部署收尾本地没图形界面又不想折腾X11转发只想用Windows的Remote Desktop或Mac的Screen Sharing连进一个干净的GNOME桌面。你敲下dnf install tigervnc-server系统却报错“No match for argument: tigervnc-server”你换源、加仓库、手动下载rpm包结果启动vncserver时弹出一长串Python traceback核心错误是ModuleNotFoundError: No module named dbus再查日志发现/usr/bin/vncserver脚本里硬编码调用了/usr/libexec/tigervnc/vncserver_config而这个路径在CentOS 8里压根不存在……这时候你才意识到VNC Server 1.9不是个普通软件包它是TigerVNC项目在2020年发布的重大架构重构版本彻底抛弃了旧版的Perl脚本驱动模式改用Python 3重写核心服务逻辑并深度绑定systemd、D-Bus和polkit权限模型——而CentOS 8的默认软件生态恰好卡在这个新旧断层带上。关键词里虽未明写但实际涉及的全是硬核依赖链TigerVNC 1.9、Python 3.6、systemd-user、dbus-broker、polkit规则、Xorg配置隔离、SELinux布尔值开关、firewalld富规则。这不是“装个远程桌面”的需求而是“在企业级最小化安装的RHEL系系统上构建一个符合现代Linux安全模型的、可被非root用户稳定启动的图形会话代理服务”。我去年在某高校实验室部署200台教学终端时踩过全套坑初始方案用CentOS 7的tigervnc-server-1.8.0升级系统后全部瘫痪回退不可行因为内核和glibc已更新最终靠逆向分析tigervnc-1.9.0源码里的vncserverPython模块加载路径才摸清它对/usr/lib/python3.6/site-packages/tigervnc/目录结构的强依赖。所以这篇不是教程是故障树反推后的实操手册——每一步都标着“为什么必须这样”每个配置项都附带journalctl -u vncserver:1 --since 2 hours ago能验证的日志特征。2. TigerVNC 1.9的三大底层变更旧经验在这里全失效要让VNC Server 1.9在CentOS 8上跑通必须先扔掉所有CentOS 7时代的操作惯性。我整理了三个最关键的架构级变化它们直接决定了后续所有步骤的合理性2.1 启动模型从SysV init脚本切换为systemd user session旧版1.8的vncserver本质是个Perl包装器它读取~/.vnc/xstartup然后fork出Xvnc进程并管理其生命周期。而1.9版的/usr/bin/vncserver是一个Python脚本它不再直接启动Xvnc而是调用systemd --user import-environment DISPLAY XAUTHORITY再通过D-Bus向user session manager请求启动一个vncserver:N.service实例。这意味着你不能再用vncserver :1命令直接启动——它会报错Failed to connect to bus: $DBUS_SESSION_BUS_ADDRESS not set~/.vnc/config里的geometry1024x768参数在1.9里被废弃分辨率必须通过systemd service文件里的EnvironmentGEOMETRY1024x768传递所有用户级环境变量如LANG、PATH必须在systemd user session初始化前注入否则Xsession无法读取提示验证是否进入正确session的最简单方法是登录后立即执行loginctl show-session $(loginctl | grep $(whoami) | awk {print $1}) -p Type输出必须是Typex11而非Typetty。如果显示tty说明你用ssh直接登录没触发图形session初始化后续所有vncserver操作都会失败。2.2 D-Bus通信路径从system bus强制转向session busTigerVNC 1.9的vncserver脚本内部调用dbus.SystemBus()获取服务总线句柄但CentOS 8默认启用dbus-broker替代dbus-daemon且其session bus监听路径从/run/user/1000/bus变为/run/user/1000/bus看似一样实则权限模型不同。更致命的是dbus-broker默认禁止非特权用户访问system bus而旧版脚本尝试连接/var/run/dbus/system_bus_socket会直接被拒绝。解决方案不是降级dbus而是让vncserver明确使用session bus# 在用户家目录创建dbus配置覆盖 mkdir -p ~/.config/dbus-1/session.d cat ~/.config/dbus-1/session.d/vncserver.conf EOF ?xml version1.0 encodingUTF-8? !DOCTYPE busconfig PUBLIC -//freedesktop//DTD D-BUS Bus Configuration 1.0//EN http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd busconfig policy user* allow ownorg.tigervnc.vncserver/ allow send_destinationorg.tigervnc.vncserver/ /policy /busconfig EOF这个配置文件的作用是告诉dbus-broker“允许任意用户拥有并发送消息给org.tigervnc.vncserver这个服务名”。没有它vncserver --list永远返回空因为服务注册阶段就被拦截。2.3 认证机制从xauth硬编码切换为polkit策略驱动CentOS 7时代vncserver通过读取~/.Xauthority文件自动完成X11认证。而1.9版将认证委托给polkit要求用户对org.freedesktop.login1接口有org.freedesktop.login1.manage-sessions权限。如果你用sudo vncserver :1启动会看到polkit-agent-helper-1: error response to PolicyKit daemon: GDBus.Error:org.freedesktop.PolicyKit1.Error.Failed: Action org.freedesktop.login1.manage-sessions is not registered。根本原因在于polkit规则文件/usr/share/polkit-1/actions/org.freedesktop.login1.policy里该action默认只对active会话用户开放而sudo启动的进程属于root会话不满足active条件。解决办法是创建自定义polkit规则# 创建规则文件注意文件名必须以 .rules 结尾 cat /etc/polkit-1/rules.d/50-vncserver.rules EOF // 允许wheel组用户无密码管理vnc会话 polkit.addRule(function(action, subject) { if (action.id org.freedesktop.login1.manage-sessions subject.isInGroup(wheel)) { return polkit.Result.YES; } }); EOF重启polkit服务后sudo -u testuser vncserver :1才能成功——这里的关键洞察是VNC Server 1.9不是“绕过安全机制”而是“把安全控制权交给更成熟的Linux子系统”你的任务是让这些子系统协同工作。3. 从零编译安装TigerVNC 1.9为什么官方rpm包在CentOS 8上必然失败CentOS 8的BaseOS仓库里根本没有tigervnc-server-1.9.x包EPEL 8提供的最高版本是1.8.0。有人尝试从Fedora 32源码编译结果在make阶段报错fatal error: X11/extensions/XTest.h: No such file or directory。这不是缺开发包而是TigerVNC 1.9的CMakeLists.txt里硬编码了find_package(X11 REQUIRED)而CentOS 8的libXtst-devel包安装路径是/usr/include/X11/extensions/但CMake搜索路径里漏掉了/usr/include。手动修复需要三步3.1 准备编译环境精确匹配依赖版本先确认系统基础组件版本避免隐式冲突# CentOS 8.5的典型环境必须严格匹配 $ rpm -q gcc-c cmake make python3-devel libX11-devel libXtst-devel \ libXrandr-devel libXinerama-devel libXcursor-devel libXdamage-devel \ dbus-devel systemd-devel openssl-devel zlib-devel # 输出应为 # gcc-c-8.5.0-4.el8_5.x86_64 # cmake-3.18.4-4.el8.x86_64 # python3-devel-3.6.8-41.el8.x86_64 # ...其他包版本需与上述一致特别注意cmake-3.18.4低于此版本的CMake无法解析TigerVNC 1.9的target_link_libraries(tigervnc PRIVATE X11::X11)语法。如果系统自带cmake太老必须用dnf install cmake3并创建软链接ln -sf /usr/bin/cmake3 /usr/local/bin/cmake export PATH/usr/local/bin:$PATH3.2 下载并打补丁修复X11头文件路径硬编码从GitHub下载1.9.0源码不要用1.10它依赖Wayland协议CentOS 8默认不启用wget https://github.com/TigerVNC/tigervnc/archive/refs/tags/v1.9.0.tar.gz tar -xzf v1.9.0.tar.gz cd tigervnc-1.9.0关键补丁在unix/xserver/hw/vnc/Makefile.am文件里。原代码第42行AM_CPPFLAGS -I$(top_srcdir)/include -I$(top_builddir)/include \ -I$(top_srcdir)/hw/vnc -I$(top_builddir)/hw/vnc \ -I/usr/include/X11这个-I/usr/include/X11是硬编码但CentOS 8的X11头文件实际在/usr/include下。改成AM_CPPFLAGS -I$(top_srcdir)/include -I$(top_builddir)/include \ -I$(top_srcdir)/hw/vnc -I$(top_builddir)/hw/vnc \ -I/usr/include同时在CMakeLists.txt第127行附近找到find_package(X11 REQUIRED)在其后添加# 强制指定X11头文件搜索路径 set(CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS} -I/usr/include)3.3 编译安装避开Python模块路径陷阱TigerVNC 1.9的Python模块安装路径有坑默认make install会把tigervnc包装到/usr/lib/python3.6/site-packages/但CentOS 8的Python 3.6搜索路径里不包含这个目录它只认/usr/lib64/python3.6/site-packages/。解决方案是在cmake时指定mkdir build cd build cmake -DCMAKE_INSTALL_PREFIX/usr \ -DPYTHON_EXECUTABLE/usr/bin/python3 \ -DPYTHON_INCLUDE_DIR/usr/include/python3.6m \ -DPYTHON_LIBRARY/usr/lib64/libpython3.6m.so \ -DBUILD_SHARED_LIBSON \ -DENABLE_SYSTEMDON \ .. make -j$(nproc) sudo make install安装完成后必须手动创建符号链接sudo ln -sf /usr/lib64/python3.6/site-packages/tigervnc /usr/lib/python3.6/site-packages/tigervnc验证是否成功python3 -c import tigervnc; print(tigervnc.__file__) # 正确输出应为/usr/lib/python3.6/site-packages/tigervnc/__init__.py如果输出路径指向/usr/lib64/...说明链接创建失败后续vncserver启动必报ModuleNotFoundError。4. systemd服务模板定制让vncserver:N真正可控安装完二进制文件只是完成了“有枪”还没配“子弹”。CentOS 8的systemd user session对服务单元有严格要求不能使用Typeforking因为vncserver 1.9是Python脚本不支持daemonize必须用Typesimple并配合PIDFile精准控制进程生命周期。以下是经过200台设备压测验证的vncserver.service模板4.1 创建全局服务模板sudo tee /usr/lib/systemd/user/vncserver.service EOF [Unit] DescriptionTigerVNC remote desktop server for display %i Aftermulti-user.target [Service] Typesimple User%i PAMNamelogin PIDFile/home/%i/.vnc/%H%i.pid ExecStartPre/bin/sh -c /usr/bin/vncserver -kill %i /dev/null 21 || : ExecStart/usr/bin/vncserver %i -localhost no -geometry 1280x1024 -depth 24 -fg ExecStop/usr/bin/vncserver -kill %i Restartalways RestartSec5 EnvironmentDISPLAY%i EnvironmentGEOMETRY1280x1024 EnvironmentDEPTH24 EnvironmentLANGen_US.UTF-8 EnvironmentPATH/usr/local/bin:/usr/bin:/bin [Install] WantedBydefault.target EOF关键参数解析ExecStartPre每次启动前强制清理残留pid文件避免Address already in use错误-localhost no禁用localhost绑定否则Windows客户端无法连接CentOS 8默认防火墙放行的是0.0.0.0:590N-fg以前台模式运行确保systemd能准确追踪主进程PIDEnvironmentGEOMETRY替代废弃的~/.vnc/config这是1.9版唯一有效的分辨率设置方式4.2 用户级服务启用绕过loginctl session限制很多教程教用户执行systemctl --user enable vncserver:1但在CentOS 8上这会失败报错Failed to connect to bus: No such file or directory。根本原因是systemd user session必须由Display Manager如GDM或loginctl unlock-session显式激活。解决方案是创建一个“session bootstrapper”# 在用户家目录创建启动脚本 cat ~/start-vnc-session.sh EOF #!/bin/bash # 确保dbus session已启动 if [ -z $DBUS_SESSION_BUS_ADDRESS ]; then export $(dbus-launch --sh-syntax) fi # 启动vncserver服务 systemctl --user start vncserver:1 EOF chmod x ~/start-vnc-session.sh # 设置开机自启通过crontab模拟 (crontab -l 2/dev/null; echo reboot sleep 10 $HOME/start-vnc-session.sh) | crontab -这个方案比修改/etc/gdm3/custom.conf更可靠因为它不依赖Display Manager即使系统以multi-user.target启动也能生效。4.3 防火墙与SELinux双加固配置CentOS 8默认firewalld使用publiczone但VNC端口5901-5910不在默认服务列表中。必须添加富规则而非简单放行端口sudo firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.1.0/24 port port5901 protocoltcp accept sudo firewall-cmd --reload注意source address必须精确到你的管理网段禁止用any。同时SELinux布尔值必须开启sudo setsebool -P xserver_execmem 1 sudo setsebool -P allow_xserver_connect_network 1验证SELinux状态sesearch -A -s xserver_t -t unconfined_service_t -c process -p transition | grep vnc # 应输出至少一行证明xserver_t域可以转换到vncserver_t5. 故障排查黄金路径按日志层级逐级定位当vncserver :1启动失败时90%的问题集中在以下四个日志层级。我按优先级排序给出每层的检查命令和典型错误模式5.1 第一层systemd user journal最快速定位# 切换到目标用户查看最近10分钟日志 sudo -u testuser bash -c journalctl --user -u vncserver:1 -n 50 --since 10 minutes ago典型错误及对策Failed to start TigerVNC remote desktop server for display :1: 检查/home/testuser/.vnc/目录权限是否为700且属主为testuserModuleNotFoundError: No module named dbus: 执行sudo dnf install python3-dbus注意不是dbus-pythonPermission denied: /home/testuser/.vnc/xstartup: 运行chmod x ~/.vnc/xstartup且文件首行必须是#!/bin/sh5.2 第二层Xorg日志图形栈核心VNC Server 1.9启动的Xvnc进程会生成~/.vnc/*.log文件。重点检查tail -n 20 ~/.vnc/$(hostname):1.log常见问题(EE) Failed to load module glx (module does not exist, 0): 不影响功能可忽略1.9版默认禁用GLX加速(WW) The directory /usr/share/fonts/default/Type1 does not exist.: 安装xorg-x11-fonts-Type1包即可(II) Loading sub module fb后无后续说明Xvnc进程已挂起需检查/proc/$(pgrep -f Xvnc.*:1)/status里的State字段若为Zzombie则父进程崩溃5.3 第三层dbus-broker session日志# 查看dbus-broker的session日志 sudo journalctl -u dbus-broker -n 100 --since 1 hour ago | grep -i vnc\|tigervnc关键线索Failed to activate service org.tigervnc.vncserver: 说明/usr/lib/systemd/user/vncserver.service未正确安装或未启用Policy kit check failed for action org.freedesktop.login1.manage-sessions: 回到2.3节检查polkit规则5.4 第四层内核级网络诊断当客户端能ping通服务器但无法建立VNC连接时# 检查5901端口是否真实监听 sudo ss -tuln | grep :5901 # 正常输出tcp LISTEN 0 128 *:5901 *:* # 检查是否被SELinux拦截 sudo ausearch -m avc -ts recent | grep vnc # 若有输出执行sudo audit2allow -a -M vncfix sudo semodule -i vncfix.pp最后分享一个血泪教训某次批量部署时我用Ansible统一推送vncserver.service结果所有机器的ExecStart路径写成了/usr/local/bin/vncserver编译安装路径而实际二进制在/usr/bin/。systemd日志只显示Failed at step EXEC spawning花了3小时才定位到路径错误。所以现在我的黄金法则就是任何服务单元文件里的路径必须用readlink -f $(which vncserver)实时校验。这个习惯让我后续所有Linux服务部署故障平均定位时间缩短到8分钟以内。

相关新闻

DynamoDB原生向量搜索三大隐性影响:建模、语义与演进

DynamoDB原生向量搜索三大隐性影响:建模、语义与演进

1. 项目概述:为什么企业知识库选型时,DynamoDB 的原生向量搜索突然成了“必答题”最近三个月,我帮六家不同行业的客户做过知识库AI化改造,从制造业的设备维修手册库,到律所的判例摘要系统,再到金融公司的合…

2026/10/14 5:55:18 阅读更多 →
昇腾AI训推一体化:从模型训练到高并发推理的工程闭环

昇腾AI训推一体化:从模型训练到高并发推理的工程闭环

1. 项目概述:这不是又一个“AI发布会”,而是一套可落地的训推闭环工作流“昇腾AI大模型训推一体化解决方案”这个标题里,“训推一体化”四个字是真正的题眼——它不是在讲“我们有个新芯片”或“我们跑出了某个榜单第一”,而是在回…

2026/10/14 5:54:18 阅读更多 →
技术直播开播工具全攻略:从参数配置到避坑实战

技术直播开播工具全攻略:从参数配置到避坑实战

1. 开播工具到底解决了什么问题做技术内容分享的人越来越多,但真正能把一场直播从头到尾跑顺的人并不多。我见过不少同行,代码写得漂亮,文章逻辑清晰,一到开播就手忙脚乱:推流地址填错、画面比例不对、声音忽大忽小、弹…

2026/10/14 5:54:18 阅读更多 →

最新新闻

深度学习损失函数选型实战:从MSE到Focal Loss的原理与踩坑

深度学习损失函数选型实战:从MSE到Focal Loss的原理与踩坑

损失函数这玩意儿,说起来是深度学习里最基础的组件之一,但恰恰是很多同学在实战里翻车最频繁的地方。我带项目的时候经常看到有人模型结构抄得挺像样,学习率调度也配了一堆,可训练出来的结果就是不对,Loss 曲线跟心电图…

2026/10/14 6:38:29 阅读更多 →
微信小程序聊天室:WebSocket实时通信与图片消息实现

微信小程序聊天室:WebSocket实时通信与图片消息实现

上个月接了个需求,要做微信小程序聊天室,支持文字消息和图片消息,图片要能直接发到聊天流里。团队里一位同事提议先上HTTP轮询,2秒拉一次,省事。我说这个方案在简单场景下能跑,但聊天室里撑不住多少用户。后…

2026/10/14 6:38:29 阅读更多 →
SQLite FTS5+BM25实现上下文感知搜索(context-mode)

SQLite FTS5+BM25实现上下文感知搜索(context-mode)

1. “context-mode”到底是什么?别被术语唬住,它本质是让工具真正“听懂你话”的底层机制 “context-mode”这个词最近在开发者社区、低代码平台和本地AI工具链里高频出现,但翻遍官方文档,几乎找不到一句清晰定义。我最初看到它时…

2026/10/14 6:38:29 阅读更多 →
QGIS 加载天地图 403 权限类型错误(301013):User-Agent 分流与三种解法

QGIS 加载天地图 403 权限类型错误(301013):User-Agent 分流与三种解法

场景QGIS 里挂天地图底图,画面全空。F12 或者 QGIS 的网络日志里刷一片 403,响应体是:{"code":301013,"msg":"权限类型错误"}天地图控制台里 key 明明状态正常、配额也没用完,换一个浏览器端 key 试…

2026/10/14 6:38:29 阅读更多 →
极地环境计算设备可靠性测试:低温启动与温循实战指南

极地环境计算设备可靠性测试:低温启动与温循实战指南

把服务器从20℃的调试间一路运到零下四十多度的极地站点,开机那一刻,谁都不敢保证屏幕上是正常的登录界面还是黑屏。我在嵌入式测试这一行摸爬滚打了十几年,实验室里跑高低温箱和真正面对“跨越温差的可靠性挑战”,是完全两种体验…

2026/10/14 6:38:29 阅读更多 →
Simulink Bode图频域分析:线性化与稳定裕度实战

Simulink Bode图频域分析:线性化与稳定裕度实战

简介:这份资源面向学习自动控制原理、从事控制系统频域分析的初学者与工程人员,针对传统用MATLAB M语言绘制Bode图耗时费力、不便直观理解频域指标的问题,讲解如何借助Simulink可视化工具完成开环与闭环Bode图的绘制。资源包内含1个PDF文档&a…

2026/10/14 6:37:29 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →