Linux Kernel 核心中文手冊(2)--軟件基礎

Software Basic( 軟件基礎 )
 
    程序是用於執行特定任務的計算機指令組合。程序可以用彙編語言,一種非常
低級的計算機語言來編寫,也可以使用和機器無關的高級語言,比如 C 語言編寫
。操作系統是一個特殊的程序,允許用戶通過它運行應用程序,比如電子錶和文字
處理等等。本章介紹了基本的編程原理,並簡介操作系統的目的和功能。
 
2.1 Computer Languages( 計算機語言 )
 
2.1.1. 彙編語言
 
    CPU 從內存中讀取和執行的指令對於人類來講無法理解。它們是機器代碼,精
確的告訴計算機要做什麼。比如十六進制數 0x89E5 ,是 Intel 80486 的指令,


將寄存器 ESP 的內容拷貝到寄存器 EBP 中。早期計算機中最初的軟件工具之一是
彙編程序,它讀入人類可以閱讀的源文件,將其裝配成機器代碼。彙編語言明確地
處理對寄存器和對數據的操作,而這種操作對於特定的微處理器而言是特殊的。
Intel X86 微處理器的彙編語言和 Alpha AXP 微處理器的彙編語言完全不同。以
下 Alpha AXP 彙編代碼演示了程序可以執行的操作類型:
 
Ldr r16, (r15) ; 第一行
 
Ldr r17, 4(r15) ; 第二行
 
Beq r16,r17,100; 第三行
 
Str r17, (r15); 第四行
 
100: ; 第五行
 
    第一條語句(第一行)將寄存器 15 指定的地址中的內容加載到寄存器 16 中
。第二條指令將緊接着的內存中的內容加載到寄存器 17 中。第三行比較寄存器
16 和寄存器 17 ,如果相等,分支到標號 100 ,否則,繼續執行第四行,將寄存
器 17 的內容存到內存中。如果內存中的數據相同,就不必存儲數據。編寫彙編級
的程序需要技巧而且十分冗長,容易出錯。 Linux 系統的核心很少的一部分是用
彙編語言編寫,而這些部分之所以使用彙編語言只是爲了提高效率,並且和具體的


微處理器相關。
 
2.1.2 The C Programming Language and Compiler (C 語言和編譯器 )
 
    使用彙編語言編寫大型程序十分困難,消耗時間,容易出錯而且生成的程序不
能移植,只能束縛在特定的處理器家族。更好的選擇是使用和機器無關的語言,例
如 C 。 C 允許你用邏輯算法描述程序和要處理的數據。被稱爲編譯程序(
compiler )的特殊程序讀入 C 程序,並將它轉換爲彙編語言,進而產生機器相關
的代碼。好的編譯器生成的彙編指令可以和好的彙編程序員編寫的程序效率接近。
大部分 Linux 核心是用 C 語言編寫的。以下的 C 片斷:
 
if (x != y)
 
x = y;
 
    執行了和前面示例中彙編代碼完全一樣的操作。如果變量 x 的內容和變量
y 的內容不一樣,變量 y 的內容被拷貝到變量 x 。 C 代碼用例程( routine )
進行組合,每一個例程執行一項任務。例程可以返回 C 所支持的任意的數值或數
據類型。大型程序比如 Linux 核心分別由許多的 C 語言模塊組成,每一個模塊有
自己的例程和數據結構。這些 C 源代碼模塊共同構成了邏輯功能比如文件系統的
處理代碼。
 


   C 支持多種類型的變量。一個變量是內存中的特定位置,可用符號名引用。上
述的 C 片斷中, x 和 y 引用了內存中的位置。程序員不需要關心變量在內存中
的具體位置,這是連接程序(下述)必須處理的。一些變量包含不同的數據例如整
數、浮點數等和另一些則包含指針。
 
    指針是包含其它數據在內存中的地址的變量。假設一個變量 x ,位於內存地
址 0x80010000 , 你可能有一個指針 px ,指向 x 。 Px 可能位於地址
0x80010030 。 Px 的值則是變量 x 的地址, 0x80010000 。
 
C 允許你將相關的變量集合成爲結構。例如:
 
Struct {
 
Int I;
 
Char b;
 
} my_struct;
 
