故障现象
初次使用RDP连接时一切正常。但当电脑休眠时,远程桌面连接就会中断。在此之后,重新连接RDP,会遇到几秒钟的黑屏,连接上后剪贴板和声音还有远程挂载的硬盘都无法正常工作。
故障起因
经过深入调查,似乎在远程桌面连接中断时,“xrdp-chansrv”进程进入“僵死”模式。
# ps -ef | grep defunc
ubuntu 42 38 0 12:42 pts/0 00:00:00 [xrdp-chansrv] <defunct>
翻看文档:
xrdp-chansrv is the xrdp channel server, which manages the Remote Desktop Protocol
(RDP) sub-channels. This program is only forked internally by xrdp-sesman.
Currently xrdp-chansrv knows about the following channels:
cliprdr
Clipboard Redirection
rdpsnd Remote Desktop Protocol Sound
rdpdr Remote Desktop Protocol Device Redirection
rail Remote Applications Integrated Locally
drdynvc
Dynamic Virtual Channel
看来声音和剪贴板都不起作用似乎是由于“xrdp-chansrv”崩溃后变成了僵尸进程所致。
我尝试了多种方法来恢复“xrdp-chansrv”,包括kill掉这个进程,但没有任何作用。这个进程似乎是由 xrdp 内部启动的子进程,无法被控制。杀死它也不能解决问题。
之后,我在neutrinolabs/xrdp#1259和此仓库的其他issue中找到了可能的错误原因:
chansrv在异常断开后会尝试连接到之前的Socket,所以当下一次连接之后就会crash。
解决方案
方法1 安装最新版的xrdp
官方已经在v0.9.13版本中修复了这个bug。因此,更新到最新版本的xrdp即可解决问题。
但是,如果你在使用Ubuntu24.04以前的系统,并且是通过apt安装的xrdp,那么你需要手动编译安装最新版本的xrdp。(低于24.04版本的Ubuntu的apt官方源中xrdp最新版为v0.9.12.
方法2 手动重启xrdp-chansrv进程
经过深入的研究,当defunct进程的父进程停止运行时,它会变成’init’进程的子进程,PID为1。‘init’进程负责清理所有成为其父进程的defunct子进程。
只要父进程仍在运行,defunct进程就会保留在那里,以便让父进程读取其子进程的退出状态。父进程应该察觉到子进程是defunct状态并将其移除,然后重新启动它。 令人无语的是,这个操作无法手动完成,且xrdp的子进程守护存在缺陷,也无法自动重启这个子进程。
不过,当xrdp-chansrv变成defunct状态时,我启动了一个新的xrdp-chansrv进程,然后重新启动RDP连接时,声音突然就正常了。
所以,新的xrdp-chansrv进程以某种方式被xrdp在新客户端连接时捕捉到。
利用xrdp允许在会话重新连接时执行一些代码的特性,我们可以往/etc/xrdp/reconnectwm.sh
中注入DISPLAY=:10.0 /sbin/xrdp-chansrv &
,即可解决问题:
echo 'DISPLAY=:10.0 /sbin/xrdp-chansrv &' >> /etc/xrdp/reconnectwm.sh
现在,每次重新连接时,都会在后台启动一个新的 xrdp-chansrv,问题解决。
但是,有同学可能会问,如果我们重新连接几次,我们是否会得到多个僵尸进程?
答案是:不会! 首先,当 /etc/xrdp/reconnectwm.sh 脚本执行完后,其父级变为 PID 1,即“bash /usr/bin/entrypoint,而不是像 linux/unix 那样处于“init”进程中。
新进程成为了 ‘sh’ 的子进程,并且 ‘sh’ 总是在其子进程失效时清理它们。所以,当新连接的远程桌面连接关闭时,新启动的“xrpd-chansrv”进程仍然会很好地关闭。
# 第一次连接RDP session后,xrdp启动了xrdp-chansrv进程.
$ ps aux |grep chan
ubuntu 50 0.0 0.0 0 0 pts/0 Z 10:13 0:00 [xrdp-chansrv]
# 在首次关闭RDP后,由xrdp启动的chansrv变为失效的僵尸进程,并且没有被清理。
$ ps aux |grep chan
ubuntu 50 0.0 0.0 0 0 pts/0 Z 10:13 0:00 [xrdp-chansrv] <defunct>
# 在/etc/xrdp/reconnectwm.sh中重新打开RDP后,启动了一个新的/sbin/xrdp-chansrv进程。
$ ps aux |grep chan
ubuntu 50 0.0 0.0 0 0 pts/0 Z 10:13 0:00 [xrdp-chansrv] <defunct>
ubuntu 1219 0.7 0.0 86672 3308 pts/0 Sl 10:38 0:00 /sbin/xrdp-chansrv
# 再次关闭RDP后,可以看到新启动的xrdp-chansrv已经关闭。
$ ps aux |grep chan
ubuntu 50 0.0 0.0 0 0 pts/0 Z 10:13 0:00 [xrdp-chansrv] <defunct>
通过此修复,重新连接的速度也得到了大幅提升,剪贴板和文件传输、磁盘挂载也正常可用了。
参考文档:https://blog.phusion.nl/2015/01/20/docker-and-the-pid-1-zombie-reaping-problem/
...