Linux主机Headless与Headed无缝热插拔切换
Linux主机物理屏与虚拟屏切换指南
书接上文,虽然我们已经实现了Linux主机的虚拟屏设置,可以很好的使用该“无头”服务器,但是如果我们希望给他外接物理显示屏则必须删除虚拟驱动设置,然后再重启。主包觉得这样太过麻烦了,是不是可以让系统自动检测当前是否有显示屏接入,如果接入显示屏那我们采用真实显卡设置,否则采用DUMMY虚拟驱动?
如何启动虚拟屏
参见“无头”Linux主机远程桌面使用指南中“使用X11桌面协议”与“配置X.org服务器”
启动虚拟屏进阶
对于上篇博文中提到的开机服务,可以设置为用户层面的服务,此时无需指定DISPLAY,这意味着鲁棒性更强vim ~/.config/systemd/user/x11vnc.service
[Unit] |
sudo systemctl --user daemon-reload |
此时
- 使用物理屏时,同时可以使用显示屏或被VNC转发
- 使用虚拟屏时,只被VNC转发
如何切换物理屏与虚拟屏
我们通过X.org
服务器建构屏幕,物理屏与虚拟屏也只是在驱动方面有所不同。物理屏采用真实的显卡作为驱动例如/usr/share/X11/xorg.conf.d/*-amdgpu.conf
;而在上一篇博客中我们采用DUMMY虚拟驱动构建虚拟屏幕即/usr/share/X11/xorg.conf.d/*-dummy.conf
。
X.org服务器读入配置规则如下
- 首先读取
/usr/share/X11/xorg.conf.d/
下所有*-*.conf
,并按照字典序依次构建整体的配置(这意味对于同名Section,后加载的配置会覆盖先加载的配置) - 然后读取
/etc/X11/xorg.conf.d/
下所有的*-*.conf
,规则如上
为了规范配置的存放方式,我们不再修改/usr/share/X11
文件夹,而是修改/etc/X11
文件夹
首先我们将物理屏设置与虚拟屏设置单独存在在同一个空闲文件夹下例如xorg.conf.d.available
sudo mkdir -p /etc/X11/xorg.conf.d.available
sudo cp /usr/share/X11/xorg.conf.d/*-amdgpu.conf /etc/X11/xorg.conf.d.available/amdgpu.conf
sudo mv /usr/share/X11/xorg.conf.d/*-dummy.conf /etc/X11/xorg.conf.d.available/dummy.conf
接下来我们只要依据是否外接显示屏某个配置链接置xorg.conf.d
下AVAILABLE_DIR="/etc/X11/xorg.conf.d.available"
ACTIVE_CONF_LINK="/etc/X11/xorg.conf.d/20-active-gpu.conf"
desired_config=$([[ "$monitor_connected" == "true" ]] && echo "amdgpu.conf" || echo "dummy.conf")
current_config_target="none"
if [ -L "$ACTIVE_CONF_LINK" ]; then
current_config_target=$(readlink "$ACTIVE_CONF_LINK" | xargs basename)
fi
if [ "$current_config_target" != "$desired_config" ]; then
log "配置需要改变!从 '$current_config_target' 切换到 '$desired_config'。"
ln -sf "$AVAILABLE_DIR/$desired_config" "$ACTIVE_CONF_LINK"
log "符号链接已更新。"
exit 0
else
exit 1
fi
如何检测是否外接显示屏
DRM(略有迟钝,通用性强)
Linux的/sys
目录下存放着大量和硬件相关的文件,而主机提供的各种接口同样以文件的形式存放于此。我们找到Linux系统的DRM子系统,该子系统负责图形硬件的控制,一般的桌面Linux系统与显示屏都通过该子系统进行交互。我们最后在/sys/class/drm
目录下找到DRM子系统的各个接口ls /sys/class/drm
card1 card1-DP-2 card1-DP-4 card1-DP-6 card1-DP-8 card1-Writeback-1 version
card1-DP-1 card1-DP-3 card1-DP-5 card1-DP-7 card1-HDMI-A-1 renderD128
card*-*
目录下的status
文件显示该接口是否处于连接状态,我们只需要探查是否有接口处于connected
状态即可
monitor_connected=false |
DDC/CI(反应迅速,需要特定硬件支持)
如果你的显示屏可以使用DDC/CI
协议通信,那么使用ddcutil
就可以直接探查是否有显示屏sudo apt update
sudo apt upgrade
sudo apt install ddcutilddcutil detect
无外接显示屏
No displays found.
Run "ddcutil environment" to check for system configuration problems.
存在外接显示屏
Invalid display
I2C bus: /dev/i2c-4
DRM connector: card1-HDMI-A-1
EDID synopsis:
Mfg id: KOS - UNK
Model: KOIOS K2418U
Product code: 9240 (0x2418)
Serial number: 0000000000000
Binary serial number: 0 (0x00000000)
Manufacture year: 2018, Week: 45
DDC communication failed
- 虽然不知道为什么DDC交互中断了,但是如果外接显示屏的话其实还是可以获取一些显示屏的信息的,我们可以通过该特点区分是否外接显示屏
if ddcutil detect 2>&1 | grep -q "No displays found."; then |
非热插拔
select-conf
接下来只要将上面两步结合在一起,就可以实现按需切换虚拟屏与物理屏了,给一个基于DRM的例子,使用DDC/CI只需要更换判断是否外接显示屏部分逻辑sudo vim /usr/local/bin/select-xorg-conf.sh
!/bin/bash |
systemd
同时,我们只要在display-manager.service
启动进行一次探查即可sudo vim /etc/systemd/system/xorg-select.service
[Unit] |
sudo systemctl daemon-reload |
注意
- 注意使用
sudo chmod +x
给执行脚本权限,否则开机服务将执行失败 - 由于DRM子系统探查硬件的时机不确定,故使用DRM方案需要先判断DRM子系统硬件是否探查完毕,以
card*
目录出现为标志 - DDC/CI方案不依赖于DRM子系统,不需要等待
card*
目录出现
热插拔
非热插拔方案只在每次开机时进行探查,这意味着在主机运行期间进行插拔HDMI线等是不会产生任何影响的(当然拔了显示屏,物理屏方案自然就不能用了,这里是说Xorg服务器运行的配置)
如何优雅地重启X.org服务器
当然你可以采用kill手段直接清楚已有的X.org服务器,但是由于图形化页面繁杂,建议直接将图形化页面重启,即重启display-manager.service
,所以我们写一个脚本或者服务,在检测到配置不符时重启display-manager.service
sudo vim /etc/systemd/system/xorg-conf-check.service
[Unit]
Description=Check X.Org configuration after a potential monitor change
[Service]
Type=oneshot
我们直接运行检查脚本
ExecStart=/usr/local/bin/select-xorg-conf.sh
0 -> 配置应当改变 -> ExecStartPost
1 -> 配置无需改变 -> ExecStartPost
ExecStartPost=/bin/bash -c 'echo "Configuration changed, restarting GDM in 3s..." | systemd-cat -p info && sleep 3 && systemctl restart display-manager.service
sudo systemctl daemon-reload |
如何实时检测
使用udev实时检测即可,DRM探测发现有热插拔现象时会将环境变量的”HOTPLUG”置1,所以我们可以为udev编写一条新的rule,在HOTPLUG
发生变化时执行我们的脚本sudo vim /etc/udev/rules.d/num-name.conf
num
代表加载顺序name
代表具有一定语义的名称- 例如
95-monitor.conf
当DRM子系统发生变化时,重启我们的延时检查定时器 |
问题
硬件更新有延迟
热插拔后,硬件有一小段时间的延迟,随后/sys/class/drm/card*/status
才会发生改变,所以我们可以设置一个计时器,udev触发后一段时间在执行xorg-conf-check
服务
新建同名timersudo vim /etc/systemd/system/xorg-conf-check.timer
[Unit] |
sudo systemctl daemon-reload |
udev改为触发计时器,而不是触发服务当DRM子系统发生变化时,重启我们的延时检查定时器
ACTION=="change", SUBSYSTEM=="drm", KERNEL=="card*", ENV{HOTPLUG}=="1", RUN+="/bin/systemctl restart /etc/systemd/system/xorg-conf-check.timer"
热插拔一两次后失效
该问题主要针对使用DRM子系统进行判断的方案,开机后进行一两次热插拔后,虽然会触发”HOTPLUG”,但是/sys/class/drm/card*/status
不再改变,导致所有基于DRM方案完全失效,目前暂无好的解决方案,推荐更换DDC/CI或者设置为非热插拔。
不过对于热插拔情况较少时仍可以考虑DRM,否则还是推荐DDC/CI
图形化页面崩溃
注销当前用户即可,目前尚不清楚是由什么引起的