工作中笔者接触到国产化达梦数据库,该数据库提供了图形化的管理工具manager。Windows上安装的达梦打开管理工具很简单,直接在开始-->所有程序中能找到manager管理工具;而Linux上安装的达梦,需要在安装位置中找到tool目录(例如全路径/home/dmdba/dmdbms/tool)并执行./manager,幸运的话就能够调出达梦manager图形化界面。
这里之所以说:幸运的话就能够调出达梦manager图形化界面,是因为连接同样的达梦服务器,有些时候在Linux SSH终端中能够打开成功manager管理工具,有些时候就会报错。比如说用putty连达梦数据库服务器,执行./manager就报错;用MobaXterm连服务端执行./manager就能够成功调出来。
实际上在没有理解
DISPLAY SERVER之前是没有发现用不同的SSH客户端连接是造成打开达梦manager工具成功或者失败差异所在。这里提前说明下,用putty和MobaXterm分别打开达梦manager,前者失败后者成功的原因是后者自带X DISPLAY SERVER并开启X Forwarding功能。
现象就是:不同的SSH终端,有时候能打开manager图形化管理工具,有些时候就打开失败(后台报错)。到底是什么问题呢?
把问题抽象一下,实际上Linux上运行的达梦manager就是一个Linux gui程序,那么问题就是SSH终端怎么正确打开Linux gui程序?一番查询之后,发现是知识的盲区DISPLAY SERVER。
Linux gui程序图形化展示和Window System有什么关系呢?
Windows系统中打开图形化应用程序很简单,这是因为Win程序的图形化功能是写在内核中的(微软在DOC系统之后发现了图形化界面的巨大商业价值,后来开发的操作系统内核级别就支持图形化内容)。Linux(或者POSIX)系统要支持图形化界面程序,需要Window System(Unix系统的哲学之一是:一个工具只做一件事并且把这件事做好,因此图形化显示在架构上也是解耦独立的)。
Window System是一种实现了窗口、图标、菜单和像素点范式的gui。我们熟知的Unix操作系统大多使用X Window System,苹果的OSX系统使用Quartz Compositor Window System。
注意这里的
Window System不是Windows System,和微软的windows系统没有关系。
Window System核心是DISPLAY SERVER(或者称为window server、compositor)。一个调用了DISPLAY SERVER来显示图形化的程序称之为该DISPLAY SERVER的客户端(client)。
既有client(调用DISPLAY SERVER服务的gui程序),也有server(DISPLAY SERVER),它们的交互就涉及到协议(protocol),这种协议就称为display server protocol。目前X Window System的X DISPLAY SERVER使用的协议就是X 11(11表示的是版本)。
DISPLAY SERVER的主要功能是协调操作系统、硬件和其他模块与gui程序之间的输入输出,它在图形化硬件上面提供一个抽象供更高级别的图形化接口(例如window manager)使用。
Window System的设计是有层级的,这也体现了Unix系统的设计哲学。Window System图形化调用架构如下:

