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 6000port上。那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來創建button、對話框、軸、窗體等視窗結構,這樣開發人員能夠easy地開發各種程序。
總結下執行過程:
(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(server),提供圖形界面的驅動,為X Window提供服務。上面的一層是用於網絡通信的網絡協議,即X網絡協議,這部分使遠程執行X Window成為可能。僅僅須要在server上執行一個X Server,而客戶機(Client)上執行更上一層的程序,則能夠實現X Window的遠程執行。再往上的一層是稱作Xlib的函數接口,介於基礎系統和較高層應用程序之間。應用程序的實現是通過調用這一層的函數實現的。最頂層就是窗體管理器了,也就是一般所說的WM(Window Manager),這一層的軟件是用戶常常接觸的,比方fvwm、AfterStep、Enlightment以及WindowMaker等。
從上面的介紹來看,X Window的執行是一種客戶機/server(Client/Server)的模式,server用於顯示客戶機執行的應用程序,又被稱為顯示server (Display Server)。顯示server位於硬件和客戶機之間,它跟蹤全部來自輸入設備(比方鍵盤、鼠標)的輸入動作,經過處理后將其送回客戶機。這樣,用戶能夠在 Microsoft Windows的機器上執行X Client,截取並傳送用戶的輸入,僅僅是將X Window的屏幕輸出顯示在用戶的屏幕上。客戶機的輸入和輸出系統跟Xserver之間的通信都是遵守X協議的。
搞清楚X server與X client關系非常重要。一般X server非常easy,就是/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中所提及的“client”和“server”等字眼用詞與人們一般想定的相反,“server”反而是在用戶本地端的自有機器上執行,而非是在遠程的還有一部機器上執行。非常多熟悉Internet原理的人首次遇到X Window的這兩個概念都會搞錯。假設他從一台Windows機器上使用Exceed通過XDMCP登錄到一台Sunserver,他就說Exceed是client(client),而Sun機器是server(server)。這就全然搞錯了。X server不是指你登錄的那台機器,而是指一個程序,它負責在某台機器上接受客戶的要求,在屏幕上顯示客戶請求的圖形,而且把消息(鍵盤,鼠標,窗體消息)通知客戶程序。這個樣例里本地的Exceed就是一個X server,它負責控制這台Windows機器上的顯示(display),遠程Sun機器上的程序xterm, xxgdb, dtwm(CDE的窗體管理器)等,是客戶程序。它們一般會使用TCP 6000號port連接Windows機器,而Windows機器的6000號port是由Exceed來bind和listen的。比方,當你通過telnet啟動Sun機器上的xterm,就會 Exceed的屏幕上顯示一個窗體。實際發生的事情是: xterm請求連接Windows機器的6000號port,跟Exceed連接,然后xterm請求得到資源,然后xterm請求在屏幕上顯示一個窗體。你在xterm的窗體里按下"A"鍵時,Exceed會把這個事件通知xterm進程,然后xterm會發送數據報,請求Exceed, “請在坐標(100,30)處顯示一個字母A,然后在后面顯示一個矩形作為光標。”這樣你的xterm窗體里就會多顯示一個字母。
實際的遠端client的樣例有:圖形化管理遠程計算機;在遠端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這個腳本。
#!/bin/bash # # This is just a sample implementation of a slightly less primitive # interface than xinit. It looks for user .xinitrc and .xserverrc # files, then system xinitrc and xserverrc files, else lets xinit choose # its default. The system xinitrc should probably do things like check # for .Xresources files and merge them in, startup up a window manager, # and pop a clock and serveral xterms. # # Site administrators are STRONGLY urged to write nicer versions. # unset DBUS_SESSION_BUS_ADDRESS unset SESSION_MANAGER userclientrc=$HOME/.xinitrc # 用戶的client定義文件 sysclientrc=/etc/X11/xinit/xinitrc # 系統的client定義文件 userserverrc=$HOME/.xserverrc # 用戶的server定義文件 sysserverrc=/etc/X11/xinit/xserverrc # 系統的server定義文件 defaultclient=xterm # 默認的client程序 defaultserver=/usr/bin/X # 默認的server程序 defaultclientargs="" # 以下定義了client和server的參數變量 defaultserverargs="" defaultdisplay=":0" clientargs="" serverargs="" enable_xauth=1 # 參數解析:將whoseargs變量賦值為字符串“client”,表示當前解析的指定client的參數 whoseargs="client" while [ x"$1" != x ]; do # 若第一個參數為空,則直接退出循環 case "$1" in # 須要單引號''以避免cpp把"/*"當作凝視 /''*|\./''*) # 假設$1是/*或者./*形式 (xinit程序要求其指定的client程序和server程序必須以/或./開頭, # 否則會被視為client程序和server程序本身的options參數,見man xinit) if [ "$whoseargs" = "client" ]; then # 解析client程序的參數 if [ x"$client" = x ] && [ x"$clientargs" = x ]; then client="$1" # 解析出用戶指定的client程序 else clientargs="$clientargs $1" #解析出client程序的options參數 fi else if [ x"$server" = x ] && [ x"$serverargs" = x ]; then server="$1" # 解析出用戶指定的X server程序 else serverargs="$serverargs $1" # 解析出X server的options參數 fi fi ;; --) # 遇到“- -”就解析server whoseargs="server" ;; *) if [ "$whoseargs" = "client" ]; then clientargs="$clientargs $1" else # 解析[display]參數,屏幕編號[display]必須為第一個給server程序的參數,以:x的形式(x為數字) if [ x"$serverargs" = x ] && \ expr "$1" : ':[0-9][0-9]*> /dev/null 2>&1; then display="$1" else serverargs="$serverargs $1" # 解析[display]以外的參數 fi fi ;; esac shift # 全部參數左移一次 done # 處理client參數 if [ x"$client" = x ]; then # 假設用戶沒有指定X client,則使用默認的X client即xterm client=$defaultclient # 假設沒有client的選項參數,則用rc文件取代 if [ x"$clientargs" = x ]; then if [ -f "$userclientrc" ]; then client=$userclientrc elif [ -f "$sysclientrc" ]; then client=$sysclientrc fi clientargs=$defaultclientargs fi fi # 處理server參數 if [ x"$server" = x ]; then # 假設用戶沒有指定X server,則使用默認的X server即X server=$defaultserver # 假設沒有server的選項參數或display參數,使用默認值 if [ x"$serverargs" = x -a x"$display" = x ]; then # 為了兼容性的考慮,假設沒有server的命令行參數,僅僅使用xserverrc文件 if [ -f "$userserverrc" ]; then server=$userserverrc elif [ -f "$sysserverrc" ]; then server=$sysserverrc fi serverargs=$defaultserverargs display=$defaultdisplay fi fi if [ x"$enable_xauth" = x1 ] ; then if [ x"$XAUTHORITY" = x ]; then # 假設環境變量XAUTHORITY為空,就設定為$HOME/.Xauthority XAUTHORITY=$HOME/.Xauthority export XAUTHORITY fi removelist= # 設置本機的默認Xauth信息 # 檢查GNU主機名,假設hostname –version中不包括GNU 就將hostname變量設定為命令hostname –f返回的字符串 if hostname --version > /dev/null 2>&1; then if [ -z "`hostname --version 2>&1 | grep GNU`" ]; then hostname=`hostname -f` fi fi if [ -z "$hostname" ]; then hostname=`hostname` fi authdisplay=${display:-:0} mcookie=`/usr/bin/mcookie` if test x"$mcookie" = x; then echo "Couldn't create cookie" exit 1 fi dummy=0 # 為server創建帶有auth信息的文件,假設為屏幕編號為':0' xserverauthfile=`mktemp --tmpdir serverauth.XXXXXXXXXX` trap "rm -f '$xserverauthfile'" HUP INT QUIT ILL TRAP KILL BUS TERM xauth -q -f "$xserverauthfile" << EOF add :$dummy . $mcookie EOF serverargs=${serverargs}" -auth "${xserverauthfile} # now add the same credentials to the client authority file # if '$displayname' already exists do not overwrite it as another # server man need it. Add them to the '$xserverauthfile' instead. for displayname in $authdisplay $hostname$authdisplay; do authcookie=`xauth list "$displayname" \ | sed -n "s/.*$displayname[[:space:]*].*[[:space:]*]//p"` 2>/dev/null; if [ "z${authcookie}" = "z" ] ; then xauth -q << EOF add $displayname . $mcookie EOF removelist="$displayname $removelist" else dummy=$(($dummy+1)); xauth -q -f "$xserverauthfile" << EOF add :$dummy . $authcookie EOF fi done fi # #以下的語句通過xinit啟動X server和Clients,把處理的參數傳給它 xinit "$client" $clientargs -- "$server" $display $serverargs retval=$? if [ x"$enable_xauth" = x1 ] ; then if [ x"$removelist" != x ]; then xauth remove $removelist fi if [ x"$xserverauthfile" != x ]; then rm -f "$xserverauthfile" fi fi exit $retval
以上即為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文件:
#!/bin/sh # 注意該腳本用的是Bourne shell解析的 # # /etc/X11/Xsession # # global Xsession file -- used by display managers and xinit (startx) # $Id: Xsession 967 2005-12-27 07:20:55Z dnusinow $ set -e # 打開errexit選項,該選項表示假設以下有命令返回的狀態非0,則退出程序 PROGNAME=Xsession # 以下4個是信息輸出函數,能夠無論 message () { # pretty-print messages of arbitrary length; use xmessage if it # is available and $DISPLAY is set MESSAGE="$PROGNAME: $*" echo "$MESSAGE" | fold -s -w ${COLUMNS:-80} >&2 if [ -n "$DISPLAY" ] && which xmessage > /dev/null 2>&1; then echo "$MESSAGE" | fold -s -w ${COLUMNS:-80} | xmessage -center -file - fi } message_nonl () { # pretty-print messages of arbitrary length (no trailing newline); use # xmessage if it is available and $DISPLAY is set MESSAGE="$PROGNAME: $*" echo -n "$MESSAGE" | fold -s -w ${COLUMNS:-80} >&2; if [ -n "$DISPLAY" ] && which xmessage > /dev/null 2>&1; then echo -n "$MESSAGE" | fold -s -w ${COLUMNS:-80} | xmessage -center -file - fi } errormsg () { # exit script with error message "$*" exit 1 } internal_errormsg () { # exit script with error; essentially a "THIS SHOULD NEVER HAPPEN" message # One big call to message() for the sake of xmessage; if we had two then # the user would have dismissed the error we want reported before seeing the # request to report it. errormsg "$*" \ "Please report the installed version of the \"x11-common\"" \ "package and the complete text of this error message to" \ "<debian-x@lists.debian.org>." } # 初始化被全部session腳本使用的變量 OPTIONFILE=/etc/X11/Xsession.options SYSRESOURCES=/etc/X11/Xresources USRRESOURCES=$HOME/.Xresources SYSSESSIONDIR=/etc/X11/Xsession.d USERXSESSION=$HOME/.xsession USERXSESSIONRC=$HOME/.xsessionrc ALTUSERXSESSION=$HOME/.Xsession ERRFILE=$HOME/.xsession-errors # 嘗試創建一個error文件,假設不能創建則退出 if (umask 077 && touch "$ERRFILE") 2> /dev/null && [ -w "$ERRFILE" ] && [ ! -L "$ERRFILE" ]; then chmod 600 "$ERRFILE" elif ERRFILE=$(tempfile 2> /dev/null); then if ! ln -sf "$ERRFILE" "${TMPDIR:=/tmp}/xsession-$USER"; then message "warning: unable to symlink \"$TMPDIR/xsession-$USER\" to" \ "\"$ERRFILE\"; look for session log/errors in" \ "\"$TMPDIR/xsession-$USER\"." fi else errormsg "unable to create X session log/error file; aborting." fi # 截短ERRFILE,假設它太大了,以避免硬盤空間的DoS攻擊 if [ "`stat -c%s \"$ERRFILE\"`" -gt 500000 ]; then T=`mktemp -p "$HOME"` tail -c 500000 "$ERRFILE" > "$T" && mv -f "$T" "$ERRFILE" || rm -f "$T" fi exec >>"$ERRFILE" 2>&1 echo "$PROGNAME: X session started for $LOGNAME at $(date)" # 理智的檢查:#假設/etc/X11/Xsession.d不存在或不是一個文件夾則打印錯誤信息並退出 if [ ! -d "$SYSSESSIONDIR" ]; then errormsg "no \"$SYSSESSIONDIR\" directory found; aborting." fi # Attempt to create a file of non-zero length in /tmp; a full filesystem can # cause mysterious X session failures. We do not use touch, :, or test -w # because they won't actually create a file with contents. We also let standard # error from tempfile and echo go to the error file to aid the user in # determining what went wrong. WRITE_TEST=$(tempfile) if ! echo "*" >>"$WRITE_TEST"; then message "warning: unable to write to ${WRITE_TEST%/*}; X session may exit" \ "with an error" fi rm -f "$WRITE_TEST" # use run-parts to source every file in the session directory; we source # instead of executing so that the variables and functions defined above # are available to the scripts, and so that they can pass variables to each # other SESSIONFILES=$(run-parts --list $SYSSESSIONDIR) # 將/etc/X11/Xsession.d文件夾中的全部文件都讀出,並存入SESSIONFILES變量中 if [ -n "$SESSIONFILES" ]; then set +e # 關閉errexit選項 for SESSIONFILE in $SESSIONFILES; do # 運行Xsession.d下的每個腳本 . $SESSIONFILE done set -e fi exit 0 # vim:set ai et sts=2 sw=2 tw=80:從以上的對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程序,把本機的username加入到同意連接X server的username列表中。username由`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 # 假設有.xsession腳本,則設置成執行它 --->STARTUP=/usr/bin/x-session-manager # 假設沒有.xsession腳本,則設置成執行x-session-manager --->/usr/bin/gnome-session # x-session-manager指向gnome-session程序 --->55gnome-session_gnomerc --->$HOME/.gnomerc # 選擇了gnome-session,則執行$HOME/.gnomerc --->75dbus_dbus-launch # 啟動dbus --->80im-switch # 設置輸入法 --->99x11-common_start --->exec $STARTUP # 啟動gnome會話,進入gnome桌面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.
jackzhou@192.168.1.116'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傳送過來的password文件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的源碼,里面有各模塊設計的具體文檔說明。