?? 4.txt
字號:
第4章 系統初始化當你想要運行程序時,你需要把程序的文件名敲入shell—或者更為流行的,在如GNOME或者KDE等之類桌面環境中點擊相應的圖標,這樣就能將其裝載進內核并運行。但是,首先必須有其他的軟件來裝載并運行內核;這通常是諸如LOADLIN或者LILO之類的內核引導程序。更進一步說,我們還需要其他的軟件來裝載運行內核引導程序—稱之“內核引導程序的引導程序”,而且看起來似乎運行內核引導程序的引導程序也需要內核引導程序的引導程序的引導程序,等等,這個過程是無限的。這個無限循環的過程必然最終在某個地方終止,這就是硬件。因此,在最低的層次上,啟動系統的第一步是從硬件中獲得幫助。該硬件總是運行一些短小的內置程序—軟件,但是這些軟件是被固化在只讀存儲器中,存儲在已知地址中。因此,在這種情況下就不需要軟件引導程序了—它能夠運行更大更復雜的程序,直到內核自身裝載成功為止。按照這種方式,系統自己的引導過程(bootstrap)會引發系統的啟動,當然這只是術語“系統引導(booting)”的一個比喻。雖然不同體系結構的引導過程的具體細節差異很大,但是它們的原則都基本相同。前面的工作都完成以后,內核就已經成功裝載了。隨后內核可以初始化自身以及系統的其他部分。本章首先將簡單介紹基于x86 PC機的典型自啟動方式,接著回顧一下每一步工作在什么時機發生,最后我們還要介紹的是內核的相應部分。4.1 引導PC機本節簡要介紹x86 PC是如何引導的。本節的目的不是讓你精通PC是怎樣引導的(這超出了本書的范圍),而是向你展示特定體系結構一般的引導方式,為下文中的內核初始化進行鋪墊。首先,機器中的每個CPU都要自行初始化,接著可能要用幾分之一秒的時間來執行自測試。在多處理器的系統中,這個過程會更復雜些—但是實際上也并不多。在雙處理器的Pentium系統中,一個CPU總是作為主CPU存在,另外一個CPU則是輔CPU。主CPU執行啟動過程中的剩余工作,隨后內核才會激活輔CPU。在多處理器的Pentium Pro系統中,CPU必須根據Intel定義的算法“搶奪標志”來動態決定由哪個CPU啟動系統。取得標志的CPU啟動系統,隨后內核激活其他的CPU。無論是哪種情況,啟動程序的剩余部分只與一個CPU有關。這樣,在隨后的一段時間內,我們可以認為該系統中只有一個CPU是可用的,而不考慮其他的CPU,或者說這些CPU被暫時隱藏了。另一方面,內核還需要明確地激活所有其他的CPU—這一點你可以在本章后續部分看到。接下來,CPU從0xfffffff0地址單元中取得指令并執行,這個地址非常接近于32位CPU的最后可用的地址。因為大多數PC都沒有4GB的RAM,所以通常在這個地址上并沒有實際內存。內存硬件可以虛擬使用它。對那些確實有4GB內存的機器來說,它們也只是僅僅損失了供BIOS使用的頂端地址空間末尾的少量內存(實際上BIOS在這里只保留了64K的空間—這種損失在4GB的機器中是可以忽略的)。該地址單元中存儲的指令是一條跳轉指令,這條指令跳轉到基本輸入輸出系統(BIOS)代碼的首部。BIOS內置在主板中,它主要負責控制系統的啟動。請注意CPU實際上并不真正關心BIOS是否存在,這樣就使得在諸如用戶定制的嵌入系統之類的非PC體系結構的計算機中使用Intel的CPU成為可能。CPU執行在目標地址中發現的任何指令,在這里使用跳轉指令轉移到BIOS只是PC體系結構的一部分(實際上,跳轉指令自己是BIOS的一部分,但是這不是考慮這個問題的最方便的方法)。BIOS使用內置的規則來選擇啟動設備。通常情況下,這些規則是可以改變的,方法是在啟動過程開始時按下一個鍵(例如,在我的系統中是Del鍵)并通過一些菜單選項瀏覽選擇。但是,通常的過程是BIOS首先試圖從軟盤啟動,如果失敗了,就再試圖從主硬盤上啟動。如果又失敗了,就再試圖從CD-ROM上啟動。為了使問題更具體,這里討論的情況假定是最普通的,也就是啟動設備是硬盤。從這種啟動設備上啟動,BIOS讀取第一個扇區的信息—首512個字節,稱之為主引導記錄(MBR)。接下來發生的內容有賴于Linux是怎樣在系統上安裝的。為使討論形象具體,我們假定LILO是內核的載入程序。在典型的設置中,BIOS檢測MBR中的關鍵數字(為了確認該數據段的確是MBR),并在MBR中檢測引導扇區的位置。這一扇區包含了LILO的開始部分,然后BIOS將其裝入內存,開始執行。注意我們現在已經實現了從硬件和內置軟件的范圍到實際軟件范圍的轉變,從有形范圍到無形范圍,也就是說從你可以接觸的部分到不可接觸的部分。下面就是LILO的責任了。它把自己其余的部分裝載進來,在磁盤上找到配置數據,這些數據指明從什么地方可以得到內核,啟動時要通過什么選項。LILO接著裝載內核到內存并跳轉到內核。通常,內核以壓縮形式存儲,只有少量足以完成解壓縮任務的指令,也就是自解壓可執行文件,是以非壓縮形式存儲的。因此,內核的下一步工作是自解壓縮內核鏡像。到這里,內核就已經完成了裝載的過程。下面是對所進行步驟的簡要描述:1) CPU初始化自身,接著在固定位置執行一條指令。2) 這條指令跳轉到BIOS中。3) BIOS找到啟動設備并獲取MBR,該MBR指向LILO。4) BIOS裝載并把控制權轉交給LILO。5) LILO裝載壓縮內核。6) 壓縮內核自解壓,并把控制權轉交給解壓的內核。正如你所見到的,引導過程每一步都將你帶入更大量更復雜的代碼塊中,一直到最后成功地運行了內核為止。依賴于你計算層次的方式,CPU成為內核引導程序的引導程序的引導程序的引導程序(CPU裝載BIOS,BIOS裝載LILO,LILO裝載壓縮內核,壓縮內核裝載解壓內核;但是你可以考慮是否這些步驟都滿足引導程序的定義)。4.2 初始化Linux內核在內核成功裝入內存(如果需要就解壓縮)以及一些關鍵硬件,例如已經在低層設置過的內存管理器(MMU,請參見第8章)之后,內核將跳轉到start_kernel(19802行)。這個函數完成其余的系統初始化工作—實際上,幾乎所有的初始化工作都是由這個函數實現的。因此,start_kernel就是本節的核心。 start_kernel19802:__init標示符在gcc編譯器中指定將該函數置于內核的特定區域。在內核完成自身初始化之后,就試圖釋放這個特定區域。實際上,內核中存在兩個這樣的區域,.text.init和.data.init—第一個是代碼初始化使用的,另外一個是數據初始化使用的(可以在進程間共享的代碼和字符串常量之類的“文本(Text)”是在可執行程序中的“純區域”中使用的一個術語)。另外你也可以看到__initfunc和__initdata標志,前者和__init類似,標志初始化專用代碼,后者則標志初始化專用數據。19807:如前所述,即使在多處理器系統中,在啟動時也只使用一個CPU。Intel稱之為引導程序處理器(bootstrap processor,簡稱為BSP),它在內核代碼的某些地方有時也稱之為BP。BSP首次運行這一行時,跳過后面的if語句,并減小boot_cpu標志,從而當其他CPU運行到此處時,都要運行if語句。等到其他CPU被激活執行到這里時,BSP已經在idle循環中了(本章稍后會更詳細地討論這個問題),initialize_secondary(4355行)負責把其他CPU加入到BSP中。這樣,其他CPU就不用執行start_kernel的剩余部分了—這也是一件好事,因為這意味著不用再對許多硬件進行冗余初始化等工作了。順便說一下,這種奇異的小小的改動只有對于x86是必需的;對于其他平臺,調用smp_init完全可以處理SMP設置的其他部分。因此,其他平臺的initialize_secondary的定義都是空的。19816:打印內核標題信息(20099行),這里顯示了有關內核如何編譯的信息,包括在什么機器上編譯,什么時間編譯,使用什么版本的編譯器,等等。如果中間任何一步發生了錯誤,在尋找機器不能啟動的原因時查明內核的來源是一個有用的線索。19817:初始化內核自身的部分組件—內存、硬件中斷、調度程序,等等。尤其是setup_arch函數(19765行)完成體系結構相關的設置,此后在command_line(傳遞到內核的參數,在下面討論)、memory_start和memory_end(內核可用物理地址范圍)中返回結果。下面這些函數都希望駐留在內存低端,它們使用memory_start和memory_end來傳遞該信息。在函數獲得所希望的值后,返回值指明了新的memory_start的值。19823:分析傳給內核的各種選項。parse_options函數(19707行,在隨后的“分析內核選項”一節中討論)也設置了argv和envp的初值。19833:內核運行過程中也可以自行對所進行的工作進行記錄,周期性地對所執行的指令進行抽樣,并使用所獲得的結果更新表格。這在定時器中斷過程中通過調用x86_do_profile(1896行)來實現,該部分將在第6章中介紹。如圖4-1中說明的那樣,這個表格把內核劃分為幾個大小相同的范圍,并簡單跟蹤在一次中斷的時間內每個范圍中運行多少條指令。這種記錄當然是非常粗糙的—甚至不是依據函數和行號進行劃分的,而只是使用近似的地址—但是這樣代價很低,且快速、短小,而且有助于專家判斷最關鍵的問題。每個表格條目所涉及到地址的多少—還有問題發生地點的不確定性—可以通過簡單修改prof_shift(26142行)來調節。profile_setup(19076行,在本章中后面討論)可以讓你在啟動的時候設置prof_shift的值,這樣比為修改這個數字而重新編譯內核要清晰方便得多。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -