?? linu家園-linux kernel核心中文手冊.htm
字號:
<P>2.1.2 The C Programming Language and Compiler (C語言和編譯器)</P>
<P> </P>
<P>使用匯編語言編寫大型程序十分困難,消耗時間,容易出錯而且生成的程序不能移植,只能束縛在特定的處理器家族。更好的選擇是使用和機器無關(guān)的語言,例如C。C允許你用邏輯算法描述程序和要處理的數(shù)據(jù)。被稱為編譯程序(compiler)的特殊程序讀入C程序,并將它轉(zhuǎn)換為匯編語言,進而產(chǎn)生機器相關(guān)的代碼。好的編譯器生成的匯編指令可以和好的匯編程序員編寫的程序效率接近。大部分Linux核心是用C語言編寫的。以下的C片斷:</P>
<P>if (x != y)</P>
<P>x = y;</P>
<P>執(zhí)行了和前面示例中匯編代碼完全一樣的操作。如果變量x的內(nèi)容和變量y的內(nèi)容不一樣,變量y的內(nèi)容被拷貝到變量x。C代碼用例程(routine)進行組合,每一個例程執(zhí)行一項任務(wù)。例程可以返回C所支持的任意的數(shù)值或數(shù)據(jù)類型。大型程序比如Linux核心分別由許多的C語言模塊組成,每一個模塊有自己的例程和數(shù)據(jù)結(jié)構(gòu)。這些C源代碼模塊共同構(gòu)成了邏輯功能比如文件系統(tǒng)的處理代碼。</P>
<P> </P>
<P>C支持多種類型的變量。一個變量是內(nèi)存中的特定位置,可用符號名引用。上述的C片斷中,x和y引用了內(nèi)存中的位置。程序員不需要關(guān)心變量在內(nèi)存中的具體位置,這是連接程序(下述)必須處理的。一些變量包含不同的數(shù)據(jù)例如整數(shù)、浮點數(shù)等和另一些則包含指針。</P>
<P> </P>
<P>指針是包含其它數(shù)據(jù)在內(nèi)存中的地址的變量。假設(shè)一個變量x,位于內(nèi)存地址0x80010000, 你可能有一個指針px,指向x。
Px可能位于地址0x80010030。Px的值則是變量x的地址,0x80010000。</P>
<P> </P>
<P>C允許你將相關(guān)的變量集合成為結(jié)構(gòu)。例如:</P>
<P>Struct {</P>
<P>Int I;</P>
<P>Char b;</P>
<P>} my_struct;</P>
<P>是一個叫做my_struct的數(shù)據(jù)結(jié)構(gòu),包括兩個元素:一個整數(shù)(32位)I和一個字符(8位數(shù)據(jù))b。</P>
<P> </P>
<P>2.1.3 Linkers(連接程序)</P>
<P> </P>
<P>連接程序?qū)讉€目標模塊和庫文件連接在一起成為一個單獨的完整程序。目標模塊是匯編程序或編譯程序的機器碼輸出,它包括機器碼、數(shù)據(jù)和供連接程序使用的連接信息。比如:一個目標模塊可能包括程序的所有數(shù)據(jù)庫功能,而另一個目標模塊則包括處理命令行參數(shù)的函數(shù)。連接程序確定目標模塊之間的引用關(guān)系,即確定一個模塊所引用的例程和數(shù)據(jù)在另一個模塊中的實際位置。Linux核心是由多個目標模塊連接而成的獨立的大程序。</P>
<P> </P>
<P>2.2 What is an Operating System(什么是操作系統(tǒng)?)</P>
<P> </P>
<P>沒有軟件,計算機只是一堆發(fā)熱的電子元件。如果說硬件是計算機的心臟,則軟件就是它的靈魂。操作系統(tǒng)是允許用戶運行應(yīng)用程序的一組系統(tǒng)程序。操作系統(tǒng)將系統(tǒng)的硬件抽象,呈現(xiàn)在用戶和應(yīng)用程序之前的是一個虛擬的機器。是軟件造就了計算機系統(tǒng)的特點。大多數(shù)PC可以運行一到多個操作系統(tǒng),而每一個操作系統(tǒng)從外觀和感覺上都大不相同。Linux由不同功能的部分構(gòu)成,這些部分總體組合構(gòu)成了Linux操作系統(tǒng)。Linux最明顯的部分就是Kernel自身,但是如果沒有shell或libraries一樣沒有用處。</P>
<P> </P>
<P>為了了解什么是操作系統(tǒng),看一看在你輸入最簡單的命令時發(fā)生了什么:</P>
<P> </P>
<P>$ls</P>
<P>Mail c images perl</P>
<P>Docs tcl</P>
<P>$</P>
<P>這里的$是登錄的shell輸出的提示符(此例是bash):表示shell在等候你(用戶)輸入命令。輸入ls引發(fā)鍵盤驅(qū)動程序識別輸入的字符,鍵盤驅(qū)動程序?qū)⒆R別的字符傳遞給shell去處理。shell先查找同名的可執(zhí)行映象,它找到了/bin/ls,
然后調(diào)用核心服務(wù)將ls執(zhí)行程序加載到虛擬內(nèi)存中并開始執(zhí)行。ls執(zhí)行程序通過執(zhí)行核心的文件子系統(tǒng)的系統(tǒng)調(diào)用查找文件。文件系統(tǒng)可能使用緩存的文件系統(tǒng)信息或通過磁盤設(shè)備驅(qū)動程序從磁盤上讀取文件信息,也可能是通過網(wǎng)絡(luò)設(shè)備驅(qū)動程序同遠程主機交換信息而讀取本系統(tǒng)所訪問的遠程文件的詳細信息(文件系統(tǒng)可以通過NFS網(wǎng)絡(luò)文件系統(tǒng)遠程安裝)。不管文件信息是如何得到的,ls都將信息輸出,通過顯示驅(qū)動程序顯示在屏幕上。</P>
<P> </P>
<P>以上的過程看起來相當復(fù)雜,但是它說明了即使是最簡單的命令也是操作系統(tǒng)各個功能模塊之間共同協(xié)作的結(jié)果,只有這樣才能提供給你(用戶)一個完整的系統(tǒng)視圖。</P>
<P> </P>
<P>2.2.1 Memory management(內(nèi)存管理)</P>
<P> </P>
<P>如果擁有無限的資源,例如內(nèi)存,那么操作系統(tǒng)所必須做的很多事情可能都是多余的。所有操作系統(tǒng)的一個基本技巧就是讓少量的物理內(nèi)存工作起來好像有相當多的內(nèi)存。這種表面看起來的大內(nèi)存叫做虛擬內(nèi)存,就是當軟件運行的時候讓它相信它擁有很多內(nèi)存。系統(tǒng)將內(nèi)存分為容易處理的頁,在系統(tǒng)運行時將這些頁交換到硬盤上。而應(yīng)用軟件并不知道,因為操作系統(tǒng)還使用了另一項技術(shù):多進程。</P>
<P> </P>
<P>2.2.2 Processes (進程)</P>
<P> </P>
<P>進程可以看作一個在執(zhí)行的程序,每一個進程都是正在運行的特定的程序的獨立實體。如果你觀察一下你的Linux系統(tǒng),你會發(fā)現(xiàn)有很多進程在運行。例如:在我的系統(tǒng)上輸入ps
顯示了以下進程:</P>
<P>$ ps</P>
<P>PID TTY STAT TIME COMMAND</P>
<P>158 pRe 1 0:00 -bash</P>
<P>174 pRe 1 0:00 sh /usr/X11R6/bin/startx</P>
<P>175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc --</P>
<P>178 pRe 1 N 0:00 bowman</P>
<P>182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black</P>
<P>184 pRe 1 < 0:00 xclock -bg grey -geometry -1500-1500 -padding 0</P>
<P>185 pRe 1 < 0:00 xload -bg grey -geometry -0-0 -label xload</P>
<P>187 pp6 1 9:26 /bin/bash</P>
<P>202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black</P>
<P>203 ppc 2 0:00 /bin/bash</P>
<P>1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black</P>
<P>1797 v06 1 0:00 /bin/bash</P>
<P>3056 pp6 3 < 0:02 emacs intro/introduction.tex</P>
<P>3270 pp6 3 0:00 ps</P>
<P>$</P>
<P> </P>
<P>如果我的系統(tǒng)擁有多個CPU那么每個進程可能(至少在理論上如此)都在不同的CPU上運行。不幸的是,只有一個,所以操作系統(tǒng)又使用技巧,在短時間內(nèi)依次運行每一個進程。這個時間段叫做時間片。這種技巧叫做多進程或調(diào)度,它欺騙了每一個進程,好像它們是唯一的進程。進程相互之間受到保護,所以如果一個進程崩潰或不能工作,不會影響其他進程。操作系統(tǒng)通過給每一個進程一個獨立的地址空間來實現(xiàn)保護,進程只能訪問它自己的地址空間。
</P>
<P>2.2.3 Device Drivers(設(shè)備驅(qū)動程序)</P>
<P> </P>
<P>設(shè)備驅(qū)動程序組成了Linux核心的主要部分。象操作系統(tǒng)的其他部分一樣,它們在一個高優(yōu)先級的環(huán)境下工作,如果發(fā)生錯誤,可能會引發(fā)嚴重問題。設(shè)備驅(qū)動程序控制了操作系統(tǒng)和它控制的硬件設(shè)備之間的交互。比如:文件系統(tǒng)向IDE磁盤寫數(shù)據(jù)塊是使用通用塊設(shè)備接口。驅(qū)動程序控制細節(jié),并處理和設(shè)備相關(guān)的部分。設(shè)備驅(qū)動程序和它驅(qū)動的具體的控制器芯片相關(guān),所以,如果你的系統(tǒng)有一個NCR810的SCSI控制器,那么你需要NCR810的驅(qū)動程序。</P>
<P> </P>
<P>2.2.4 The Filesystems(文件系統(tǒng))</P>
<P> </P>
<P>象Unix一樣,在Linux里,系統(tǒng)對獨立的文件系統(tǒng)不是用設(shè)備標示符來存取(比如驅(qū)動器編號或驅(qū)動器名稱),而是連接成為一個樹型結(jié)構(gòu)。Linux在安裝新的文件系統(tǒng)時,把它安裝到指定的安裝目錄,比如/mnt/cdrom,從而合并到這個單一的文件系統(tǒng)樹上。Linux的一個重要特征是它支持多種不同的文件系統(tǒng)。這使它非常靈活而且可以和其他操作系統(tǒng)良好共存。Linux最常用的文件系統(tǒng)是EXT2,大多數(shù)Linux發(fā)布版都支持。</P>
<P> </P>
<P>文件系統(tǒng)將存放在系統(tǒng)硬盤上的文件和目錄用可以理解的統(tǒng)一的形式提供給用戶,讓用戶不必考慮文件系統(tǒng)的類型或底層物理設(shè)備的特性。Linux透明的支持多種文件系統(tǒng)(如MS-DOS和EXT2),將所有安裝的文件和文件系統(tǒng)集合成為一個虛擬的文件系統(tǒng)。所以,用戶和進程通常不需要確切知道所使用的文件所在的文件系統(tǒng)的類型,用就是了。</P>
<P> </P>
<P>塊設(shè)備驅(qū)動程序掩蓋了物理塊設(shè)備類型的區(qū)別(如IDE和SCSI)。對于文件系統(tǒng)來講,物理設(shè)備就是線性的數(shù)據(jù)塊的集合。不同設(shè)備的塊大小可能不同,如軟驅(qū)一般是512字節(jié),而IDE設(shè)備通常是1024字節(jié),同樣,對于系統(tǒng)的用戶,這些區(qū)別又被掩蓋。EXT2文件系統(tǒng)不管它用什么設(shè)備,看起來都是一樣的。</P>
<P> </P>
<P>2.3 Kernet Data Structures(核心數(shù)據(jù)結(jié)構(gòu))</P>
<P> </P>
<P>操作系統(tǒng)必須紀錄關(guān)于系統(tǒng)當前狀態(tài)的許多信息。如果系統(tǒng)中發(fā)生了事情,這些數(shù)據(jù)結(jié)構(gòu)就必須相應(yīng)改變以反映當前的實際情況。例如:用戶登錄到系統(tǒng)中的時候,需要創(chuàng)建一個新的進程。核心必須相應(yīng)地創(chuàng)建表示此新進程的數(shù)據(jù)結(jié)構(gòu),并和表示系統(tǒng)中其他進程的數(shù)據(jù)結(jié)構(gòu)聯(lián)系在一起。
</P>
<P>這樣的數(shù)據(jù)結(jié)構(gòu)多數(shù)在物理內(nèi)存中,而且只能由核心和它的子系統(tǒng)訪問。數(shù)據(jù)結(jié)構(gòu)包括數(shù)據(jù)和指針(其他數(shù)據(jù)結(jié)構(gòu)或例程的地址)。乍一看,Linux核心所用的數(shù)據(jù)結(jié)構(gòu)可能非常混亂。其實,每一個數(shù)據(jù)結(jié)構(gòu)都有其目的,雖然有些數(shù)據(jù)結(jié)構(gòu)在多個的子系統(tǒng)中都會用到,但是實際上它們比第一次看到時的感覺要簡單的多。</P>
<P> </P>
<P>理解Linux核心的關(guān)鍵在于理解它的數(shù)據(jù)結(jié)構(gòu)和核心處理這些數(shù)據(jù)結(jié)構(gòu)所用到的大量的函數(shù)。本書以數(shù)據(jù)結(jié)構(gòu)為基礎(chǔ)描述Linux核心。論及每一個核心子系統(tǒng)的算法,處理的方式和它們對核心數(shù)據(jù)結(jié)構(gòu)的使用。</P>
<P> </P>
<P>2.3.1 Linked Lists(連接表)</P>
<P> </P>
<P>Linux使用一種軟件工程技術(shù)將它的數(shù)據(jù)結(jié)構(gòu)連接在一起。多數(shù)情況下它使用鏈表數(shù)據(jù)結(jié)構(gòu)。如果每一個數(shù)據(jù)結(jié)構(gòu)描述一個物體或者發(fā)生的事件的單一的實例,比如一個進程或一個網(wǎng)絡(luò)設(shè)備,核心必須能夠找出所有的實例。在鏈表中,根指針包括第一個數(shù)據(jù)結(jié)構(gòu)或單元的地址,列表中的每一個數(shù)據(jù)結(jié)構(gòu)包含指向列表下一個元素的指針。最后元素的下一個指針可能使0或NULL,表示這是列表的結(jié)尾。在雙向鏈表結(jié)構(gòu)中,每一個元素不僅包括列表中下一個元素的指針,還包括列表中前一個元素的指針。使用雙向鏈表可以比較容易的在列表中間增加或刪除元素,但是這需要更多的內(nèi)存存取。這是典型的操作系統(tǒng)的兩難情況:內(nèi)存存取數(shù)還是CPU的周期數(shù)。</P>
<P> </P>
<P>2.3.2 Hash Tables</P>
<P> </P>
<P>鏈接表是常用的數(shù)據(jù)結(jié)構(gòu),但是游歷鏈接表的效率可能并不高。如果你要尋找指定的元素, 可能必須查找完整個表才能找到。Linux使用另一種技術(shù):Hashing
來解決這種局限。Hash
table是指針的數(shù)組或者說向量表。數(shù)組或向量表是在內(nèi)存中依次存放的對象。書架可以說是書的數(shù)組。數(shù)組用索引來訪問,索引是數(shù)組中的偏移量。再來看書架的例子,你可以使用在書架上的位置來描述每一本書:比如第5本書。</P>
<P> </P>
<P>Hash
table是一個指向數(shù)據(jù)結(jié)構(gòu)的指針的數(shù)組,它的索引來源于數(shù)據(jù)結(jié)構(gòu)中的信息。如果你用一個數(shù)據(jù)結(jié)構(gòu)來描述一個村莊的人口,你可以用年齡作為索引。要找出一個指定的人的數(shù)據(jù),你可以用他的年齡作為索引在人口散列表中查找,通過指針找到包括詳細信息的數(shù)據(jù)結(jié)構(gòu)。不幸的是,一個村莊中可能很多人年齡相同,所以散列表的指針指向另一個鏈表數(shù)據(jù)結(jié)構(gòu),每一個元素描述同齡人。即使這樣,查找這些較小的鏈表仍然比查找所有的數(shù)據(jù)結(jié)構(gòu)要快。</P>
?? 快捷鍵說明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -