在Mac上使用远程X11应用

XWindows vs Windows

XWindows太老了,历史比Windows和Linux的开发时间都长,以至于很多人每天实际在用,但已经不知道它的存在。
XWindows目前是Linux/类Unix系统上的标准显示配置,QT/GTK等架构也是基于XWindows的。所以通常也有很多人只关注占领桌面市场的Windows,对于败退在边缘的XWindows完全嗤之以鼻。
其实只从GUI层面上来对比Windows和XWindows是不公平的。XWindows设计之初就是一个显示服务器的概念,在显示器服务器和应用之间,有一套协议来沟通彼此,是C/S的架构,这个协议可以序列化,从而显示的设备、跟应用运行的环境,可以不在同一台电脑之上。想想你熟悉的那些无线投影啥的,是啥时候才出现的吧。

其实我个人也很久不用XWindows了。平常工作在Mac,但是最近机器学习的任务越来越多,Mac用起来就有点不顺手了。因为MacPro标准配置的opencl,远远比不上cuda在机器学习领域的支持广泛。恐怕如果Mac电脑不尽快做出改变,这一波风暴足以把很多依赖于Mac环境的开发人员驱逐到Linux甚至Windows的怀抱中去了。
Windows的环境天然对NV系列显卡和CUDA的驱动支持很充分,所以也有很多程序员使用Windows环境做开发。但很多开源系统在Windows环境的编译甚至移植实在太艰苦了,一个应用中很大的精力都在折腾这些事情,完全不能集注到应用本身。
所以我用的方法是另外找一台电脑安装NV显卡,然后运行Linux,虽然CUDA和CUDNN安装麻烦了一点点,但后续的工作就都很顺畅了。
接下来就需要XWindows闪亮出厂了。当然可以另外接一套显示和键盘,但那样要占空间的…虽然显示器、键盘、鼠标不贵,但空间贵啊。再有就是远程桌面,不过那种分割的感觉,用起来会增加许多额外的麻烦。所以很多人忘记很多年的远程XWindows,可以出来嘚瑟一下了 :)

macOS虽然也是类Unix,但从很早开始就不使用XWindows作为显示系统了,所以现在想在Mac上使用XWindows,需要先安装另外一个Apple发起的开源项目:XQuartz。除了去官网下载安装包,在有Homebrew的系统上安装更简单:brew cask install xquartz,安装后是个app应用,可以在LaunchPad启动。所有XWindows的应用,都应当先启动xquartz应用,然后在终端中(系统自带的终端及Xwindows中的终端都能有效转发,其它终端不一定有效请自行确认)再启动XWindows应用。
接着是将远程的linux服务器上的运行结果,在本地的XQuartz中显示。正常情况下,如果本机Mac及远程的Linux在一个局网,或者双方能直接ping通那就简单了,只需要设置一个环境参数DISPLAY。如果linux用的是bash外壳,其设置方法为:export DISPLAY=mac电脑IP地址:0.0,冒号后面数字的意思是:第0个设备的第0个屏幕。
如果两台电脑不在一个网段,就需要ssh大神的配合,首先查看/etc/ssh/sshd_config中的设置,是否打开了以下两项:

X11Forwarding yes
X11DisplayOffset 10

这两项通常在安装sshd的时候都是默认屏蔽的。打开之后,还要设置DISPLAY环境变量为:export DISPLAY=localhost:10.0,其中localhost表示直接将显示数据发送到本地,位置10跟上面sshd的设置配套,表示由本地的sshd转发到远端的客户端去显示。
最后还有一项,在mac使用ssh连接远端的服务器的时候,首先要确保ssh命令中需要增加-X或者-Y参数,表示接受远端的XWindows转发数据。示例:ssh -Y john@123.123.123.123
连通之后,可以在远端运行一下xeyes、xclock、xlogo这样的基本应用来测试一下,看能否在本地桌面上显示出来。题头图的右上角两个应用分别是xlogo和xclock的样子。
最后给一个在我的电脑跑起来的样子:
看起来跟在本地运行没有什么两样 :)

补充:
有些主机,或者有的时候,ssh连接过去后,执行x11应用会报错:Error: Can't open display: localhost:10.0,这时候仔细观察ssh命令执行后的第一条提示,有可能会是X11 forwarding request failed on channel 0。这表示实际本地和远端没有能建立起来X11协议的转发体系,原因可能有很多,比如连接端口不是10.0,或者认证没通过等等。
可以做以下的尝试:
方法一:
检查ping localhost是否能ping通,有可能是/etc/hosts中,没有把localhost指向127.0.0.1本机地址。这种情况可以设置显示到127.0.0.1:10.0就可以。

方法二:
sudo vi /etc/ssh/sshd_config增加一行:X11UseLocalhost yes,接着sudo service sshd restart。然后ssh重新连过来再试试。

方法三:

  1. 去掉自己设置$DISPLAY环境参数的脚本,比如我通常设置在.bashrc中最后一条,把这个设置删除,使用系统的自动设置功能。
  2. sudo vi /etc/ssh/sshd_config,把刚才增加的X11UseLocalhost yes改成:X11UseLocalhost no,接着sudo service sshd restart
  3. sudo apt purge xauth然后sudo apt install xauth重新安装xauth授权。
  4. 断开ssh连接,使用ssh -AX username@ip地址重新连过来,-A的意思是使用X11认证授权方式,这样连接之后,linux主机会生成一个~/.Xauthority保存授权允许连接的远程终端信息。
  5. echo $DISPLAY可以显示当前xauth自动生成的显示端口,比如我这里是:ubuntu:10.0,ubuntu是我的linux主机名,其实在我这里ubuntu跟localhost是一样的。
  6. 再次尝试执行x11应用,比如xclock,应当能成功了。
  7. 以后连接远程主机的时候,使用ssh -X ...或者ssh -Y ...而不用增加-A选项了,我们使用-A只是为了生成~/.Xauthority授权文件。