探究X Window System運行原理與啟動過程
第一次在Debian下裝XFree86,startx啟動了twm,裝了gnome 後startx啟動了gnome環境,為什嗎?裝gnome 時修改了什麼檔案以及X環境是怎麼起來的?本來是想搞清這幾個文題開始研究這個題目的,沒想到還學到了很多別的東西^_^本文主要說明X Window System的基本運行原理,其啟動過程,及常見的跨網路運行X Window System。
一) 基本運行原理
X Window System採用C/S結構,但和我們常見的C/S不同。常見的C/S結構中,稱提供服務的一方為server,即伺服器端(如HTTP服務,FTP服務等),使用服務的稱為client,即用戶端。但在X Window System中,client是執行程式的一方,在上面執行各種X程式,而server則是負責顯示client運行程式的視窗的一方。
X Window System的組成可以分為X server,X client,X protocol三部分。X server主要控制輸入輸出,維護字型,顏色等相關資源。它接受輸入裝置的輸入資訊並傳遞給X client,X client將這些資訊處理後所返回的資訊,也由X server負責輸出到輸出裝置(即我們所見的顯示器)上。X server傳遞給X client的資訊稱為Event,主要是鍵盤滑鼠輸入和視窗狀態的資訊。X client傳遞給X server的資訊則稱為Request,主要是要求X server建立視窗,更改視窗大小位置或在視窗上繪圖輸出文字等。X client主要是完成應用程式計算處理的部分,並不接受使用者的輸入資訊,輸入資訊都是輸入給X server,然後由X server以Event的形式傳遞給X client(這裡感覺類似Windows的訊息機制,系統接收到使用者的輸入資訊,然後以訊息的形式傳遞給視窗,再由視窗的訊息處理過程處理)。X client對收到的Event進行相應的處理後,如果需要輸出到螢幕上或更改畫面的外觀等,則發出Request給X server,由X server負責顯示。
常見的情況是X server與X client都在同一台電腦上運行,但他們也可分別位於網路上不同的電腦上。在X Window System中,X client是與硬體無關的,它並不關心你使用的是什麼顯卡什麼顯示器什麼鍵盤滑鼠,這些只與X server相關。我們平常安裝完XFree86後運行xf86config或xf86cfg進行的配置實際上只是與X server有關,可以說就是配置X server吧,不配置照樣可以運行X client程式(如:xeyes -display xserver:0就可以在xserver這台機器上的0號螢幕(螢幕編號displaynumber為0)上顯示那對大眼睛了)。
X protocol就是X server於X client之間通訊的協議了。X protocol支援現在常用的網路通訊協定。我只能測試TCP/IP,可以看到X server偵聽在tcp 6000連接埠上。那X protocol就是位於運輸層以上了,應該屬於應用程式層吧?。
總結下運行過程吧:
(1) 使用者通過滑鼠鍵盤對X server下達操作命令
(2) X server利用Event傳遞使用者操作資訊給X client
(3) X client進行程式運算
(4) X client利用Request傳回所要顯示的結果
(5) X server將結果顯示在螢幕上
二) 啟動過程
我們從控制台進入X一般是用startx命令。下面就從startx分析起。首先man startx和man xinit可以看到staratx和xinit的使用方法:
startx [[client] options .....] [-- [server] options ....]
xinit [[client] options ] [-- [server] [display] options]
把上面[client]和[server]分別稱為client程式和server程式。man手冊裡寫明其必須以/或者./開頭。
下面看看startx這個指令碼,中文為我加的注釋,這個指令碼是安裝x-window-system-core後得到的,都是XFree86,不同發行版的linux裡該指令碼應該大同小異:
代碼:
#!/bin/shuserclientrc=$HOME/.xinitrc #使用者的client定義檔案userserverrc=$HOME/.xserverrc #使用者的server定義檔案sysclientrc=/usr/X11R6/lib/X11/xinit/xinitrc #系統的clientsysserverrc=/usr/X11R6/lib/X11/xinit/xserverrc #系統的serverdefaultclient=/usr/X11R6/bin/xterm #預設的client程式defaultserver=/usr/X11R6/bin/X #預設的server程式defaultclientargs="" #下面定義了client和server的參數變數defaultserverargs=""clientargs=""serverargs=""#如果使用者client檔案存在則使用使用者檔案裡定義的client,否則使用系統定義的clientif [ -f $userclientrc ]; then defaultclientargs=$userclientrcelif [ -f $sysclientrc ]; then defaultclientargs=$sysclientrcfi#如果使用者server檔案存在則使用使用者檔案裡定義的server,否則使用系統定義的serverif [ -f $userserverrc ]; then defaultserverargs=$userserverrcelif [ -f $sysserverrc ]; then defaultserverargs=$sysserverrcfi#下面迴圈處理client和server的參數whoseargs="client"while [ x"$1" != x ]; do #若第一個參數為空白,退出迴圈 case "$1" in # '' required to prevent cpp from treating "/*" as a C comment. /''*|/./''*) #如果$1是/*或者./*形式(xinit程式要求其參數裡的client程式和server程式必須以/或./開頭,否則會被視為client程式和server程式的參數,見man xinit)if [ "$whoseargs" = "client" ]; then #如果當前是在處理client的參數 if [ x"$clientargs" = x ]; then #如果clientargs為空白,則賦值$1給client變數,也即上面#startx使用方法裡的[client]參數client="$1" elseclientargs="$clientargs $1" #否則clientargs賦值為$clientargs $1,即上面#startx使用#方法裡的options參數 fielse #當前在處理server的參數,代碼的含義同上 if [ x"$serverargs" = x ]; thenserver="$1" elseserverargs="$serverargs $1" fifi;; --)#如果$1為--,則表示開始處理server的參數,--為client和server參數的分界whoseargs="server";; *)if [ "$whoseargs" = "client" ]; then #處理給client程式的參數 clientargs="$clientargs $1"else #處理給server程式的參數 # display must be the FIRST server argument#螢幕編號必須為第一個給server程式的參數,以:x的形式(x為數字),這可從上面startx和xinit 的使用方法的區別看出,xinit多了個[display],這裡即過濾出這個[display]。試試看這兩個命令:xinit /usr/bin/X11/xeyes -display localhost:1 -- /usr/bin/X11/X :1 -dpi 70&xinit /usr/bin/X11/xeyes -display localhost:1 -- /usr/bin/X11/X -dpi 70 :1&即可看出不把螢幕編號作為第一個server參數的後果 if [ x"$serverargs" = x ] && expr "$1" : ':[0-9][0-9]*$' > /dev/null 2>&1; thendisplay="$1" else #處理螢幕編號以外的參數serverargs="$serverargs $1" fifi;; esac shift #所有參數左移一次done# process client argumentsif [ x"$client" = x ]; then #如果client程式為空白 # if no client arguments either, use rc file instead if [ x"$clientargs" = x ]; then #且clientargs為空白,賦值$defaultclientargs給client程式client="$defaultclientargs" elseclient=$defaultclient #使用預設的client程式 fifi# process server arguments處理server參數,同上if [ x"$server" = x ]; then # if no server arguments or display either, use rc file instead if [ x"$serverargs" = x -a x"$display" = x ]; thenserver="$defaultserverargs" elseserver=$defaultserver fifi#…………省略授權碼若干xinit $client $clientargs -- $server $display $serverargs #把處理過的參數交由xinit程式處理#………… |
由上面代碼可以得出startx主要是置X client和X server所在的位置,並處理相關參數,最後交給xinit處理。可以看出startx 設定X client的位置是先搜尋$HOME/.xinitrc,然後是/etc/X11/xinit/xinitrc;設定X server的位置是先搜尋$HOME/.xserverrc,然後是/etc/X11/xinit/xserverrc。這就解釋了我們平常為什麼說啟動X Window時使用者目錄下的.xinitrc和.xserverrc檔案優先順序要高。所以我們用startx命令啟動X時,如果使用者目錄存在.xinitrc和.xserverrc檔案,則實際上等價於命令xinit $HOME/.xinitrc -- $HOME/.xserverrc 。如果使用者目錄不存在那兩個檔案,則等價於xinit /usr/X11R6/lib/X11/xinit/xinitrc -- /usr/X11R6/lib/X11/xinit/xserver。別的情況類推。
至於xinit,則根據startx傳過來的參數啟動X server,成功後根據xinitrc啟動X client。
以上即為X Window System的啟動過程,startx只是負責一些參數傳遞,真正的X啟動由xinit實現。實際上可以分為啟動X server和啟動X client兩部分。下面在使用者目錄下構造.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,這裡X server即是/usr/bin/X11/X 程式,X client即是/usr/bin/X11/xeyes 程式。
總結下單機用startx啟動過程吧:
(1) startx置X client和X server的位置,處理參數並調用xinit
(2) xinit根據傳過來的參數啟動X server,成功後呼叫X client
(3) 根據xinitrc設定相關資源,啟動視窗管理器,IME和其他應用程式等X client程式。
但還未搞清楚gnome是怎麼起來的!gnome當然屬於X client了,看上面啟動過程第(3)步。
這裡分兩種情況看吧,第一種是用系統的xinitrc檔案。看/etc/X11/xinit/xinitrc檔案(我的sarge裝x-window-system-core和gnome-core),裡面只包含了. /etc/X11/Xsession一句話。接著看/etc/X11/Xsession這個指令碼,只看關鍵區段吧。最後面有:
代碼:
SESSIONFILES=$(run_parts $SYSSESSIONDIR)if [ -n "$SESSIONFILES" ]; then for SESSIONFILE in $SESSIONFILES; do . $SESSIONFILE donefiexit 0[code]接著看run_parts(),位於本檔案中間:[code]run_parts () { # until run-parts --noexec is implemented if [ -z "$1" ]; then internal_errormsg "run_parts() called without an argument." fi if [ ! -d "$1" ]; then internal_errormsg "run_parts() called, but /"$1/" does not exist or is" / "not a directory." fi for F in $(ls $1); do if expr "$F" : '[[:alnum:]_-]/+$' > /dev/null 2>&1; then if [ -f "$1/$F" ]; then echo "$1/$F" fi fi done} |
大概意思就是run_parts () 把$SYSSESSIONDIR目錄下的檔案名稱取出來賦值給$SESSIONFILES,然後迴圈運行該目錄下的檔案。看看該目錄,即/etc/X11/Xsession.d目錄,可以看到幾個以數字開頭的檔案,實際上這些數值就表示了這幾個檔案被啟動並執行優先順序,數字小的優先順序高,因為在上面的run_parts () 裡是用ls命令顯示該目錄下的檔案,所以前面數字小的被ls時顯示在前面,所以被
代碼:
for SESSIONFILE in $SESSIONFILES; do . $SESSIONFILE done |
這個for迴圈執行時也先被執行。看到/etc/X11/Xsession.d目錄下有個55gnome-session_gnomerc檔案,裡面提到了STARTUP變數。然後運行:
xdkui@Debian:/etc/X11/Xsession.d$ grep STARTUP *
看到50xfree86-common_determine-startup檔案。裡面有
代碼:
if [ -z "$STARTUP" ]; then if [ -x /usr/bin/x-session-manager ]; then STARTUP=x-session-manager elif [ -x /usr/bin/x-window-manager ]; then STARTUP=x-window-manager elif [ -x /usr/bin/x-terminal-emulator ]; then STARTUP=x-terminal-emulator fifi |
即設定啟動程式,實際上設定STARTUP變數,如果以上程式都沒有找到,則會報錯退出,即X環境沒有被啟動。再運行
xdkui@Debian:/etc/X11/Xsession.d$ grep STARTUP *
看到優先順序最低也即最後被啟動並執行99xfree86-common_start檔案,裡面只有一句話:
exec $STARTUP
好了,到這裡就啟動我們的X client了,終於完了^_^。總結下這第一種方式的啟動過程,簡單的說就是依次順序尋找/usr/bin/x-session-manager ,x-window-manager,/usr/bin/x-terminal-emulator 這三個檔案。如果存在則啟動之,也即X client。如果三個都不存在則報錯退出了。看/usr/bin/x-session-manager檔案可以看到是個符號串連,最終串連到/usr/bin/gnome-session,也就是gnome 了。至於我們在gnome 啟動時可能會設定啟動IME等程式,那就歸gnome-session管了,也就不再分析了。可以試著把/usr/bin/x-session-manager 改為指向xfce4-session(如果安裝了的話) ,再startx就會啟動xfce4環境了。大概RedHat的switchdesk工具就是改這個串連實現的吧?。或者刪掉/usr/bin/x-session-manager ,再startx,只啟動了/usr/bin/x-window-manager 所指向的window manager了吧,我這裡是blackbox。
下面看第二種情況,即使用者目錄的xinitrc檔案$HOME/.xinitrc。對比hiweed-debian-desktop_0.55_i386,存在$HOME/.xinitrc檔案,在裡面有exec xfce4-session。故其X client可以說最主要的x-session-manger是從$HOME/.xinitrc啟動的。也就不會經過上面第一種情況的執行過程了。
終於把gnome(或者說x-session-manger)的啟動過程弄明白了,下面說點別的吧。xinit程式同時啟動X server和X client,這在單機上還可。要是位於網路上的兩台電腦分別是client和server,則xinit就無能為力了。這時就得靠純“手工”來啟動X了。下面簡單的“手工”啟動X server和X client:在CUI模式下運行命令:
xdkui@Debian:~$X :1&
看到了一個灰色的全螢幕和一個滑鼠指標,這就是X server了,其螢幕編號為1。下面構造X client,按Ctrl+Alt+F1回到剛才的CUI(Ctrl+Alt+F7對應原生第一個啟動的X server,Ctrl+Alt+F8對應第二個,有人說F7對應螢幕編號為0的X server實際上是不對的,如果第一個啟動的螢幕編號為1,第二個啟動的編號為0,則F7對應1螢幕,F8對應0螢幕),運行命令:xdkui@Debian:~$xeyes -display localhost:1&
然後按Ctrl+Alt+F7,看到我們的X client也就是xeyes了吧。再回到CUI,運行
xdkui@Debian:~$X&
開啟一個螢幕編號0的X server,CUI下再運行
xdkui@Debian:~$xterm&
這時Ctrl+Alt+F7對應螢幕編號1;而F8對應螢幕編號0,且其X client為xterm。先退出上面的兩個X server,下面複雜點手動啟動我們的gnome吧,首先
xdkui@Debian:~$X&
然後
xdkui@Debian:~$gnome-session
看到的就和用startx 啟動的X一樣了,這時X server是X這個程式,X client是gnome-session及其啟動的視窗管理器等程式。看到這裡感覺xinit用處並不大(??不知是否正確),簡單的指令碼就可以實現。本來想把xinit反組譯碼了分析下,可懶得搞了^_^這是位於原生情況,對於X server和X client位於不同主機的情況見下面本文第三部分。
個人感覺對於X Window System,搞清楚X server與X client關係很重要。一般X server很簡單,就是/usr/bin/X11/X程式;X client則花樣繁多,從進階的CDE,GNOME,KDE,到低級一點的只有twm,Window Maker,blackbox等視窗管理器,再到最簡陋的只有xterm,rxvt,xeyes等單個x程式。正是由於X client的各種搭配,使得我們的X Window System看起來多樣化。這可能也是X Window System最大的賣點之一吧 ^_^
三) 跨網路運行X Window System
一般用來做伺服器的系統(Linux,FreeBSD,Solaris等等)都不會裝X server,甚至很多都沒有顯示器。這樣可以在這些系統裡安裝簡單的X client,以GUI的方式遠程顯示在管理員們所坐的X server裡。我們實驗室用FreeBSD做網關,提供WWW,FTP服務,一般在管理員的本地機器起個X server,然後ssh或telnet上網關運行X client程式顯示在本地顯示器上,當然,也可用XDMCP(X Display Manager Control Protocol),man xsession裡提到/etc/X11/Xsession一般被startx(我的/etc/X11/xinit/xinitrc裡調用Xsession指令碼)或display manager調用,但有的display manager只調用Xsession而不是xinitrc,故為了startx和display manager兩種方式下都可正常啟動GUI,最好把X client啟動的程式放在Xsession檔案裡。遠程運行X client程式需要設定DISPLAY環境變數,設定為 主機名稱:螢幕編號(如192.168.1.2:0,則表示X server是192.168.1.2這台機器上的0號螢幕);或是給X client程式加個—display參數。由於條件限制,只測試了位於TCP/IP網路環境,X server為192.168.1.2,X client為192.168.1.1。
1) Windows系統做X server
a) 用ssh或telnet方式
Windows下面的X server軟體有很多種,我這裡使用X-win32。在Windows裡運行X-win32程式,則相當於本地機器是個X server。遠程登入上Debian(我這裡是用VMware模擬網路環境,直接進虛擬機器即可^_^),運行:
xdkui@xclient:~$export DISPLAY=192.168.1.2:0
xdkui@xclient:~$xterm&
這時即在Windows裡的X server裡看到了xterm了,至於X client還運行什麼程式就看你的需要了,檔案管理工具阿,資源查看器等。當然,這裡X-win32要設定好授權,好像預設是禁止接入控制,即任何X client都可使用這個X server。
b) XDMCP方式
常見的Display Manager有xdm,gdm,kdm等。我這裡使用的是gdm。需要修改gdm的設定檔/etc/X11/gdm/gdm.conf,修改[xdmcp]段的Enable=true,使得可以遠程登入,在X client運行gdm。
在X-win32裡建一個XDMCP的session,查詢方式,填入IP為運行gdm的機器地址。串連,即可看到登入介面,下面的就不用說了,享受吧
2) Linux與Linux互聯
a) ssh或telnet方式
在linux本地起個X server,需要注意授權問題,建立檔案/etc/X0.hosts,填入X client的IP192.168.1.1,其中X0.hosts表示本地第0個螢幕允許串連的X client地址,建立X1.hosts檔案則是本地第1個螢幕允許串連的X client地址,以此類推,man xserver裡有。運行
xdkui@xserver:~$X&
運行該程式時別加-nolisten參數,否則不會在網路上偵聽。
這個時候Ctrl+Alt+F7是X server,返回Ctrl+Alt+F1還可以ssh上X client機器上。
然後登入上X client,運行
xdkui@xclient:~$xterm –display 192.168.1.2:0
即可在本地的X server裡看到xterm了,如果有的話,還可把gnome-session也顯示在本地來。同樣可以在linux裡的VMware裡做這個測試,需要用點手腕了^_^見下
b) XDMCP方式
在我們的X client裡運行gdm(別忘了修改gdm.conf),然後在本地X server的CUI下面運行X -query 192.168.1.1(X client開gdm機器的地址)。可以看到登入介面了吧。
我是在linux裡的VMware裡做的測試,說說所用的手腕吧。在Ctrl+Alt+F1的CUI下正常運行startx&啟動GUI,這時Ctrl+Alt+F7即為我的X server,X client啟動的gnome,然後在這裡運行VMware開啟Debian虛擬機器,並運行gdm。然後回到Ctrl+Alt+F1,運行X :1 -query 192.168.1.1。看到登入介面了吧。這時Ctrl+Alt+F7為我的0號螢幕,裡面運行了虛擬機器。Ctrl+Alt+F8為1號螢幕,在遠程GUI登入X client。相當於我在本地起了兩個X server。
X Window System設計的真是相當神奇,使用方法更是眼花繚亂。佩服………
PS:這篇文章參考了人民郵電出版的《X Window徹底研究》,有小部分是裡面的原話。有興趣可以看看,不過裡面只有2~3章值得一看,不到一個小時即可翻完,但確有助於理解X Window System。打了這麼多字,希望對想瞭解的朋友有協助,不然可就辛苦我了阿小弟水平有限,如有錯誤之處,還請指正^_^