如图所示,DISPLAY SERVER是Window System的核心所在。当你在Linux上打开一个gui程序的时候,该程序会调用DISPLAY SERVER的图形化显示服务。这个时候DISPLAY SERVER是服务端,而gui程序是客户端。这里和我们平时的理解有差异,一般我们认为服务端都是在远端,而客户端是在本地,这里DISPLAY SERVER服务的调用是反过来(远端调用本地,也就是GUI程序调用DISPLAY SERVER)。
那到底DISPLAY SERVER在本地哪里呢?
可能是你本地Linux操作系统中自带的,也可能是你在Windows系统中手动安装的(Windows系统默认没有display server服务),还可能是ssh客户端工具(例如MobaXterm)内置提供的等等。但是总之,你想在本地启动Linux gui程序,本地一定是要有DISPLAY SERVER服务的。
Linux gui程序是如何找DISPLAY SERVER服务的呢?
在程序启动的环境变量中会查找DISPLAY设置的服务地址。比如:
EXPORT DISPLAY=:0.0表示调用本机DISPLAY SERVER服务,服务的端口是127.0.0.1:6000。
EXPORT DISPLAY=:10.0表示调用本机DISPLAY SERVER服务,服务的端口是127.0.0.1:6010。
EXPORT DISPLAY=xx.26.18.37:3600.0表示调用非本机(但可以在本地)DISPLAY SERVER,服务的端口是xx.26.18.37:9600。
这里说的“本机”指的是环境变量设置的主机,可能是本地主机,也可能是本地虚拟机,或者
ssh远程的远端主机。“本地”指的是本地主机。
这里实际DISPLAY SERVER服务监听的端口号是设置环境变量:后第一个数字+6000,正如上面:3600.0实际服务监听的端口就是6000 + 3600 --> 9600。配置完DISPLAY环境变量之后,可以使用xhost +来验证并禁用掉Access Control限制。
有些Linux服务端查看
echo $DISPLAY设置的是:0.0,但是并不能通过netstat -nalp |grep 60000找打对应的X SERVER监听服务,这样的X SERVER只能够正常打开本地的gui程序,远程其他host并配置当前机器export DISPLAY=x.x.x.x:0.0的DISPLAY属性并不能调通X SERVER服务。如果通过端口查看本地X服务在监听,则远程host配置本地X SERVER能够调用X SERVER服务并打开gui程序。
X11 Forwarding和DISPLAY环境变量设置是两个概念。DISPLAY是告诉你的环境去哪里调用X SERVER服务,而X11 Forwarding能够将本地配置的DISLAY SERVICE转发到Forwarding服务启动的X SERVER上。
使用MobaXterm连接远程服务器时,该ssh客户端会自动启动一个X DISPLAY SERVER服务并开启X11 Forwarding,保证你始终能正确打开Linux GUI程序。而putty没有相应的功能,这也是解释了文章开头处说的为什么这两个ssh客户端工具打开同样Linux GUI程序结果却不相同。
常见的DISPLAY SERVER如下:
Windows系统的图形化是有内核程序支持的,因此Windows默认没有DISPLAY SERVER。我们如果远程Linux机器并且在Win本地打开Linux gui程序,就需要安装DISPLAY SERVER,这里使用的DISPLAY SERVER是X DISPLAY SERVER或者称之为X SERVER。
Windows经常用的X SERVER有XManager、MobaXterm X SERVER、XMing等。MobaXterm启动会默认在本地6000端口(6000端口也是X协议:0的默认端口)启动一个X SERVER并开启X Forwarding功能。当在windows通过ssh工具远程Linux并启动gui程序的时候,内核会调用环境变量中配置的DISPLAY服务绘制图形化界面,DISPLAY的值写为IP:PORTOFFSET:0.0的形式,会访问IP:6000+PORTOFFSET的X服务。我们试验一下:
远程一个安装有完整JDK的虚拟机,打开jvisualvm,如果没有DISPLAY环境变量设置,会报错:

在Windows10系统上安装VcXsrv Windows X Server,,安装后打开并配置display num为3600、Access Control为disable,如图:


在虚拟机上设置export DISPLAY=$HOST:3600.0,重新打开jvisualvm:

这时候就能够成功打开jvisualvm这个linux的gui程序了。
这里的
$HOST是安装VcXsrv的Windows的IP,保证9600端口和虚拟机是通的。这里的DISPLAY NUM这个端口是可以自定义设置的,这里设置为3600的原因是本机的9600端口和虚拟机是通的。
Unix like系统的gui程序图形化展示需要Window System服务的支持,服务的核心是DISPLAY SERVER。当图形化gui程序打开的时候,DISPLAY SERVER作为本地被调用服务端,而gui程序作为本地或远端客户端调用方存在,二者通过display protocol进行通信。gui程序通过程序启动的环境变量DISPLAY获取要调用的DISPLAY SERVER地址,该默认值是:0.0,表示调用本机的DISPLAY SERVER。
需要注意:
DISPLAY SERVER服务监听的端口之间必须是通的(如果有防火墙限制,服务调用会失败)。export DISPALY=x.x.x.x之后,启动gui程序报错缺少*.so类库,则可能是类库版本有差异,在/lib或者/lib64找到名称类似版本不同的类库创建需要类库版本的软连接即可。命令:ln -s /lib64/libxxx.so.6 /lib64/libxxx.so将/lib64/libxxx.so.6创建一个软链接为/lib64/libxxx.so。