是一個叫做 my_struct 的數據結構,包括兩個元素:一個整數( 32 位) I 和一
個字符( 8 位數據) b 。
 


2.1.3 Linkers (連接程序)
 
    連接程序將幾個目標模塊和庫文件連接在一起成爲一個單獨的完整程序。目標
模塊是彙編程序或編譯程序的機器碼輸出,它包括機器碼、數據和供連接程序使用
的連接信息。比如:一個目標模塊可能包括程序的所有數據庫功能,而另一個目標
模塊則包括處理命令行參數的函數。連接程序確定目標模塊之間的引用關係,即確
定一個模塊所引用的例程和數據在另一個模塊中的實際位置。 Linux 核心是由多
個目標模塊連接而成的獨立的大程序。
 
2.2 What is an Operating System (什麼是操作系統?)
 
    沒有軟件,計算機只是一堆發熱的電子元件。如果說硬件是計算機的心臟,則
軟件就是它的靈魂。操作系統是允許用戶運行應用程序的一組系統程序。操作系統
將系統的硬件抽象,呈現在用戶和應用程序之前的是一個虛擬的機器。是軟件造就
了計算機系統的特點。大多數 PC 可以運行一到多個操作系統,而每一個操作系統
從外觀和感覺上都大不相同。 Linux 由不同功能的部分構成,這些部分總體組合
構成了 Linux 操作系統。 Linux 最明顯的部分就是 Kernel 自身,但是如果沒有
 shell 或 libraries 一樣沒有用處。
 
    爲了瞭解什麼是操作系統,看一看在你輸入最簡單的命令時發生了什麼:
 
 


$ls
 
Mail c images perl
 
Docs tcl
 
$
 
    這裏的 $ 是登錄的 shell 輸出的提示符(此例是 bash ):表示 shell 在
等候你(用戶)輸入命令。輸入 ls 引發鍵盤驅動程序識別輸入的字符,鍵盤驅動
程序將識別的字符傳遞給 shell 去處理。 shell 先查找同名的可執行映象,它找
到了 /bin/ls, 然後調用核心服務將 ls 執行程序加載到虛擬內存中並開始執行。
 ls 執行程序通過執行核心的文件子系統的系統調用查找文件。文件系統可能使用
緩存的文件系統信息或通過磁盤設備驅動程序從磁盤上讀取文件信息 , 也可能是
通過網絡設備驅動程序同遠程主機交換信息而讀取本系統所訪問的遠程文件的詳細
信息(文件系統可以通過 NFS 網絡文件系統遠程安裝)。不管文件信息是如何得
到的, ls 都將信息輸出,通過顯示驅動程序顯示在屏幕上。
 
    以上的過程看起來相當複雜,但是它說明了即使是最簡單的命令也是操作系統
各個功能模塊之間共同協作的結果,只有這樣才能提供給你(用戶)一個完整的系
統視圖。
 


2.2.1 Memory management (內存管理)
 
    如果擁有無限的資源,例如內存,那麼操作系統所必須做的很多事情可能都是
多餘的。所有操作系統的一個基本技巧就是讓少量的物理內存工作起來好像有相當
多的內存。這種表面看起來的大內存叫做虛擬內存,就是當軟件運行的時候讓它相
信它擁有很多內存。系統將內存分爲容易處理的頁,在系統運行時將這些頁交換到
硬盤上。而應用軟件並不知道,因爲操作系統還使用了另一項技術:多進程。
 
2.2.2 Processes ( 進程 )
 
    進程可以看作一個在執行的程序,每一個進程都是正在運行的特定的程序的獨
立實體。如果你觀察一下你的 Linux 系統,你會發現有很多進程在運行。例如:
在我的系統上輸入 ps 顯示了以下進程:
 
$ ps
 
PID TTY STAT TIME COMMAND
 
158 pRe 1 0:00 -bash
 
 
174 pRe 1 0:00 sh /usr/X11R6/bin/startx


 
175 pRe 1 0:00 xinit /usr/X11R6/lib/X11/xinit/xinitrc --
 
178 pRe 1 N 0:00 bowman
 
182 pRe 1 N 0:01 rxvt -geometry 120x35 -fg white -bg black
 
