故障现象

初次使用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/