1、概述
X Window System是1984年由麻省理工学院(MIT)和DEC公司共同开发研究的,是运行在UNIX系统上的视窗系统。严格地说,X Window System并不是一个软件,而是一个协议,这个协议定义一个系统成品所必需具备的功能(就如同TCP/IP、DECnet或IBM的SNA,这些也都是协议,定义软件所应具备的功能)。能满足此协议及符合X协会其他规范的系统便可称为X。X Window System独有的网络通透性(Network Transparency),使其成为UNIX平台上的工业标准,现在UNIX的工作站或大型主机几乎都执行着X Window。X Window是非常巧妙的设计,很多时候它在概念上比其它窗口系统先进,以至于经过很多年它仍然是工作站上的工业标准。许多其它窗口系统的概念都是从X Window学来的。
X Window System本身是一个非常复杂的图形化作业环境,我们可以将它分成3个部分,分别是X Server、X Client和X Protocol。X Server主要是处理输入输出的信息,X Client执行大部分应用程序的运算功能,X Protocol则是建立X Server和X Client的沟通管道。
(1)X Server
X Server主要负责处理输入输出的信息,并且维护字体、颜色等相关资源。它接收输入设备(如键盘、鼠标)的信息,将这些信息交给X Client处理,而X Client所传来的信息就由X Server负责输出到输出设备(如显示卡、荧幕)上。X Server传给X Client的信息称作Events(事件)。X Client传给X Server的信息称作Request(要求)。Events(事件)主要包括键盘的输入和鼠标的移动、按下等动作,而Request(要求)主要是X Client要求对显示卡及屏幕的输出作调整。
(2)X Client
X Client主要负责应用程序的运算处理部分,它将X Server所传来的Events作运算处理后,再将结果以Request的方式去要求X Server显示在屏幕上的图形视窗。在X Window System的结构中,X Server和X Client所负责的部分是分开的,所以X Client和硬件无关,只和程序运算有关。这样有一个好处,例如更换显示卡时,X Client部分不需要重新编写;因为X Server和X Client是分开的,所以可以将两者分别安装在不同电脑上,这样就可以利用本地端的屏幕、键盘和鼠标来操作远端的X Client程序。常见的X Client有大家熟悉的gdm, xterm, xeyes等。
(3)X Protocol
X Protocol就是X server与X client之间通信的协议。X protocol支持现在常用的网络通信协议。例如测试TCP/IP,可以看到X server侦听在tcp 6000端口上。那X protocol就是位于传输层以上了,应该属于应用层。通常,X server和X client在两台机器上时,则之间一般使用TCP/IP协议通信,若在同一台机器,则使用高效的操作系统内部通信协议。
(4)X Library、X Toolkit和Widget
X Client主要就应用程序,而开发程序大多会提供函数库,以方便开发人员开发,在X则提供有X Library(X Lib),X Library主要提供X Protocol的存取能力,由于X Server只是根据X Client所给的Request(要求)去显示画面,因此所有的图形使用界面都交由X Client负责。我们没有必要每写一个应用程序都得从头再开发一个界面,所以有了图形界面库X Toolkit和Widget的产生,开发者就可以使用Toolkit和Widget来创建按钮、对话框、轴、窗口等视窗结构,这样开发者可以容易地开发各种程序。
总结下运行过程:
(1)用户通过鼠标键盘对X server下达操作命令
(2)X server利用Event传递用户操作信息给X client
(3)X client进行程序运算
(4)X client利用Request传回所要显示的结果
(5)X server将结果显示在屏幕上
可以看出,X Window的工作方式跟Microsoft Windows有着本质的不同。Microsoft Windows的图形用户界面(GUI)是跟系统紧密相联的。而X Window则不是,它实际上是在系统核心(kernel)的上面运行的一个应用程序。
X Window的运行分为4层。最底层的是X Server(服务器),提供图形界面的驱动,为X Window提供服务。上面的一层是用于网络通信的网络协议,即X网络协议,这部分使远程运行X Window成为可能。只需要在服务器上运行一个X Server,而客户机(Client)上运行更上一层的程序,则可以实现X Window的远程运行。再往上的一层是称作Xlib的函数接口,介于基础系统和较高层应用程序之间。应用程序的实现是通过调用这一层的函数实现的。最顶层就是窗口管理器了,也就是一般所说的WM(Window Manager),这一层的软件是用户经常接触的,比如fvwm、AfterStep、Enlightment以及WindowMaker等。
从上面的介绍来看,X Window的运行是一种客户机/服务器(Client/Server)的模式,服务器用于显示客户机运行的应用程序,又被称为显示服务器 (Display Server)。显示服务器位于硬件和客户机之间,它跟踪所有来自输入设备(比如键盘、鼠标)的输入动作,经过处理后将其送回客户机。这样,用户可以在 Microsoft Windows的机器上运行X Client,截取并传送用户的输入,只是将X Window的屏幕输出显示在用户的屏幕上。客户机的输入和输出系统跟X服务器之间的通信都是遵守X协议的。
搞清楚X server与X client关系很重要。一般X server很简单,就是/usr/bin/X11/X程序或Xorg程序,在Microsoft Windows上常用的X server有Xming,Xmanager,Exceed和X-win32等。而X client则花样繁多,从高级的CDE,GNOME,KDE,到低级一点的只有twm,Window Maker,blackbox等窗口管理器,再到最简陋的只有xterm,rxvt,xeyes等单个x程序。正是由于X client的各种搭配,使得我们的X Window System看起来多样化。
注意,X中所提及的“客户端”和“服务器”等字眼用词与人们一般想定的相反,“服务器”反而是在用户本地端的自有机器上运行,而非是在远程的另一部机器上运行。很多熟悉Internet原理的人首次遇到X Window的这两个概念都会搞错。如果他从一台Windows机器上使用Exceed通过XDMCP登录到一台Sun服务器,他就说Exceed是客户端(client),而Sun机器是服务器(server)。这就完全搞错了。X server不是指你登录的那台机器,而是指一个程序,它负责在某台机器上接受客户的要求,在屏幕上显示客户请求的图形,并且把消息(键盘,鼠标,窗口消息)通知客户程序。这个例子里本地的Exceed就是一个X server,它负责控制这台Windows机器上的显示(display),远程Sun机器上的程序xterm, xxgdb, dtwm(CDE的窗口管理器)等,是客户程序。它们通常会使用TCP 6000号端口连接Windows机器,而Windows机器的6000号端口是由Exceed来bind和listen的。比如,当你通过telnet启动Sun机器上的xterm,就会 Exceed的屏幕上显示一个窗口。实际发生的事情是: xterm请求连接Windows机器的6000号端口,跟Exceed连接,然后xterm请求得到资源,然后xterm请求在屏幕上显示一个窗口。你在xterm的窗口里按下"A"键时,Exceed会把这个事件通知xterm进程,然后xterm会发送数据报,请求Exceed, “请在坐标(100,30)处显示一个字母A,然后在后面显示一个矩形作为光标。”这样你的xterm窗口里就会多显示一个字母。
实际的远端客户端的例子有:图形化管理远程计算机;在远端UNIX计算机上运行计算密集的仿真程序并把结果显示到本地的Windows桌面计算机;用一套显示器、键盘和鼠标控制同时运行在多台计算机上的图形化软件。
2、X Window System的启动过程
从控制台(即字符界面)进入X一般是用startx命令。在前面“Linux init程序分析“中介绍过startx(以Fedora为例),这里以Ubuntu为例。startx是用xinit程序来启动X的。首先man startx和man xinit可以看到staratx和xinit的使用方法:
startx [ [client] options .....] [-- [server] [display] options ....]
xinit [ [client] options ] [-- [server] [display] options ...]
把上面[client]和[server]分别称为X client程序和X server程序。man手册里写明其必须以/或者./开头,即必须是绝对路径或从当前路径开始。
下面看看/usr/bin/startx这个脚本。
以上即为X Window System的启动过程,startx只是负责一些参数传递,真正的X启动由xinit实现。我们可以知道,startx将会先解析用户的参数,如果该用户指定了该参数(即解析结果不为空),那么startx就会以该参数来启动xinit,否则就会解析(与其说是解析,还不如说是执行)$HOME目录下的rc文件,如果该文件不存在,就会解析系统目录下(/etc/X11/xinit/)的rc文件,如果这个文件也不存在,那startx就将以默认的client(xterm)和server(/usr/bin/X)为参数来启动xinit。例如,可以在用户目录下构造.xinitrc(即X client)和.xserverrc(即X server)文件。在.xserverrc里写入/usr/bin/X11/X :1。.xinitrc里写入/usr/bin/X11/xeyes -display localhost:1。这就是最简单的X server+ X client了,只不过把屏幕编号从默认的0改为了1。
到目前为止,我们还不知道仅仅在终端输入startx是怎么样启动gnome桌面的,gnome当然属于X client了。通过对startx的分析可知,startx主要有三种启动方式:
(1)一种是自己指定要启动的client和server, 例如:startx /usr/bin/xclock -- /usr/bin/X :0;
(2)一种是通过在$HOME下新建.xinitrc文件来指定要启动的多个client和.xserverrc来指定要启动的server(注意:这两个文件本来是不存在的);
(3)还有一种是直接输入startx而不指定参数,这也就是我们启动gnome桌面的方法。
在第(3)中启动方法中,我们可以知道,startx脚本会先去看系统目录(/etc/X11/xinit/)下的rc文件是否存在,如果不存在就会用默认的xterm和/usr/bin/X来启动xinit。显然,startx启动的不是xterm,而是gnome桌面,因此gnome的启动是通过系统文件/etc/X11/xinit/xinitrc来指定的。在“Linux init程序分析”中详细介绍过的gnome的启动。这里以Ubuntu为例,/etc/X11/xinit/xinitrc文件中只包含了. /etc/X11/Xsession一句话,因此gnome是通过Xsession脚本启动的(在Fedora中则是直接用xinitrc来启动gnome)。下面是Xsession文件:
从以上的对Xsession脚本文件的分析,可以看出,Xsession脚本仅仅是执行了/etc/X11/Xsession.d目录下的所有文件,每个文件名都以数字开头,这些数字就表示了文件被运行的优先级,数字小的优先级高,run-parts会将数字小的排在前面,这样就能确保以上文件能按数字由小到大的顺序执行。主要的文件如下:
(1)20x11-common_process-args:这个文件主要是处理传给/etc/X11/xinit/ xinitrc脚本文件的参数的。该参数个数只能为0或一个,否则将不进行任何处理。如果该参数是failsafe,则该脚本将执行x-terminal-emulator,否则就执行该参数。需要说明的是,x-terminal-emulator是一个符号链接,指向/etc/alternatives/x-terminal-emulator,同时,/etc/alternatives/x-terminal-emulator也是一个符号链接,它指向/usr/bin/gnome-terminal.wrapper,而gnome-terminal.wrapper则是一个perl脚本,它最终是调用了gnome-terminal。
(2)30x11-common_xresources:该文件主要是调用xrdb,根据/etc/X11/Xresources目录下及$HOME/.Xresources目录下的文件的内容来设置根窗口的屏幕0上的RESOURCE_MANAGER属性的内容。
(3)40x11-common_xsessionrc:该文件主要是判断$HOME/.xsessionrc文件是否存在,如果存在则执行该脚本文件。
(4)50x11-common_determine-startup:该文件主要先查看配置文件/etc/X11/Xsession.options中是否允许使用用户的xsession,如果/etc/X11/Xsession.options中存在allow-user-xsession字段,则查看用户指定的$HOME/.xsession是否存在并有执行权限,如果是,则将STARTUP变量设置为该文件,如果没有执行权限就将STARTUP变量设置为“sh 该xsession文件”。如果此时STARTUP变量仍然为空(即没有使用用户指定的.xsession脚本),则将其设置为/usr/bin下的x-session-manager,x-window-manager或x-terminal-emulator,它们都是指向/etc/alternatives/下相应程序的符号链接。注意这个STARTUP将会在后面的脚本中被启动。
(5)55gnome-session_gnomerc:该文件会先得到STARTUP的basename,如STARTUP=/usr/bin/x-session-manager,则其basename为x-session-manager。再判断该basename是否为gnome-session,或者为x-session-manager并且x-session-manager是个符号链接,它指向/usr/bin/gnome-session,如果选择的是gnome-session,则执行$HOME/.gnomerc(如果该文件存在并且可读)。
(6)60x11-common_localhost:使用xhost程序,把本机的用户名添加到允许连接X server的用户名列表中。用户名由`id -un`命令给出。
(7)60xdg_path-on-session:根据选择的窗口会话,通过设置XDG_CONFIG_DIRS变量来添加额外的xdg路径。
(8)60xdg-user-dirs-update:用xdg-user-dirs-update自动生成$HOME下的文件夹,该命令主要是根据/etc/xdg/user-dirs.defaults文件的内容来为用户创建文件夹的。
(9)70gconfd_path-on-session:根据选择窗口会话,添加额外的gconf路径。
(10)75dbus_dbus-launch:把use-session-dbus选择放入Xsession.options文件中,表示使用在窗口会话中使用dbus。把启动/usr/bin/dbus-launch程序的选择添加到STARTUP变量中。
(11)80im-switch:该文件主要用于设置输入法。具体的请自己参考文件内容。
(12)90consolekit:如果环境变量$XDG_SESSION_COOKIE为空,并且/usr/bin/ck-launch-session可执行,则将STARTUP重新赋值为” /usr/bin/ck-launch-session $STARTUP”。至于ck-launch-session的功能,我也不是很清楚,估计是和session有关,对窗口会话进行一些检查。
(13)90x11-common_ssh-agent:该文件主要先查看配置文件/etc/X11/Xsession.options中是否使用ssh agent,如果/etc/X11/Xsession.options中存在use-ssh-agent字段,则判断/usr/bin/ssh-agent是否可执行,并且环境变量$SSH_AUTH_SOCK和$SSH2_AUTH_SOCK是否都为空,如果是,这将STARTUP重新赋值为” /usr/bin/ssh-agent $STARTUP”。
(14)99x11-common_start:它仅仅是用exec启动$STARTUP。关于exec,在Bourne shell中,它与fork的区别就在于它执行一个新的脚本不需创建sub-shell,而它与Source和Dot的区别就在与在这条语句后面的语句将不会再被执行。此时,我们可以发现变量$STARTUP的值为:“startup=/usr/bin/ssh-agent /usr/bin/ck-launch-session /usr/bin/seahorse-agent --execute x-session-manager”, 因此,最终将会被执行的就是这么一条语句。而x-session-manager最终指向的是gnome-session。
gnome-session就是最终启动GNOME桌面环境的,这个程序一般被登入管理器gdm、xdm和脚本startx调用。总结一下Ubuntu中Gnome的启动过程,核心的流程总结如下:
- /usr/bin/startx
- --->xinit /etc/X11/xinit/xinitrc -- /etc/X11/xinit/xinitrc
- --->./etc/X11/Xsession
- --->/etc/X11/Xsession.d/
- --->50x11-common_determine-startup
- --->/etc/X11/Xsession.options
- --->STARTUP=$HOME/.xsession
- --->STARTUP=/usr/bin/x-session-manager
- --->/usr/bin/gnome-session
- --->55gnome-session_gnomerc
- --->$HOME/.gnomerc
- --->75dbus_dbus-launch
- --->80im-switch
- --->99x11-common_start
- --->exec $STARTUP
3、跨网络运行X Window System
通常用来做服务器的系统(Linux,FreeBSD,Solaris等等)都用字符界面,不会装X server,甚至很多都没有显示器。这样可以在这些系统里安装简单的X client,以GUI的方式远程显示在管理员们所坐的X server里。例如可以用FreeBSD做网关,提供WWW,FTP服务,一般在管理员的本地机器起个X server,然后通过ssh或telnet登录远程到FreeBSD系统,运行X client程序显示在本地显示器上,当然,也可用XDMCP(X Display Manager Control Protocol)的方式来登录运行。man xsession手册里提到/etc/X11/Xsession一般被startx(Ubuntu中在/etc/X11/xinit/xinitrc里调用 Xsession脚本)或display manager调用,但有的display manager只调用Xsession而不是xinitrc,故为了startx和display manager两种方式下都可正常启动GUI,最好把X client启动的程序放在Xsession文件里。远程运行X client程序需要设置DISPLAY环境变量,设置为 Xserver主机名称:屏幕编号(如192.168.1.115:0,则表示X server是192.168.1.115这台机器上的0号屏幕);或是给X client程序加个—display参数。这里我们在TCP/IP网络环境中测试X Window System。
在VMWare虚拟机中安装Fedora 14,地址为192.168.1.115,使用/usr/bin/X作为X server。安装Ubuntu 10.04,地址为192.168.1.116。现在我们要使它们默认启动到字符界面。Fedora中比较简单,修改/etc/inittab文件,把其中的默认运行由5改成3,重启即可。Ubuntu中使用grub v2,需要把/etc/default/grub文件中的GRUB_CMDLINE_LINUX_DEFAULT变量修改成"quiet splash text",即加上一个text参数,然后运行sudo update-grub,重新生成grub启动菜单的配置文件/boot/grub/grub.cfg。重启即可。
(1)配置X Server
第一步:在$HOME下生成.Xauthority认证文件中
[jackzhou@localhost ~]$ xauth add 192.168.1.115:0 . `mcookie`
xauth: creating new authority file /root/.Xauthority
第二步:查看认证文件
[jackzhou@localhost ~]$ xauth list
192.168.1.115:0 MIT-MAGIC-COOKIE-1 728ef8138827dcc82b7aa562d946796a
第三步:通过上面的认证文件生成一个密钥文件jackcookie
[jackzhou@localhost ~]$ xauth extract jackcookie 192.168.1.115:0
[jackzhou@localhost ~]$ ls -l jackcookie
-rw-------. 1 jackzhou jackzhou 49 Jul 17 13:20 jackcookie
第四步:将生成的密钥文件jackcookie,拷贝到X Client端。注意这需要Ubuntu上安装有open ssh server,可以用sudo apt-get install openssh-server安装。
[jackzhou@localhost ~]$ scp jackcookie 192.168.1.116:$HOME
The authenticity of host '192.168.1.116 (192.168.1.116)' can't be established.
RSA key fingerprint is fe:a2:ef:ab:75:37:04:47:c6:4d:02:9d:58:1c:f7:35.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.1.116' (RSA) to the list of known hosts.
[email protected]'s password:
jackcookie 100% 49 0.1KB/s 00:00
第五步:在后台启动X Server
[jackzhou@localhost ~]$ X -auth $HOME/.Xauthority &
这样X server会在tty7上运行(tty7上是运行图形界面的),通过Ctrl+Alt+F7可以切换到X界面,上面一片黑暗,只显示一个光标。可以通过Ctrl+Alt+F1切换回原来的字符界面,由于在不同的tty上运行,它们互不影响。
(2)配置X Client
第一步:将X Server传送过来的密码文件jackcookie,进行导入操作
jackzhou@jackzhou-desktop:~$ ls -l jackcookie
-rw------- 1 jackzhou jackzhou 2011-07-17 13:17 jackcookie
jackzhou@jackzhou-desktop:~$ xauth merge jackcookie
jackzhou@jackzhou-desktop:~$ ls -l jackcookie
-rw------- 1 jackzhou jackzhou 49 2011-07-17 13:21 jackcookie
第二步:配置DISPLAY环境变量
jackzhou@jackzhou-desktop:~$ export DISPLAY=192.168.1.115:0
第三步:启动各种X Client,如xterm和xeye、、xclock、twm、gedit、gnome-terminal,甚至gnome-session程序,都行。注意twm在Ubuntu中默认没有安装,可以用apt-get安装。
jackzhou@jackzhou-desktop:~$ xterm &
jackzhou@jackzhou-desktop:~$ xeyes &
jackzhou@jackzhou-desktop:~$ twm &
这样在Fedora的X Server上就会显示Ubuntu中相应的X client界面了。通常的应用场景是X client机器在远程且没有显示器,X Server机器在本地,你可以通过在Fedora中使用ssh 192.168.1.116登录到远程的X client机器,来配置X Client,而不用直接跑到远程的机器上去配。
要更加深入地研究X Window System,可参考man xserver和man x的手册页,Wikipedia的介绍http://zh.wikipedia.org/zh-cn/X_Window,以及Xorg Foundation的官方站点http://www.x.org/wiki/。下载X11R7.6的源代码,里面有各模块设计的详细文档说明。