184 pRe 1 < 0:00 xclock -bg grey -geometry -1500-1500 -padding 0
 
185 pRe 1 < 0:00 xload -bg grey -geometry -0-0 -label xload
 
187 pp6 1 9:26 /bin/bash
 
202 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black
 
203 ppc 2 0:00 /bin/bash
 
1796 pRe 1 N 0:00 rxvt -geometry 120x35 -fg white -bg black
 
 
1797 v06 1 0:00 /bin/bash
 


3056 pp6 3 < 0:02 emacs intro/introduction.tex
 
3270 pp6 3 0:00 ps
 
$
 
    如果我的系統擁有多個 CPU 那麼每個進程可能(至少在理論上如此)都在不
同的 CPU 上運行。不幸的是,只有一個,所以操作系統又使用技巧,在短時間內
依次運行每一個進程。這個時間段叫做時間片。這種技巧叫做多進程或調度,它欺
騙了每一個進程,好像它們是唯一的進程。進程相互之間受到保護,所以如果一個
進程崩潰或不能工作,不會影響其他進程。操作系統通過給每一個進程一個獨立的
地址空間來實現保護,進程只能訪問它自己的地址空間。
 
2.2.3 Device Drivers (設備驅動程序)
 
    設備驅動程序組成了 Linux 核心的主要部分。象操作系統的其他部分一樣,
它們在一個高優先級的環境下工作,如果發生錯誤,可能會引發嚴重問題。設備驅
動程序控制了操作系統和它控制的硬件設備之間的交互。比如:文件系統向 IDE
磁盤寫數據塊是使用通用塊設備接口。驅動程序控制細節,並處理和設備相關的部
分。設備驅動程序和它驅動的具體的控制器芯片相關,所以,如果你的系統有一個
 NCR810 的 SCSI 控制器,那麼你需要 NCR810 的驅動程序。
 


2.2.4 The Filesystems (文件系統)
 
    象 Unix 一樣,在 Linux 裏,系統對獨立的文件系統不是用設備標示符來存
取(比如驅動器編號或驅動器名稱),而是連接成爲一個樹型結構。 Linux 在安
裝新的文件系統時,把它安裝到指定的安裝目錄,比如 /mnt/cdrom ,從而合併到
這個單一的文件系統樹上。 Linux 的一個重要特徵是它支持多種不同的文件系統
。這使它非常靈活而且可以和其他操作系統良好共存。 Linux 最常用的文件系統
是 EXT2 ,大多數 Linux 發佈版都支持。
 
    文件系統將存放在系統硬盤上的文件和目錄用可以理解的統一的形式提供給用
戶,讓用戶不必考慮文件系統的類型或底層物理設備的特性。 Linux 透明的支持
多種文件系統(如 MS-DOS 和 EXT2 ),將所有安裝的文件和文件系統集合成爲一
個虛擬的文件系統。所以,用戶和進程通常不需要確切知道所使用的文件所在的文
件系統的類型,用就是了。
 
    塊設備驅動程序掩蓋了物理塊設備類型的區別(如 IDE 和 SCSI )。對於文
件系統來講,物理設備就是線性的數據塊的集合。不同設備的塊大小可能不同,如
軟驅一般是 512 字節,而 IDE 設備通常是 1024 字節,同樣,對於系統的用戶,
這些區別又被掩蓋。 EXT2 文件系統不管它用什麼設備,看起來都是一樣的。
 
2.3 Kernet Data Structures (核心數據結構)
 


    操作系統必須紀錄關於系統當前狀態的許多信息。如果系統中發生了事情,這
些數據結構就必須相應改變以反映當前的實際情況。例如:用戶登錄到系統中的時
候,需要創建一個新的進程。核心必須相應地創建表示此新進程的數據結構,並和
表示系統中其他進程的數據結構聯繫在一起。
 
    這樣的數據結構多數在物理內存中,而且只能由核心和它的子系統訪問。數據
結構包括數據和指針(其他數據結構或例程的地址)。乍一看, Linux 核心所用
的數據結構可能非常混亂。其實,每一個數據結構都有其目的,雖然有些數據結構
在多個的子系統中都會用到,但是實際上它們比第一次看到時的感覺要簡單的多。
 
 
    理解 Linux 核心的關鍵在於理解它的數據結構和核心處理這些數據結構所用
