探究X Window System運行原理與啓動過程

摘自 http://bbs.chinaunix.net/forum/4/041229/474678.html 作者:xdkui

第一次在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/sh
userclientrc=$HOME/.xinitrc #用戶的client定義文件
userserverrc=$HOME/.xserverrc #用戶的server定義文件
sysclientrc=/usr/X11R6/lib/X11/xinit/xinitrc #系統的client
sysserverrc=/usr/X11R6/lib/X11/xinit/xserverrc #系統的server
defaultclient=/usr/X11R6/bin/xterm #默認的client程序
defaultserver=/usr/X11R6/bin/X #默認的server程序
defaultclientargs="" #下面定義了client和server的參數變量
defaultserverargs=""
clientargs=""
serverargs=""
#如果用戶client文件存在則使用用戶文件裏定義的client,否則使用系統定義的client
if [ -f $userclientrc ]; then
defaultclientargs=$userclientrc
elif [ -f $sysclientrc ]; then
defaultclientargs=$sysclientrc
fi
#如果用戶server文件存在則使用用戶文件裏定義的server,否則使用系統定義的server
if [ -f $userserverrc ]; then
defaultserverargs=$userserverrc
elif [ -f $sysserverrc ]; then
defaultserverargs=$sysserverrc
fi
#下面循環處理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"
else
clientargs="$clientargs $1" #否則clientargs賦值爲$clientargs $1,即上面#startx使用#方法裏的options參數
fi
else #當前在處理server的參數,代碼的含義同上
if [ x"$serverargs" = x ]; then
server="$1"
else
serverargs="$serverargs $1"
fi
fi
;;
--)#如果$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; then

display="$1"
else #處理屏幕編號以外的參數
serverargs="$serverargs $1"
fi
fi
;;
esac
shift #所有參數左移一次
done
# process client arguments
if [ x"$client" = x ]; then #如果client程序爲空
# if no client arguments either, use rc file instead
if [ x"$clientargs" = x ]; then #且clientargs爲空,賦值$defaultclientargs給client程序
client="$defaultclientargs"
else
client=$defaultclient #使用默認的client程序
fi
fi
# 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 ]; then
server="$defaultserverargs"
else
server=$defaultserver
fi
fi
#............省略授權代碼若干

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設置相關資源,啓動窗口管理器,輸入法和其他應用程序等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
done
fi
exit 0
接着看run_parts(),位於本文件中間:
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
fi
fi
即設置啓動程序,實際上設置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 啓動時可能會設置啓動輸入法等程序,那就歸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設計的真是相當神奇,使用方法更是眼花繚亂。佩服.........

發佈了27 篇原創文章 · 獲贊 0 · 訪問量 4萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章