到的大量的函數。本書以數據結構爲基礎描述 Linux 核心。論及每一個核心子系
統的算法,處理的方式和它們對核心數據結構的使用。
 
2.3.1 Linked Lists (連接表)
 
    Linux 使用一種軟件工程技術將它的數據結構連接在一起。多數情況下它使用
鏈表數據結構。如果每一個數據結構描述一個物體或者發生的事件的單一的實例,
比如一個進程或一個網絡設備,核心必須能夠找出所有的實例。在鏈表中,根指針
包括第一個數據結構或單元的地址,列表中的每一個數據結構包含指向列表下一個
元素的指針。最後元素的下一個指針可能使 0 或 NULL ,表示這是列表的結尾。


在雙向鏈表結構中,每一個元素不僅包括列表中下一個元素的指針,還包括列表中
前一個元素的指針。使用雙向鏈表可以比較容易的在列表中間增加或刪除元素,但
是這需要更多的內存存取。這是典型的操作系統的兩難情況:內存存取數還是 CPU
 的週期數。
 
2.3.2 Hash Tables
 
    鏈接表是常用的數據結構,但是遊歷鏈接表的效率可能並不高。如果你要尋找
指定的元素, 可能必須查找完整個表才能找到。 Linux 使用另一種技術:
Hashing 來解決這種侷限。 Hash table 是指針的數組或者說向量表。數組或向量
表是在內存中依次存放的對象。書架可以說是書的數組。數組用索引來訪問,索引
是數組中的偏移量。再來看書架的例子,你可以使用在書架上的位置來描述每一本
書:比如第 5 本書。
 
    Hash table 是一個指向數據結構的指針的數組,它的索引來源於數據結構中
的信息。如果你用一個數據結構來描述一個村莊的人口,你可以用年齡作爲索引。
要找出一個指定的人的數據,你可以用他的年齡作爲索引在人口散列表中查找,通
過指針找到包括詳細信息的數據結構。不幸的是,一個村莊中可能很多人年齡相同
,所以散列表的指針指向另一個鏈表數據結構,每一個元素描述同齡人。即使這樣
,查找這些較小的鏈表仍然比查找所有的數據結構要快。
 
    Hash table 可用於加速常用的數據結構的訪問,在 Linux 裏常用 hash


table 來實現緩衝。緩衝是需要快速存取的信息,是全部可用信息的一個子集。數
據結構被放在緩衝區並保留在那裏,因爲核心經常訪問這些結構。使用緩衝區也有
副作用,因爲使用起來比簡單鏈表或者散列表更加複雜。如果數據結構可以在緩衝
區找到(這叫做緩衝命中),那麼一切很完美。但是如果數據結構不在緩衝區中,
那麼必須查找所用的相關的數據結構,如果找到,那麼就加到緩衝區中。增加新的
數據結構到緩衝區中可能需要廢棄一箇舊的緩衝入口。 Linux 必須決定廢棄那一
個數據結構,風險在於廢棄的可能使 Linux 下一個要訪問的數據結構。
 
2.3.3 Abstract Interfaces (抽象接口)
 
    Linux 核心經常將它的接口抽象化。接口是以特定方式工作的一系列例程和數
據結構。比如:所有的網絡設備驅動程序都必須提供特定的例程來處理特定的數據
結構。用抽象接口的方式可以用通用的代碼層來使用底層特殊代碼提供的服務(接
口)。例如網絡層是通用的,而它由底層符合標準接口的同設備相關的代碼提供支
持。
 
    通常這些底層在啓動時向高一層登記。這個登記過程常通過在鏈接表中增加一
個數據結構來實現。例如,每一個連結到核心的文件系統在覈心啓動時進行登記(
或者如果你使用模塊,在文件系統第一次使用時向核心登記)。你可以查看文件
/proc/filesystems 來檢查那些文件系統進行了登記。登記所用的數據結構通常包
括指向函數的指針。這是執行特定任務的軟件函數的地址。再一次用文件系統登記
的例子,每一個文件系統登記時傳遞給 Linux 核心的數據結構都包括一個和具體


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