天天看點

體會Linux與生俱來的美 初級篇  走進Linux 作業系統

摘要:本期涉獵了作業系統的來龍去脈後與大家攜手步入Linux世界。我們力圖展示給大家一幅Linux系統的全景圖,并為了加深對linux系統的全面認知,親手搭建了一個能運作在記憶體中的試驗系統。同時為大家提供了幾個shell腳本幫助建立試驗系統。​

最熟悉的陌生人

用蕭亞軒的一首歌形容作業系統給我們的感覺再合适不過了,“最熟悉的陌生人”。

說熟悉,因為幾乎每天我們都在有意無意和它打着交道。無論是日常辦公,還是暢遊網際,我們都無法離開作業系統的幫助。電腦初啟,首當其沖運作的就是作業系統。對于許多人而言,電腦就是那扇“窗”。即便遠離電腦,我們依然無法逃脫作業系統的“魔掌”。在您收發短信、撥打電話的時候,手機螢幕背後一個嵌入作業系統正在默默為您服務。越來越多的數字裝置都開始擁有自己作業系統,雖然它們不同于Bill為我們帶來的“窗子”,但并無什麼本質的差别。假如有一天,您的電視機開始對您噓寒問暖,請别忘記,有一個叫做作業系統的東西站在它的背後。

說陌生,因為直接為我們服務的大多屬于應用軟體,也許是文字處理、也許電子郵件、也許網絡遊戲。他們都運作于作業系統之上,提供我們所需的服務,這讓我們很少直接面對作業系統。對大多數使用者而言,作業系統的細節是不可見的,是以雖說我們無時無刻不在使用作業系統,彼此之間卻多了一層隔閡。對于作業系統的認識,往往隻能停留在間接地、片面地感性基礎之上,其内部的運作機制,我們無法一目了然。

無處不在卻不以正面相示,讓作業系統有了一種“猶抱琵琶”的美,于是作業系統被冠以“最神秘”的稱号。

揭開這層神秘面紗,是我們雜志的初衷。我們希望能夠诠釋作業系統設計思想,和您一起熟悉作業系統提供的各種服務,學習靈活運用這些服務的方法和技巧。我們将以理論讨論結合執行個體實驗方式,讓您和我們一起擁有作業系統級開發軟體的本領。

好了,開始我們的核心之旅,走近這個“最熟悉的陌生人”吧!

本期目标!

在第一期中,我們将:

提綱挈領地介紹作業系統的基本概念;

理清楚作業系統和其它系統軟體或應用軟體之間的關系;

了解作業系統的體系結構;

在此基礎上,提出作業系統核心概念。

簡而言之,希望讀者可以在讀過本期的内容,能在腦海裡勾勒出一幅作業系統的全景圖,為日後剖析核心、開發核心創造基礎。

學習的最佳方法是實踐。

我們的雜志将以實踐為主,理論為輔。将實驗貫穿到學習、分析當中。

在本期中,為了讓讀者能比較全面地了解作業系統,并加強對作業系統的感性認識,我們将本期分為兩大部分:

第一部分從理論上簡要介紹作業系統的内涵,揭示作業系統在軟體體系中的特殊地位,然後讨論作業系統的體系結構和演化程序。

第二部重點分析Linux作業系統,介紹Linux作業系統的發源,特色以及核心結構。最後通過和讀者一同親手搭建一個實驗作業系統——一個經過裁減具備基本功能的小型Linux,讓讀者從實踐中感受作業系統的體系結構和在計算機系統中所起的作用。

什麼是作業系統

作業系統的概念

雖然定義總是生硬、乏味、令人費解,但是它畢竟是概括性最強、最能展現水準的,是以我們還是要在開始就給出作業系統定義:

作業系統是應用程式的運作環境。​

夠精辟吧!

可能運作環境這個術語令你如墜雲端,它太廣泛、太抽象了。你一定在問運作環境到底是什麼?簡單地講,運作環境是一種即服務和控制于一體地容器。

如果你沒有了解環境這個概念,我可以舉個并不貼切的社會執行個體。

在開發區中可以看到許多企業孵化池或産業園,其中入住了各種各樣的企業,孵化池或産業園的管理機構會統一為其中的企業辦理各種工商、保險、衛生醫療等手續、提供後勤、治安等基礎服務,企業需要某些政務服務時,可以通過管理機構去和政府聯系,處理相關事宜,而不需要親自去處理這些和企業業務無關的政務活動,是以可以抽出身來集中精力在業務上。孵化池和産業園為企業提供了統一、普遍地服務和管理,是企業運作的外在環境。作業系統從這個意義上來說,類似于産業園的管理結構,為應用程式——好比企業——提供基礎服務和管理。

當然,我們現在并不指望你立刻認識到作業系統深刻内涵,對它的認識需要在不斷地使用和思考中消化和積累,在本期雜志中,我們将從各種角度介紹“運作環境”這個術語的真實含義,并在今後各期雜志中分階段、分層次的展開學習作業系統的各個領域。相信在不久的将來,你就能和我們一起認清它的廬山真面目了。

作業系統産生背景

想要認清一個人,最好是從小認識他。對技術的了解也是如此,我們必須了解它的産生原因和發展過程,才能較為全面的認識該技術所解決的問題,認識它在學科發展中所處的地位。對比技術發展各個階段的特點,才能認清該技術的優劣。同樣,我們想要把握作業系統的特性,還是先把作業系統放在時間軸上看看它的來龍去脈吧!

作業系統并不是計算機出現之初就有的,最初的計算機科學中并不存在作業系統這個概念,所有任務都直接運作于硬體之上。那時的任務大多集中于科學計算領域,系統硬體實作相對簡單、直接,任務對I/O操作的要求也比較低,将結果記錄到錄音帶機之類的簡單裝置中足矣。老程式員們大多采用手工或是用打孔機的方式将将二進制資料和程式輸入機器記憶體,然後執行計算,最後,将結果儲存到錄音帶機上。一旦出現錯誤,機器上的調試燈會将儲存在寄存器中的錯誤代碼反映出來,程式員們會據此去分析錯誤所在。

随着科學計算任務變得越來越複雜,計算機逐漸被應用到了科學計算以外的其它領域。硬體裝置比以前更加豐富和複雜了,I/O操作要求大幅提高,程式規模迅速擴大,需要調試的錯誤更是直線上升,直接操作硬體,對程式員來說變得越發困難。

于是出現了進階語言、編譯系統,幫助程式員簡化開發工作;出現了作業系統,幫助程式員管理和操作硬體裝置。程式員們可以将精力集中于開發需要的任務,煩瑣的如任務裝載、配置設定/釋放記憶體、記憶體尋址、裝置驅動、資料存儲等等硬體相關操作統統交給作業系統管理——真可以說是生産力的一次解放。

時代繼續發展,多使用者多任務時代的來臨,使得系統管理更加強調資源共享性。使用者直接作業系統資源顯然有悖于上述精神,是以資源合理配置設定與保護更為作業系統發展提供了新的挑戰和機遇,同時奠定了作業系統不可或缺的地位,從此,作業系統成為為軟體體系中最基礎,最重要的組成部分了。

作業系統的任務

從作業系統的起源可以看出,作業系統的核心任務是作為硬體和應用程式之間的一個中間層,或者說是應用程式的一個操作平台,通過它應用程式和系統硬體隔離開,應用程式利用它提供的服務完成硬體相關操作。

總而言之,作業系統友善了應用程式運作,保護了系統資源。具體地講,作業系統為使用者帶來了幾個方面的好處:

易操作性: 作業系統是使用者和計算機之間的接口,它大大簡化了使用者執行任務的複雜程度。

作為應用程式的執行環境:它為程式員建立應用程式提供了必要的編輯環境、編譯環境和調試工具;為程式的執行提供了載入服務和資源配置設定服務;為資料存取提供了I/O通路服務;為資料格式轉化和定位提供了檔案操作服務;為程式的安全運作提供了權限控制服務;為程式運作失敗提供了錯誤報告服務等等系統服務,從此,程式員和使用者都不再需要關心那些令人生畏的計算機體系結構細節,可以全心全意地開發應用程式了。

  有效性:從另一個角度看待作業系統,可以将它認為是一個計算機資源管理系統。

由于系統中資源種類各異,用法也大不相同,如果直接由使用者管理這些資源,比如記憶體配置設定,時鐘計時,I/O驅動,存儲維護,勢必要求使用者具有豐富的軟硬體知識,深刻把握計算機系統結構,否則資源将難以合理使用,最終造成系統混亂,甚至崩潰。而且現代的多使用者作業系統更是要求系統資源共享,資源必須合理配置設定給多使用者、多任務,隻有采用一定的排程政策和配置設定政策,才能保證資源被公平有效的利用。是以,配置資源成了提高性能的關鍵——如同資源配置是提高生産力的關鍵一樣。

  安全性:安全性是作業系統為我們提供的另一個重要的特點,它為我們提供了多層面的安全保障。

首先,作業系統作為系統硬體和使用者的中間平台,禁止應用程式直接操作硬體,禁止應用程式直接通路記憶體,執行特權指令。多數系統都将應用程式運作限制在使用者空間(低特權級),而作業系統則運作于核心空間(高特權級),應用程式隻有通過系統調用請求作業系統所提供的接口,才能通過作業系統間接執行和硬體相關的操作或是執行特權指令。是以保護了系統不被惡意的應用程式破壞或非法操作。

其次,多任務多使用者作業系統必須保證,不同任務之間資訊不能洩漏,是以需要為任務劃分各自的私有空間和對其進行通路控制。對不同使用者進行相應的授權和認證,可以保護使用者各行其是,互不侵犯。

總之,作業系統安全涉及方方面面,健壯的作業系統必須能多方位地保證任務安全執行。

易擴充:計算機技術的高速發展和計算機日益普及,計算機硬體裝置不斷推陳出新,這要求作業系統提供的服務也能夠日新月異,是以要求作業系統具有良好的擴充性。

由于作業系統對系統資源和服務進行了抽象,屏蔽了底層細節,統一了上層接口,添加裝置或服務成了一件輕而易舉的事,需要做的僅僅是,在裝置或服務規定的接口下完成新的實作即刻。

什麼是資源?

資源概念在作業系統中使用得相當廣泛,記憶體、磁盤、檔案、處理器、時鐘等等軟硬體都可以劃歸到資源範疇。資源的概念其實很好了解,概括來講,系統中的資源指的是系統提供給程序使用的特殊實體,程序通過向作業系統請求獲得這些實體,另外,系統配置設定這些實體給程序前,程序需要挂起等待。凡是滿足上述條件的實體就屬于資源。

作業系統的演化

    和其它任何事務一樣,作業系統并非一成不變。迄今為止,它已經經曆了半個多世紀的發展,已經形成了一個龐大的家族。從個人計算機到工作站,從通用系統到專用系統,從嵌入式到虛拟機,可謂形式豐富多樣。我們難以将所有作業系統囊括,隻希望提綱挈領地介紹在作業系統發展程序中具有代表性的幾種系統,理清它的演化脈絡。

進化曆程 :

 最早的作業系統是簡單的單道批處理系統。它的功能相當簡陋,隻能串行執行預先組織好的任務組。早先的系統一次隻能運作一個任務,每個任務必須先裝入,再等執行完後才能裝入下一個任務,重複的裝入浪費了大量的時間。單道批處理系統的出現,大大的提高了系統吞吐率。

 事情并非總如想象般順利。

 由于資料存儲時所消耗的時間——I/O操作時間——相比資料處理時間——CPU操作時間——要高出數倍(往往在20倍以上),是以程式運作到I/O操作期間,CPU總是需要停下來(挂起)等待資料傳輸完成,無形中浪費了大量寶貴的時間,任務組中後續程式的執行也是以被延遲了。如何避免資料傳輸等待帶來的時間浪費呢?能否在進行傳輸期間,解放CPU去執行别的任務呢?

為解決這個瓶頸,單道批處理系統進化到了多道批處理系統。

所謂多道就實際就是說,處理器(當然現在談到的都屬于單處理器系統)可以交錯運作多個程式,某個任務挂起時,運作另一個程式。這樣一來, CPU等待資料傳輸造成的時間浪費問題得以解決,系統吞吐率又一次得到了提高。

計算機的發展使得任務不再僅僅局限于科學計算,越來越多的應用于辦公、生活等日常活動中。科學計算中的任務多數執行路徑都是固定不變,預先定義好的,隻需要給定輸入,得到結果期間程式執行中途不需要外界幹預,與之不同,辦公,生活中的許多任務都必須和使用者不斷互動,任務結果随時都會因為使用者的選擇改變。這時的系統變得更公開、更普遍,往往允許多個使用者可以同時使用。互動模式和共用模式需要任務響應時間盡可能的快(超過20秒的話,人的思維就容易被打斷或變得很不耐煩),這樣才能讓多個使用者都滿意,于是作業系統開始采用分時技術,處理器的運作時間分成數片,均分或依照一定權重派發給系統中的使用者使用。這種将處理器虛拟給多使用者共同使用的方法,不但可以滿足快速響應,而且也可以使得所有使用者獲得計算機完全是在為自己服務的假象。

上面給出了作業系統發展的主流路線:單道批處理——多道批處理——分時系統,除此以外現在還出現了許多分布式作業系統,嵌入系統,不過總體技術思路都仍然脫離不了多道、分時等概念。

作業系統内容

作業系統的演化使得其功能變得愈來愈強大,但結構也越來越複雜。在以友善使用者(包括開發人員和終端使用者)為宗旨的思想下,作業系統不斷內建新功能,新服務。回憶從前大家使用的DOS系統僅僅隻需要一張軟碟,而如今的windows系統或Linux系統動辄就需要數張CD光牒,可見已經從過去的麻雀變成了恐龍——雖然它們都有五髒六腑。

雖然變成了恐龍,但是其結構還時相對穩定,清晰的。和軟體工程提出的思想一緻,作業系統也采取了分層結構,越向上層抽象都越高,越接近使用者;相反越向下層,越靠近硬體,抽象也相對接近硬體。而且高層軟體依靠下層軟體提供的服務,再加上本身提供附加服務為更高層服務。總體來講呈現倒金子塔形式。

下面我們就簡要分析一下作業系統的體系結構,然後再談談作業系統設計時需要主要考慮的問題。

作業系統組成

在形形色色的作業系統之中,組成結構不盡相同。因為同樣目的實作的手段可以自由選擇,是以其組成也有很大差異,我們選取最普遍的作業系統(UNIX)組成結構,向大家揭示作業系統的體系結構的大緻架構。對于各種作業系統之間的具體差異,大家可以以下面講述的結構對比認識。(注意我們這裡所說的作業系統屬于宏觀概念,接近于作業系統發行版,不但包括了核心,還包含了學多系統軟體和基礎應用軟體。)

我們用一組簡單的數學公式來描述作業系統的組成要素:

作業系統 = 核心+系統程式

系統程式 = 編譯環境+ API + AUI

編譯環境=編譯程式+連接配接程式+裝載程式

API      = 系統調用+語言庫函數(C,C++,Java,etc)

AUI     =  shell + 系統服務例程(如x伺服器等)+應用程式(浏覽器,字處理,編輯器)

作業系統最底層的元件是核心,其上層搭建了許多系統軟體。系統程式包括三個部分。這三個部分分别是:編譯環境、應用程式接口和使用者接口。編譯環境包含彙編,C 等低進階語言編譯程式,連接配接程式和裝載程式,這些程式負責将文本格式的程式語言轉變為機器能識别和裝載的機器代碼;應用程式接口(API)包含核心提供的系統調用接口和語言庫,系統調用是為了能讓應用程式使用核心服務,語言庫函數則是為了友善應用程式開發,是以将一些常用的基礎功能預先編譯以供使用,比如對C語言來說常用的C庫有gun C等;使用者接口(AUI)包括我們熟悉的shell(關于shell 應該專門寫一個教程)、系統服務程式和常用的應用程式。

這些部分并非所有的作業系統都必須一個不少的包含,不過其中大多數功能都應該提供,尤其核心,系統調用,shell這些基本元件,它們都屬于作業系統必備元件,其它元件是否包含需要根據具體系統的要求和應用環境決定,你也可以将其歸為作業系統之外的附加部分。

系統硬體
                   核心
系統調用
應用程式
shell
編譯環境
函數庫
系統服務程式

系統程式:系統程式是相對應用程式而言的,應用程式針對終端使用者需求完成功能,而系統程式則是為了簡化應用程式的開發而存在的,比如資料庫系統為了應用程式提供了有效的資料傳輸,存儲服務;還有程式設計語言的執行環境——它由C庫實作——也屬于一種系統程式,它為應用程式開發提供了諸如I/O操作例程,圖形庫,計算庫等等基礎服務。可見系統程式範圍覆寫很廣,隻要面相服務群體不是最終使用者的軟體都可以劃歸到系統軟體中來。

核心概念

作業系統最核心,最基礎的元件就要屬核心了——核心和作業系統的其它系統軟體或應用程式本質的差別在于核心運作在高特權級,和硬體直接互動,操作權限幾乎不收任何限制,是以核心程式編寫也要求格外謹慎,必須保證效率和可靠。

特權級别:現代體系結構中往往為了保護作業系統(核心)專用的資料不被應用程式通路,以免關鍵資料洩露或系統被破壞,将系統(硬體機制)劃分為不同的特權級别,敏感資料存在高特權級,且還規定了一些特權指令,其它級别的任務不能通路敏感資料和使用這些特權指令,隻有處于特權級别的任務才有權使用。比如Ox86體系結構中存在4個特權級别(0,1,2,3)Linux作業系統将核心存在0級,其它任務運作在3級。0級被稱為核心空間,3級被稱為使用者空間。

核心設計的主要任務

核心作為作業系統的核心,運作級别最高。其它系統程式都必須通過它才可以使用系統資源,擷取系統服務。是以核心使用最為頻繁,一切系統行為無論巨細都要通過核心參與。

是以核心運作效率和正确性對整個系統的運作效率和可靠性至觀重要,如果核心效率稍微下降,那麼在應用程式中就必然造成層層放大。

    核心要求高效率,是以它必須自系統運作起就要載入記憶體,并且在運作期間一直駐留在記憶體中,直到系統關閉。這是核心與其它應用程式或系統程式的另一個顯著差別。雖然說記憶體今天已經不再是天價了,但是畢竟記憶體容量有限,是以核心大小不能過大(Linux核心隻有幾M或十幾M,甚至可以裁減得更小),是以核心隻應該包含最基礎和核心的功能,其它附加功能應該盡量提到使用者空間完成。

    那麼到底有那些功能是作業系統使用最頻繁,最需要在核心中實作的呢?核心直接架構于硬體資源之上,是以首先要做的就是對硬體的資源管理。是以核心必須負責:記憶體管理,程序和程序排程(對CPU的管理),檔案系統管理,I/O處理等任務。

我們的雜志核心就是在解釋核心原理的基礎上,帶領大家學習核心級别的開發,也就是說進行核心開發。本期僅僅給大家一個概念上的說明,描述核心設計需要完成的主要任務,至于具體核心各部分的詳細讨論在後續期刊中将逐漸展開。

核心至少需要包含如下幾個子產品。

程序管理:程序是作業系統中的執行代碼,是任務在系統内的動态化身。核心必須負責将任務抽象為程序,而且必須能将程序執行,能為程序配置設定資源,維護程序的執行狀态,提供程序間通訊方法。更進一步講,程序管理還必須保證程序運作的可靠性,是以需要提供程序同步,互斥,防死鎖等等服務,另外程序排程也是程序管理中的重要任務。

記憶體管理:計算機存儲部件由快到慢、由小到大分為緩存、記憶體和磁盤。其中最主要和必須是記憶體,記憶體管理包括記憶體的配置設定和釋放,以及通路保護等。另外對使用虛拟記憶體的系統,記憶體管理還包含虛拟記憶體管理,磁盤交換管理,記憶體影射等等。

檔案系統:檔案是多數系統中使用者使用和管理資料的主要方式,檔案系統需要負責使用者檔案通路,通路權限控制,檔案格式轉換,資料傳輸等一系列問題。

裝置管理:除了儲存設備外,系統還有大量外設需要作業系統管理,比如時鐘,網卡,鍵盤,磁盤等等,裝置管理需要負責驅動這些裝置為上層調用服務。

I/O管理:作業系統中I/O管理負責處理複雜的I/O操作,其中包括I/O緩沖和磁盤排程等。​

另外中斷管理也是作業系統核心應該實作的功能。

以上是作業系統核心設計要考慮的主要問題,其中各種子產品彼此互相交錯、互相利用。不過這些子產品的劃分并不是絕對的,在實際系統中可能有不同的組合或更細緻地劃分,是以我們不必追究子產品的具體内容,需要關注的是核心究竟需要完成那些功能。

核心子產品大緻也有層次之分,我們可以這樣了解層次含義:直接和硬體作用的是硬體抽象層,和使用者更靠近的屬于邏輯抽象層。

所謂硬體抽象層,是指管理硬體裝置的子產品,比如存儲管理、裝置管理這些子產品将硬體功能抽象為核心資料結構和接口函數,以供上層使用。比如磁盤裝置驅動,需要将磁盤裝置功能抽象為打開open,寫入(write),讀取(read)等接口函數;記憶體管理需要将記憶體抽象為頁、段等結構體。然後配置設定、合并、釋放等工作都是通過操作這些抽象得來的結構體,再由這些結構影射到記憶體的實體實體上去的完成實際操作的。

所謂邏輯抽象層最主要的目的是為了貼近使用者需求,最重要的邏輯抽象子產品就是檔案系統,檔案系統的存在完全是從使用者角度出發設計的,因為使用者最能接受以用檔案形式包裝的資訊,是以檔案系統屬于邏輯上的抽象,因為實體裝置中沒有對應檔案的實體。

對于程序管理來說,其中程序執行和排程要和處理器打交道,應該說屬于硬體抽象層,但其中程序狀态維護,程序通訊等更接近使用者使用,是以可以歸結到邏輯抽象層。

另外I/O管理和中斷管理些子產品,在核心中屬于為其它子產品服務的借用力量,它們主要被檔案系統或裝置管理子產品使用,但總之是面向硬體的,是以也可以将其歸為硬體邏輯層。

Linux作業系統

在衆多商業作業系統和免費作業系統中,Linux占有獨到地位,它不但功能強大,接近于工業強度,而且結構設計幽雅,具有良好的擴充性和移植性,接口定義規範,基本和Unix系統相容。更為重要的優勢在于Linux作業系統是最具影響力的開源軟體,它的産生揭開了開元運動的新紀元,對自由軟體發展起到了前所為有的推動作用。

Linux開放性,也就是它不拘一格的拿來主義精神,吸引了無數軟體愛好者熱情的投入到其開發中去,是以Linux是當今發展最快,範圍最廣的開元軟體之一。它是社群中大家最樂意讨論和參與的項目,也正是這種開源精神使Linux成為作業系統愛好者最好的良師益友,它在教育意義上的貢獻是前所未有的。從這節起我們将進入Linux世界去探索作業系統軟體的嚴謹,去感受Linux的可愛。

Linux作業系統的起源

Linux的第一個版本誕生于1991年,它的作者就是現在大名鼎鼎Linus Torvalds,這個芬蘭小夥子據說最初是在做一個作業排程系統的學校家庭作業,後來他突發靈感開始着手将系統改造為一個實用的作業系統,他在開發初期借助了當時最負盛名的教育類作業系統Minx的一些思想和成果,但他的雄心是要将自己這個系統變的比Minx更實用、更強健,是以他決定把自己的系統代碼公布于衆,并且歡迎任何支援者來修改和擴充Linux系統——這正是我們現在耳聞祥熟的GUN協定的權益——Linux選擇了當時在世界上最受推崇的Un ix系統接口标準:POSIX.1來作為自己的核心系統調用接口,從此Linux成為了Unix風格作業系統家族中的新貴,而且是一個代碼完全公開的作業系統。

Linux的生命力來自于它的開源思想,自Linus公開Linux代碼一來,世界各地的軟體工程師和愛好這不斷積極地對Linux系統今進行修改和加強,先後将其版本從0.1 提高到2.0 、2.2、2.4到如今的2.6,同時Linux也被從初期的x86平台移植到了Powerpc、Sparc、MIPS、68K等幾乎市面上能找到的所有體系結構上。更另人激動的是,拜開源運動之新風,數不勝數的應用軟體出現在Linux系統之上,這樣大大加強了Linux系統的實用能力。

Linux作為開源軟體中的桂冠,越來越受到歡迎,毫無疑問地成為人氣最旺,最活躍的gun項目,圍繞Linux的社群雨後春筍般的出現,這一切都預示着Linux将在教育領域,在工業領域在政治領域将得前所未有的成功。

Linux 作業系統的技術特點

Linux系統吸收了Unix作業系統的精華思想——“簡單就是美”,是以它采用了緊縮核心結構,隻在核心中實作那些必要的功能,盡量保持核心精悍短小。至于那些豐富多彩的附加功能統統交給使用者空間的庫函數或其它系統軟體或應用軟體完成。

有時大家将直接将Linux核心和 Linux作業系統化等号,這也沒錯的。而我們上文提到的操作作業系統多數情況不僅僅隻核心而且還隻核心之上的系統程式,可以說是廣義的作業系統概念,希望大家差別。

為了能受益于Unix系統的影響力,Linux采用了Unix的系統調用接口标準POXIS.1,保證了和Unix系統的有限相容,進而抓住了很大一部分Unix技術人員。

另外Linux起源于小型計、通用算機,并非針對大型和專用計算機設計,是以結構複雜性和規範性都比較适中。

還有就是目前多數Linux作業系統版本都是以伺服器為出發點,是以網絡功能和系管理能力突出,多數應用也是專為網絡管理服務的,對于個人使用者所關注的桌面應用和嵌入應用關注的實時性支援尚且有限(今年Linux發展的一個重要議題就是針對桌面和嵌入開發相應的核心版本)。

Linux 核心的特點

Linux是一種是實用性很強的現代作業系統,開發它的中堅力量是軟體工程師,是以多以實用性和效率為出發點,很多地方還考慮了工業規範和相容性等因素,是以不同于教學性作業系統追求理論上的最先進性,Linux系統核心最注重的問題是實用和效率。

下面我們簡要歸納一下Linux核心的特色。

第一,Linux核心被設計成單巨核心(monolithic?)結構(相對微核心而言,微核心是一種功能更貼近硬體的核心軟體,它一般僅僅包括初等記憶體管理、同步原語、程間通訊機制、I/O操作和中斷管理,這樣做有利于擴充性和移植性。但是微核心與諸如檔案管理、裝置驅動、虛拟記憶體管理、程序管理等其它上層子產品之間需要有較高的通訊開銷,是以目前多集中在理論教學領域,對工業應用效率難以保證。),是以效率高,緊湊性強。

第二, 2.6版本前Linux核心是單線程結構——所謂但線程結構是說同一時間隻有一個執行線程(核心中的執行程式)允許在核心中運作,不會被排程程式打斷運作其它任務,這種核心被成為非搶占的,它的好處在于核心中沒有并發任務(單處理器而言),是以避免了許多複雜的同步問題,但其不利影響是非搶占特性延遲了系統響應速度,新任務必須等待目前任務在核心執行退出才能獲得運作機會。工業控制領域需要高響應速度,是以2.6版本後由于Robert love等人的貢獻,将搶占技術引入了Linux核心,使得其變為核心搶占系統,當然付出的代價是同步操作進一步複雜化了。

第三,為了保證能友善地支援新裝置、新功能,又不會無限擴大核心規模,Linux系統對裝置驅動或新檔案系統等采用了子產品化方式,使用者在需要時可以現場動态加載,使用完畢可以動态解除安裝。同時對核心,使用者也可以定制,選擇适合自己的功能,将不需要的部分剔除出核心。這兩種技術都保證了核心的緊湊性和擴充性。

第四,Linux核心純粹是一種被動調用服務對象,所謂被動是因為它為使用者服務的唯一方式是使用者通過系統調用來請求在核心空間運作某個函數。核心本身是一種函數和資料結構的集合,不存在運作的核心程序為使用者服務(雖然Linux的确存在一種被稱為核心線程的程序,但它并不是用來服務于使用者的,僅僅作為系統自身的服務目的)。

第五, Linux核心的采用虛拟記憶體技術使得記憶體空間虛拟擴充到了4GB之多,其中0

-3G屬于使用者空間,稱為使用者段,

3G

-4G屬于使用者空間,稱為核心段。這樣使得使用者編寫程式可以使用遠遠大于實際記憶體的存儲空間。

第六, Linux的檔案系統最大特點是實作了一種抽象檔案模型——VFS(虛拟檔案系統),該檔案系統屬于Unix風格。使用虛拟檔案系統屏蔽了各種不同檔案系統的内在差别,使得使用者可以使用同樣的方式通路各種不同格式的檔案系統,可以毫無差別地在不同媒體不同格式的檔案系統之間使用VFS提供的統一接口交換資料。這種抽象為Linux帶來了無限活力。

第七, Linux提供了一套很有效的延遲執行機制——下半部分,軟中斷,tasklet和2.6新引入的工作列隊等,這些技術保證了系統可以針對任務的輕重緩急,更細粒度的選擇執行時機。保證了系統運作時盡量在安全時間(不關中斷)。

Linux除了以上提到的特色外,還有許多其它突出特點,我們将在以後各期的介紹中有序地介紹。如果對上述特色有疑問的話,請别着急,後面的學習将為你解答。

Linux 作業系統核心結構

Linux核心雖然實作和Unix系統有很大不同,但是其結構還基本保持和Unix雷同,其中功能也和我們前面提到的作業系統核心要求大體一緻。

下面我們簡要說明一下個子產品之間的聯系。

使用者空間的任何程式如果需要使用核心提供的服務,都必須經過系統調用,是以系統調用層和核心中大多數子產品都留有接口,它們或是用來控制系統服務屬性(如sys_fnctl設定檔案操作屬性;sys_nice設定程序時間片),或是從核心提取資料(如sys_time獲得由時間中斷維護的系統計時),或請求核心配置設定資源(brk擴充程序堆記憶體)。

檔案系統包含VFS和各種實際檔案系統。VFS為實際檔案系統抽象了統一接口,而實際檔案系統提供自身具體執行個體操作方法。另外在Linux中和Unix一樣裝置被巧妙的歸屬為特殊檔案,受檔案系統抽象和管理,是以其操作方式和檔案系統一緻。檔案系統将對裝置的操作遞交給實際的裝置驅動處理。

Linux中裝置管理将裝置被區分為塊裝置——可以随機通路,如磁盤——和字元裝置——隻能順序通路,如鍵盤。字元裝置結構簡單,檔案系統可将請求直接送出給字元裝置驅動處理,但是對于塊裝置,由于頻繁的随機通路需要反複進行磁盤尋址操作,這樣會對系統載核的帶來沉重負擔,是以核心對塊裝置的請求必須加以整合,比如對請求排隊、合并、然後有選擇地派發給實體裝置;另外讀取裝置時還需要在記憶體中進行緩沖磁盤塊。是以在請求被送出給塊裝置前必須經過I/O層處理進行預處理,在磁盤塊被讀取後必須由I/O層進行塊緩沖處理。

中斷管理系統負責為裝置服務,它相比輪詢等方式節約了CPU周期,另外時鐘中斷還要負責更新系統時間,觸發程序排程。

記憶體是系統中的核心資源之一,是資料存儲和傳遞的必被條件,是以管理系統不但系統調用需要使用,而且幾乎系統中所有子產品都多多少少地需要使用記憶體管理系統的函數。檔案系統、I/O系統用來緩沖資料都需要配置設定記憶體,程序管理中的程序資料存儲,位址影射都需要記憶體,堆的增長也需要動态請求記憶體;還有就是程序通訊中的一個有效方法就是利用共享記憶體來實作的。

程序管理除了和記憶體管理和程序通訊有關外,也和檔案系統有重要關聯,因為程序資源中檔案毫無疑問屬于最重要的部分之一,是以程序管理系統也必須和檔案系統互動。

Linux 作業系統的檔案構成

核心雖說是Linux作業系統的精華所在,是其它程式賴以運作的基礎,但是如果一個實用的作業系統僅僅隻有核心,而在其上沒有豐富、強大的系統程式和應用程式供使用者使用,就好比大廈建好了,也通了電,但卻沒有電梯、電話和辦公裝置,使用者仍然無法入住使用。是以Linux作業系統的發行版除了帶有核心以外,還帶有大量的系統程式和應用程式,比如最新紅帽子系統的發行版本句需要2-3張CD光牒,其中絕大部分是應用程式。

     要想深入學習linux核心,首先需要能熟練使用Linux作業系統,了解整個系統檔案構成——正所謂,刨丁解牛,始見無非全牛者——由外至内的學習Linux,再從内向外推敲;從感性深入理性,再由理性傳回感性,才會獲得最深刻的認識。核心的衆多特點最終還是要反映到使用者應用上的,是以先熟悉應用無疑會對核心學習有很大裨益。而且學習核心結構的一個重要目的就是推動我們更有效的使用Linux作業系統,無論是從系統管理角度來說或是從程式開發角度上說,掌握核心級别的系統調用、資源配置設定、中斷控制或程序排程等技術都是不凡的價值,可以幫助你有效開發和駕馭系統。

這節我們先在這裡簡要介紹一下Linux系統的檔案構成,将Linux作業系統的外在全貌展現給大家。我們在系統啟動後,進入系統所能觀察到的就是一系列目錄(使用ls或dir)

,認識這些目錄構成是學習使用Linux系統的第一步,下面我們就羅列出主要目錄并簡要描述各自内容。

Linux系統根目錄/下包含包含:

bin:該目錄存放最常用的基本指令,比如拷貝指令cp、編輯指令vi、删除指令rm等。

boot:該目錄包含了系統啟動需要的配置檔案、核心(vmliuxz)和系統鏡像(initrd….img)等。

dev:該目錄下存放的是Linux中使用或未使用的外部裝置檔案(fd代表軟碟,hd代表硬碟等),使用這些裝置檔案可以用操作檔案的方式操作裝置。

     etc:該目錄下包含了所有系統服務和系統管理使用的配置檔案;比如系統日志服務的配置檔案syslog.conf,系統使用者密碼檔案passwd等

     home:該目錄下包含了除系統管理者外的所有使用者的主目錄,使用者主目錄一般以使用者登陸帳号命名。

     Lib:該目錄下包含了系統使用的動态連接配接庫(*.so)和核心子產品(在modules下)。

     host+found:該目錄包含了磁盤掃描檢測到的檔案碎片,如果你非法關機,那麼下次啟動時系統會進行磁盤掃描,将損壞的碎片存到該目錄下。

     mnt:該目錄下包含使用者動态挂載的檔案系統。如果要使用CD光牒,U盤都一般應該将它們安裝到該目錄下的特定位置。

     proc:該目錄屬于記憶體影射的一個虛拟目錄,其中包含了許多系統現場資料,比如程序式數,中斷情況,cpu資訊等等,它其中的資訊都是動态生成的,不在磁盤中存儲。

     root:該目錄是系統管理者(root使用者)的主目錄。

     sbin:該目錄下包含系統管理者使用的系統管理指令,比如防火牆設定指令iptable,系統停機指令halt等

tmp:該目錄下包含一些臨時檔案。

usr:該目錄下一般來說包含系統釋出時自帶的程式(但具體放什麼東西,并沒有明确的要求),其中最值得說明的有三個子目錄

  /usr/src :Linux核心源代碼就存在這個目錄

  /usr/man :Linux中指令的幫助檔案

  /usr/local : 新安裝的應用軟體一般預設在該目錄下

var:該目錄中存放着在不斷擴充着的資訊,比如日志檔案。

以上就是Linux檔案系統的原始構成,熟悉它們是應用Linux作業系統的前提,希望大家親自打開各目錄看看。

搭建Linux試驗系統執行個體

進入後續章節讨論的核心前,我們先與讀者一同從頭構架一個試驗作業系統。這樣既有助大家熟悉Linux作業系統的組成結構,也會在建構過程中學習介紹一些Linux指令和

使用技巧,加深了解Linux作業系統的運作方式。

實驗系統将在保證明用價值的基礎上,盡量小巧。希望大家通過親手建構系統的過程中,能消除對Linux的恐懼感,更希望讀者自己能使用裁減的系統,給自己帶來成就感和學習熱情。

必備的基礎知識

對于第一次接觸Linux的朋友,僅僅看下面的内容顯然不能指望學會Linux的操作方法和系統行為,建議你去找本系統一點的Linux系統教程慢慢咀嚼吧。對于像系統管理者這種大牛,跳過下面内容吧,再高的就去看看新浪體育新聞什麼的,别在這瞎轉了J。

搭建系統過程中将離不開敲擊各種各樣的指令,離不開執行大大小小的shell腳本。而最整個過程中重要的是了解系統的運作思路,一切活動的指導思想都要圍繞系統運作的步伐,要“順從“系統運作自己和系統運作服務這一指導思想。是以基礎知識也從這幾個角度展開。 不過我們蜻蜓點水,不做深究。

基本指令我們首先介紹一組搭建Linux系統需要使用的基本的指令。當登陸到Linux系統上後,出現在我們面前的是一個shell 提示符(# 或 $等),該提示符号告訴我們系統已經準備接受指令了,你可以用鍵盤輸入指令行來作業系統了,你輸入的指令将在螢幕上顯示出來,并議Enter鍵表述指令輸入結束、發送指令給系統的标志

Shell和SHELL程式設計

Shell是什麼?

在你登陸到系統後,系統首先運作的是一個特别的應用程式,它顯示一個提示符号表明系統已經準備好開始接受你的指令了,當你鍵入你要執行的指令後,該應用程式将指令送出給Linux系統去處理,然後等處理完畢再把結果傳回給你,這之後她又将回到提示狀态,去等待你下次輸入指令。這個特殊的“接待”程式就被稱為shell,其作用相當是一個核心與使用者交流的界面,她周而複始地向核心解釋使用者指令,是以Shell又被稱稱為指令解釋器。

SHELL作為一種應用程式并非隻有唯一一種,目前流行的shell有sh / bash /ksh /tcsh/csh等等,他們其實也就始一個應用程式,你可以使用指令whereis ksh/sh/bash來檢視其存在于系統中的具體位置。

有興趣得話,你可以通過指令 echo $SHELL來觀察系統預設的SHELL屬于那一種。你也可以在登陸後(使用Ctrl+D可以重新登陸)使用chsh來改變選擇使用的shell程式,或幹脆直接在預設shell上執行新的shell程式——隻要鍵入新shell名字并回車即可,如果想推出新shell,就再執行exit程式。

各種shell程式各有特點,功能也有強又弱,但是相同點都需要能夠執行程式或指令;能夠處理程式或指令的輸入輸出;能夠執行shell腳本。(shell 能執行三種不同概念的檔案:1指令指shell程式自己内置的基本指令——如 cd 指令,管道 | 指令 >重定向指令——和以二進制檔案形式存在的系統指令——如ls cp等。2  程式指使用者安裝和編譯身成的二進制檔案;3腳本指包含邏輯關系的程式和指令序列)

shell執行檔案需要必要的環境,這些環境包含檔案搜尋路徑,目前目錄,使用者主目錄,預設編輯器等等(你可以從man shell種獲得這些資訊)。這些資訊屬于環境變量,可以通過env觀察目前系統預設的環境變量,改變這些變量可以通過:變量=設定(如 PATH= /opt)指令方式和修改存在于使用者目錄下的相關配置檔案(如對bash來說配置檔案愛你為~/.bashrc,~/.bash_profile)

shell程式設計

shell程式設計簡單地講就始講指令序列化後執行,而不用被編譯成二進制可執行檔案。這類似于dos下地BAT批處理檔案。使用shell程式的意義在于,有些任務無法通過現有的指令完成,必須使用一組指令協作才能完成,而且各種指令之間不是簡單的羅列而是按照設定的邏輯關系有機結合。由此可見shell程式需要能夠控制各種指令的執行流,能夠讀寫臨時資料,是以,shell程式存在自己控制語句和變量,而且對其使用也由相關文法。

Shell程式,也可以成為shell腳本,以普通的Linux文本檔案形式存在。可以是用vi等文本編輯器生成,再将其屬性改為可執行即可運作。

比如 touch test 生成檔案test

     chmod u+x test 修改屬性

     ./test

保險其間可以再腳本頭先使用#!符号來強制目前shell運作其後的制定shell檔案來執行該腳本。

當然shell程式設計覺非上面說的那樣簡單,想要真正學習shell程式設計并能使用它可不那麼容易。有興趣的朋友可以參看有關資料了解shell程式設計。

系統服務安裝過Linux的朋友一定熟悉安裝過程種系統會提示你選擇何種服務,或安裝完畢使用setup指令也可看到一個配置界面其中包含系統服務配置。系統服務包含一系列形形色色的服務,很多服務選項我們聞所未聞,或者僅僅聽說過罷了。着很正常,因為服務太多太雜了,很少有人能全部搞清楚這些服務是幹什麼的。我們這裡也不追究所有服務的詳細作用,僅僅從系統運作角度介紹一下這些服務的使用方法。(想知道系統到底有那些服務,試試setup指令吧。)

系統服務程式和普通應用程式或系統指令本質是相同的,都是一些二進制檔案。但其運作方式卻有一些自身特點。系統服務多數情況下都處于背景運作,是以運作結果一般不再螢幕顯示(往往被重新定向到/dev/null中),但是為了安全目的或分析目的,大多記錄都要求儲存到相關日志中;另外系統服務程式運作時多需要進行一定配置,比如ftp伺服器有使用者通路權限配置,工作目錄配置,是以需要從配置檔案取資料初始化服務程式。最後就時服務程式很多時随系統啟動就開始運作,而不需要使用者自己啟動。

由于這些特點系統服務程式的啟動或停止一般都存在相應的shell腳本檔案管理,利用這些腳本可以控制服務程式的配置,啟動,日志記錄以及關閉服務和清理臨時檔案操作。這樣相比使用者手動操作要友善安全得多。

Linux系統中的服務程式運作腳本(啟動或關閉)都存放在目錄/etc/rc.d/init.d下——Linux系統的檔案組織層次遵循FHS規範,包括腳本位置——比如我們啟動/停止網絡所用的network 等腳本。這些腳本都具有相同的使用方法運作:服務腳本 {start|stop|restart|reload|status}。如果你需要手動啟動或停止某項服務,鍵入/etc/rc.d/init.d/服務腳本名 start|stop 即可,除此方法外也可以利用指令 service服務腳本名 start|stop,它們執行作用相同。

系統服務程式多數情況下随系統啟動開始運作,系統關閉停止運作,這也正是你開機或關機時為什麼能在螢幕上看到一系列的服務啟動[ok] 或服務停止[stop]的原因。那麼系統如何啟動和關閉這些服務呢?

談到這裡很有必要說一下Linux系統運作級别這個問題。所謂運作級别更通俗的講就是指定系統的行為,每種運作級别都對應一組該級别應用程式。

運作級 描述
系統停止
1 單使用者系統,不需要登陸
2 多使用者系統但不支援NFS,指令行模式登陸
3 完整多使用者模式,指令行模式登陸
4 未用
5 X11圖形模式,圖形模式登陸
6 重新啟動系統

我們可以使用指令init(後問會說明它) 級别來切換系統的運作級别。一般伺服器系統使用級别3,如果需要圖形界面使用5,對于單使用者或嵌入系統使用運作級1即可。

其中級别0和6可以使用來安全停止系統,它們會将除根目錄以外的檔案系統解除安裝,并且以隻讀方式重新安裝根檔案系統,這樣一來防止了破壞檔案系統。

言歸正傳,回到系統服務程式。我們應該能猜到不同的運作級别也對應了不同的系統服務集合。比如運作級别5至少就需要比級别3多啟動x伺服器和xfs(字型伺服器)等。你可以利用指令chkconfig –list來觀察每個運作級别下的各種系統服務是否允許。顯然級别5開啟的服務最多,下來是級别3 。總之,功能越強要求服務越多。

 下面的啟動部分回告訴大家,系統根檔案安裝後,首先尋找init程式并運作它,該程式的任務就是從配置檔案确定系統的運作級,并且根據級别啟動相應的服務程式。具體的過程如下

init程式從inittab中獲得系統運作級别X ,後會依次運作/etc/rc.d/rcX.d/中以大寫S開頭的shell腳本來啟動對應的服務。

Linux系統啟動的标準流程

對于系統裝載過程我們暫時不做介紹,我們假設核心已經被載入記憶體并且已經完成了異常表、中斷表、排程程式、時鐘、控制台、記憶體等初始化,最後進行程序管理器的初始化,從此核心可以開始使用真正的程序了。

初始化完成後,核心建立第一個程序(初始程序),該程序作為系統的第0号程序,在程序描述符表中由task[0]或INIT_TASK表示。該程序進而再建立了一個程序去執行init()函數進行第二階段的初始化操作,而初始程序(INIT_TASK)本身則去執行idle循環,可見初始程序在核心初始化後唯一的作用就是去使用空閑的CPU時間。

第二階段的初始化工作要比前一階段輕松一點,因為現在是由一個真正程序完成它們的,而前一階段都是由“硬體程序”手工去做的。該階段,這個由INI_TASK建立的新程序需要初始化總線、網絡并啟動系統中的各種系統核心背景線程,然後再初始化外設、設定檔案格式,在這之後,它要為進入系統做最後的準備——初始化檔案系統,安裝根檔案,打開/dev/console裝置,重定向stdin、stdout和stderr到控制台,然後搜尋檔案系統中的init程式,并使用 execve()系統調用加載執行init程式。系統自此進入了使用者态。

init程式接着将依照initab配置檔案中的選項依次執行:

1 确定運作級别(1-6)

2 運作​

​rc.sysinit​

​​

​腳本中的的系統服務,如​

​激活交換分區,檢查磁盤,加載硬體子產品等

3 運作規定級别下的服務:/etc/rc.d/rc*.d/下的S打頭的服務,如網絡服務S*NETWORK。

4 在指定序列槽上運作getty程式,getty打開終端線,并設定模式,然後運作login程式。如果使用者帳号和密碼正确(需要通過/etc/passwd驗證),則進入使用者的工作目錄,并按照其工作目錄中的設定執行相應的shell。

到這裡使用者才可以真正實用作業系統了。

Idle程序是個奇怪的程序,它是在沒有别的任務使用CPU時是才使用CPU的,它的存在價值據說可以延長CPU壽命。

 核心背景線程是種執行在核心态的程序,它們和使用者程序一樣受排程程式排程,系統利用它們周期性(不一定固定周期)地執行一些自身管理方面的“家務事”。主要的幾種核心線程為:bdflush——清理被寫過的記憶體緩沖區;kupdate——按時将記憶體緩沖區中的資訊更新到磁盤中; Kswapd——将記憶體頁交換到磁盤;keventd——關系系統事件;Ksoftirq——執行軟體中斷。

搭建實驗系統

很抱歉搭建一個Linux作業系統到目前為止還沒有一個很标準的流程或規範,不過大體流程都大通小異,無非是首先編譯核心——将核心源代碼編譯成一個可執行的鏡像檔案,當然編譯核心時可能會帶有一些子產品也需要同期進行編譯和安裝(是否有子產品取決于你的具體選擇)。

有了編譯後的核心,接着就需要建立一個根檔案系統,在其中又需要建立必要的

目錄。至于其中使用的軟體和庫函數你可以選擇下載下傳源代碼包,然後交叉編譯,再進行安裝。或者我們偷個懶,從一個釋出的完整系統裡直接拷貝需要的軟體和庫,同時将必要的裝置檔案、配置檔案和服務腳本也拷貝過來,你這時所要做得僅僅是去修改一些相關的配置檔案就可以擁有一個自己的檔案系統了。

核心與檔案系統都有了,就可以說一切具備隻欠東風,你所需要做得隻剩下将核心和檔案系統綁定到一起,讓系統被引導載入核心,核心載入後可以找到根檔案系統,并執行其中的初始化程式。你可别以為這個收尾動作能輕松搞定,往往初學者都在這裡要栽跟頭。

怎麼能在最小的代價學習搭建系統呢?想想看可不是每個網友都能找個空硬碟或者磁盤(看看你的機器,也許連軟驅都沒)來做新系統的,為了保護原有系統,即便開一個新分區都不能鼓勵。是以最好的方法就是用記憶體模拟一個磁盤,将建立的根檔案系統放在其中,系統引導後,就登陸到記憶體模拟的磁盤上運作。這時你徹底跳出了你的實體硬碟。這種方法有時在嵌入系統中會被使用,或希望斷電後資料被抹掉的安全系統中使用。

下面我們就一同做個這樣的試驗系統,你付出的唯一代價是消耗些時間和無數次擊健。

編譯核心 

第一步要做的工作就是挑選一個合适版本的核心源代碼包,然後編譯它。不要以為編譯核心很神秘,其實它和編譯普通程式差不多,核心源代碼其實就是“一大堆”程式,編譯它就等于分别編譯個個程式然後在将它們連結成一個單一的可執行鏡像檔案。這個鏡像就是你在/boot目錄下看到的vmlinuz-*(如果你細心的話,一定能發現在該目錄下還有一個叫vmlinux的檔案。其實這兩個檔案是一回事,但前面那個是經過壓縮的)

   正如第一部分所說,Linux核心具有很強的伸縮性,在核心裡面許多功能是可選擇的,如果需要就可以被編譯到核心,不過核心會是以變的肥胖。一種可替代的方式是将某些功能編譯成子產品放在檔案系統内,等你真正需要它時,再由被載入到核心,這樣就核心就可以輕裝上陣了,啟動起來也快許多。

雖然是個試驗系統,但還是盡力讓它功能做強點吧。是以在編譯前,配置核心選項時,除了支援最基本的ext2檔案系統,PCI接口,自動裝在子產品等功能外,再将ext3,JFS,即插即用,網絡,SCSI,USB等比較常用的功能加入。再一個就是為了能實作我們的虛拟記憶體中建立根檔案系統,核心還需要支援Ramdisk 和initrd。

核心網絡裝置選項裡包含大量網卡驅動程式,你必須知道自己網卡内型才能正确選擇,一般情況都将網卡驅動編譯未子產品,在系統啟動後載入。我們試驗系統運作在vmware下,而vmware虛拟網卡驅動為pcnet32,是以這個子產品被包含進來了。

編譯步驟

先去下載下傳一個核心源代買不用我在多說了吧。如果你實在是個衣來伸手的家夥,好吧告訴你,到​​www.kernel.org​​網站上蕩一個想要版本的核心源代碼。如果是gz結尾的壓縮源檔案,就使用tar xvzf linux-

2.4.18.tar.gz解開,如果是gz2結尾的,就使用tar xvjf linux-2.4.18.tar.bz2解開。

核心版本編号可是有點講究的,簡單的說,偶數為穩定版本,奇數為開發版本,是以我們用

2.4.18版,一是因為它屬于穩定版,再一個就使我機器裡以前下載下傳過它,不想再換了J。

然後進入存放解開後的核心原代碼的目錄(标準系統預設情況下在目錄/usr/src/linux下存放該系統的核心源代碼),執行指令make menuconfig進行核心功能配置,選擇需要的功能以子產品形式編譯或直接編譯到核心。配置資訊預設情況下記錄在隐含檔案.config中,你也可以選擇将其記錄到自定義檔案中,比如可以把資訊記錄在MiniSys.config中。在以後配置核心時可以友善地導入指定的配置檔案。<make 詳細解釋***>

make menucofig提供給你一個文本圖形界面的配置菜單,其中列出了核心所能提供的全部功能,如果你在選項前選則*号,那麼該選項被編譯到核心中,如果選M則被編譯為子產品,對于你不清楚的選項可以使用?檢視其解釋。除了用make meunconfig外你有複古情節的話,可以試試使用make config,它完成同樣的功能,不過你得有足夠得耐心去忍受刷平一樣得指令行選擇<y><n> 。如果你在X環境下(桌面環境)不妨可以使用make xconfig配置,它相比前兩種方法得好處就試比較容易看清楚,對眼神不好得網友建議使用它。

儲存核心配置後,就執行

 make dep  /*確定所有的相依關系,例如 include files 都沒問題.除非你的電腦真的很慢,否則它不會花太久時間的*/

 make clean  /*清除核心編譯的所有目的檔以及其它東西.在重建一個核心之前不要忘記這個步驟*/

make bzImage 或zImage/*編譯核心——bz和z格式核心之間最大的差别是對于核心體積大小的限制。zImage核心需要放在實模式1MB的記憶體之内,其體積受到了限制。而bzImage的核心沒有1MB記憶體限制*/

記住核心編譯完了,還必須再編譯子產品, 即使您在配置核心時沒有使用任何子產品,也不要跳過此步驟,在編譯完 bzImage 後立刻編譯并安裝子產品是個好習慣。而且,如果您真的沒有子產品需要編譯,這個步驟也非常快就結束了。

make modules; /*編譯核心子產品,凡是配置核心時标記為M功能都将被編譯未子產品*/

make modules_install。/*這将導緻子產品被編譯而且被安裝到 /usr/lib/<核心版本号> 目錄下。不過如果你想改變核心鏡像或子產品的所在目錄,都可以通過修改核心源碼中的Makefile檔案來達到,比如修改INSTALL_MOD_PATH來改變子產品安裝目錄*/

等黃蜂一樣的字元風暴再螢幕上停止後,你現在擁有了新核心了。它藏在核心源碼目錄下arch/i386/boot下叫bzImage或zImage。新核心随帶的子產品被安裝到了lib下的modules目錄中。

組建檔案系統

組建根檔案系統說白了更簡單,一來格式化檔案系統的宿主裝置,二來就是拷貝需要的檔案。簡單明了吧!我們先來一同拷貝檔案吧,等考完了再談宿主裝置的問題,别忘了我們可都是完空手套百狼呀!(除了拷貝檔案外,更标準的方法是下栽各種工具包,在本地交叉編譯,在進行安裝,不過為了省事,我們采用拷貝标準系統檔案的方法來構造檔案系統,不但友善而且異曲同工。但前提是新系統體系結構——處理器——和我們原料系統一緻,如果你想在0x86系統上編譯運作在ARM機上的檔案系統,那麼最好是去下載下傳源代碼包重新交叉編譯吧)

檔案系統基本要求

Linux檔案系統的結構上文已經給出,我們這裡着手搭建一個精簡的檔案系統,它包含最基本的目錄以及檔案,配置檔案也盡量修改簡潔明了。下面列出檔案系統必須包含的内容。

       檔案系統最小需要包含/dev 、/proc、/bin 、/sbin 、etc 、/lib 、/usr 、/tmp 等目錄

       需要一組基本指令

       支援上述指令的運作庫函數,其中也包括編譯核心産成的子產品

  必須的裝置檔案

       一些必要的配置檔案

我們要做地就是按部就班地生成和拷貝以上内容,唯一地要就就是你要夠心細。

建立根檔案系統内容

我們先來建立一個将包含根檔案系統内容的新目錄“rootfs”(mkdir /rootfs),然後開始在其中生成(拷貝)根檔案系統需要地所有目錄和檔案。

    第一步當然是在rootfs目錄下建立根目錄下地必要地子目錄啦,用一行指令就可完成mkdir dev,proc,bin,etc,lib,usr,tmp,sbin 。

第二步拷貝你需要的指令。比如你需要 ls 指令,你先确定它在系統中的位置whereis ls (發現在/bin/ls目錄下),然後将該指令拷貝到你工作目錄下相同的目錄結構下 cp /bin/ls /workdir/bin/ls,但是僅僅拷貝指令檔案還不夠,還必須考被該指令所用到的動态共享庫檔案。如何發現指令用到了那些動态共享庫呢?很簡單,利用ldd /bin/ls 可以察看指令使用的共享庫,顯示在輸出右列的就是被用到的共享庫檔案(名字中有so)。

比如在我的系統上,該操作輸出為:​

libtermcap.so.2 => /lib/libtermcap.so.2 (0x

4001f000)​

libacl.so.1 => /lib/libacl.so.1 (0x40023000)​

libc.so.6 => /lib/libc.so.6 (0x40029000)​

libattr.so.1 => /lib/libattr.so.1 (0x40149000)​

/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)​

我們要做的是将指令要用到的庫檔案按照它的目錄結構拷貝到我們工作目錄下的/lib或/lib/i386。(使用者所用到的指令多集中在/bin和/sbin下,另外一些腳本會用到一些出現在/usr/bin和/usr/sbin下的檔案,如果你要使用這些腳本,不用說這些指令和它們要用的庫都不能少)。但是這些檔案并非我們實際想要的,它門隻是實際庫檔案的一個符号連結,系統隻是以要使用符号連結,是為了便于庫檔案更新換代時不影響使用它的應用程式。是以我們單單拷貝符号連結是沒有意義,同時也必須将符号連結指向的實際庫檔案一同拷貝到workdir/lib錄下面去。

/lib目錄下還有一個重要目錄就是modules目錄,它裡面包含了核心編譯産生子產品,對于不同版本的子產品存放在以版本号命名的檔案中。要們可别忘了拷貝這個目錄到我們的新系統中。

在原始時期/lib下的這些庫就足夠用了,但現在的Linux系統對安全多了許多要求,尤其是系統從安全性考慮,增加了許多驗證手段,是以往往你還必須具有和安全驗證相關的庫。這些庫不會在指令中直接使用,但卻間接地要被系統的安全架構利用到,多數都是由配置檔案中說明如何關聯,安全架構通過檢視配置檔案,選擇調用具體地驗證庫(這些配置檔案後問會提及)。安全架構方面話題,我們不多說,有興趣的可以查查 pam 和 nss等的用法。在這裡我們不管它三七二十一将在/lib/security/下和pam相關地庫和/lib下nss相關地所有libnss*庫都考到我們地/lib下的/security下和/lib下。雖然很笨,但确省事。

Linux系統将那些會被多數應用程式頻繁使用的庫函數,多數都不會以靜态的方式編譯連接配接到應用程式中,而是采取動态庫的方式,集中存儲管理。這樣如果多各程式都用到某個共享庫,那麼該庫檔案隻被調入記憶體一次,駐留在記憶體一個拷貝,是以利用共享庫大大節約了空間,縮減了執行檔案提及。當然天下沒有免費的午餐,雖然共享庫相比靜态庫靈活,但卻學要而外的路徑搜尋,而且調入時間也更耗時

連結檔案是Unix風格作業系統提供的一個特色之一,其中具體又可分為軟連結和硬連結。軟連結又稱為符号連結,其實就該檔案唯一的内容就是包含實際額外檔案系統的路徑。硬連結則是和被連結檔案共享索引節點的(索引節點概念如果還不清楚,那麼自己去找找吧)。是以符号連結怕得是實際檔案被删除和轉移,那麼符号連結仍然存在但不再有效了;相反硬連結删除源檔案直會使索引節點記數減少,不會破壞硬連結檔案的。

      第三步建立裝置檔案,這點很重要但卻不費事。由于Linux繼承了Unix将裝置抽象成特殊檔案來使用和管理,是以要想使用系統的外設,比如軟硬碟,時鐘,系統終端,甚至記憶體也可以作以為其制作相應的檔案來通路。是以我們要建立系統可能用到的所有裝置對應的裝置檔案。至于你具體需要哪些裝置檔案不能一概而論,你可以打開/dev/目錄看看保準裡面檔案多地讓你炫目。不過也别怕多數都是些廢物,就我們要建立的實驗系統來說用道的裝置檔案就屈指可數:console 控制台裝置,tty* 是由控制台管理的虛拟(我們用ctrl-[1-7]切換的就是這個裝置),sda1 SCSI接口裝置 (因為我的Linux是運作在vware虛拟機上,而vmware虛拟機使用的儲存設備是虛拟的SCSI硬碟,是以需要這個檔案。它的用法和使用标準IDE硬碟沒什麼兩樣),ram 記憶體虛拟盤裝置(以後我們的系統就運作在記憶體虛拟盤中),null 空裝置(是一個非常有用的字元裝置檔案,送入這個裝置的所有東西都被忽略。如果将任何程式的輸出結果重定向到/dev/null,則看不到任何輸出資訊),zero 零裝置 (讀取這個裝置,隻會得到空的内容,是以有時為了獲得高壓縮率,需要對某空間用全零添充往往就會用到它)

initrd 這是一個特殊的字元裝置,它被用來從使用者空間向系統核心發送切換運作級别的資訊,屬于一個虛拟字元裝置(比如你向改變運作級别的init 1-6指令,都試通過該裝置傳達到核心的),關于虛拟字元裝置作為使用者向核心發指令的利器作用你可看看這個文章。

明确了你需要那些裝置檔案,可以依次利用mknod指令建立需要的裝置檔案。建立過程中需要的參數。你可以通過ls – la /dev/裝置名指令來檢視以下裝置屬于塊裝置還是子符号裝置,察看主從裝置号獲得。如果你覺得煩,就用拷貝指令直接從标準系統地/dev/目錄下拷貝這些檔案吧,不過要配合參數-R否則,你靠過來的可使檔案的整個内容而不僅僅是裝置檔案了。如果那樣可就如同把自己往自己衣服口袋裡塞,你是永遠賽不進去的。

Linux系統将裝置分為塊裝置和字元裝置,塊裝置可以随機通路(B),字元裝置隻能按順序通路C。另外一個裝置控制器可以控制多個裝置,是以有主裝置号和從裝置号之分。主裝置号對應驅動,從裝置号用來區分具體裝置。

      第四步需要建立系統運作需要的配置檔案或腳本了。我們還是從簡出發,拷貝标準系統的有用檔案,然後針對需要進行修修改改。我們試驗系統将以多任務多使用者環境使用,是以需要登陸密碼,也有分組能力,是以需要passwd和group檔案,如果系統使用shadow功能隐藏密碼,那麼還需要檔案shadow。登陸首先執行init檔案,它可需要不少配置檔案呀。首當其沖的便是inittab檔案,該檔案規定了許多系統運作的基本功能(具體内容參見)。下一步init先執行rc.sysinit腳本來初始化系統,其中會使用到fstab配置檔案,它包含了系統啟動後挂載的檔案系統和目錄,對于我們試驗系統來說隻有兩項一個是将/dev/ram作為根檔案系統安裝到/下,另一個是将proc檔案系統安裝到/proc目錄下。Init執行完rc.sysinit後依照inittab中定義的運作級别進入對應的/etc/rc.d/執行其中S開頭的服務運作腳本。不羅嗦了,細節内容别問我了,去看man init吧。你要做的就是把/etc/下initab,rc.sysinit和rc.d目錄的所有東西拷貝到你對應系統裡。我們實驗系統運作級别為3,隻啟動網絡服務服務,是以可以把除了S*network外的S腳本都删除。(當然你也可以改變系統預設的啟動流程,讓它執行你自己的初始化腳本,這點隻需要載inittab中修改 “sysinit:XXX“中的腳本名稱)。執行了上述初始化和服務後,系統最後運作rc.local檔案,這裡你可以放一些你希望開機執行的指令,我們這裡放一句“ ok you are welcome !!!”為你進入系統前的問候。

另外要知道登陸是login往往要使用pam驗證子產品認證使用者,是以pam的配置檔案也最好拷貝到新系統。很多系統還會用到NSS(名稱服務開關,這個服務來幫助客戶機器或應用程式獲得網絡資訊,可從本地或從網絡某處取得——從DNS或NIS等。諸如getXbyY()等函數都往往會用到這種服務,使用者登陸時login很可能就要使用,這取決于你libc的版本),是以/etc/nsswitch.conf需要拷貝,至于如何使用去看man nsswitch.conf吧。

剩下你還要靠被terminfo/termcap檔案,它們對設定TERM終端環境變量有用。拷貝modules.conf,它包含了有關子產品資訊,我們實驗系統中的modules.conf中僅僅給pcnet32.o 起了個别名而已。說的我口渴,不說了有什麼疑問自己去找資料吧。

    差不多完了,對了别忘了吧/root/目錄下的那些.開頭的用于bash配置的隐藏檔案也考到新系統的如root下,這些都是bash的環境參數等東西。

結束動作。ldconfig –r workdir/rootfs(試驗檔案系統目錄) 建立庫檔案路徑緩存 ,從此指令再使用動态連接配接庫時就不必指定目錄了,因為它們的路徑都被緩存了。(ldconfig 要用動态庫配置檔案ld.so.conf,試驗系統中置空它好了)

安裝根檔案系統——核心和root檔案系統綁定

别混淆,剛剛我們做的是檔案系統應該包含的檔案。具體檔案系統現在才開始做。上面說了需要在系統未來的宿主盤上制作檔案系統——進行格式化。如果你手頭沒有實際裝置,Linux提供給你另外兩種變通方法 : ramdisk和loop裝置(回環裝置) 。利用loop裝置可以将檔案虛拟成一個檔案系統進行安裝,而ramdisk則是将記憶體模拟一個塊裝置用來存放資料。

    使用ramdisk或loop裝置相比直接使用實體磁盤操作要快一些,也相對安全,不會損壞實體裝置。是以在需要建立檔案系統的情況下,很多時候都會使用上面兩種虛拟技術建立檔案系統,然後在将檔案系統轉移到實體裝置中。

    我們采取ramdisk作為檔案系統的宿主,在上面制作檔案系統,然後拷貝我們前面建立的檔案系統内容到其上去。然後觀察ramdisk的大小後 (可不是其中檔案内容的大小,因為其中還包含檔案系統本身格式的一些資訊),将整個檔案系統統轉移到某個檔案中去(利用dd指令,由于 dd 指令允許二進制方式讀寫,是以特别适合在原始實體裝置上進行輸入/輸出,制作整個檔案系統的鏡像),該檔案被稱為檔案系統鏡像。雖然Linux對檔案字尾沒有要求,但這裡我們還是習慣以img命名它。

    具體做法大該如此:

         dd if=/dev/zero of=/dev/ram bs=1k count=20000

          mke2fs –m0 /dev/ram 20000

          mount /dev/ram /mnt/

          cp –av /rootfs/*  /mnt/ram

          運作df ,注意1k-blocks一欄中/dev/ram的數值,假定為ramsize

          umount /dev/ram

          dd if=/dev/ram of=ramlinux.img bs=1k count=ramsize 

        gzip –9v ramlinux.img

  第一步是給 /dev/ram裝置清出

20M的全零空間,然後格式化/dev/ram裝置,頁就是将格式信心寫入/dev/ram中。

 接下來,安裝/dev/ram裝置到/mnt目錄下,再把你建立的檔案系統内容全部考進來。完成了這步,你才可以說真正有了一個檔案系統(檔案系統格式資訊+檔案系統内容)

 然後解載裝置後,把裝置内容(包含檔案系統格式和内容)統統轉移到名為ramlinux.img的鏡像檔案中,

最後壓縮鏡像檔案(壓縮後名字為ramlinux.img.gz),開始使用/dev/zero清零/ram裝置地目的就是為了提高壓縮率,因為壓縮算法利用統計規律替換字元,是以統一為零會大大提高gzip的壓縮率的。

一般标準系統中ramdisk預設大小為4098位元組,你不能建立超過該大小的ram盤。但我們搭建的系統大小超過了4096位元組,是以必須擴大ramdisk的大小。最簡單的方法是在lilo啟動時給ramdisk指定大小,實作系統中大概用到

20M大空間,是以在Lilo.conf中應該加入“append = “ramdisk_size=

20000”這一行,系統啟動時就會自動更改ramdisk預設大小了。

系統引導引導過程簡述PC打開電源後,先執行ROM中BIOS中的代碼,該程式負責将啟動裝置(軟碟、硬碟、CD光牒)的第一個扇區(0扇區)第一個磁到道的資料載入記憶體。接着BIOS執行該扇區中的代碼(将核心從啟動裝置中逐漸導入到記憶體)。是以扇區中要麼直接存放作業系統核心,要麼存放啟動裝載程式,比如Lilo等,由啟動裝載程式負責找到核心,裝載核心到系統,然後執行核心。

   核心被載入記憶體後的動作上面已經初步介紹了,我們這裡要強調的是核心初始化以後緊接着就需要安裝根檔案系統,那麼根檔案系統的位置如何确定?(ramdisk size?)

       我們必須在建立過程中指定驅動裝置,利用指令

rdev filename devicename 設定或在核心原代碼目錄下的 makefile中修改相關參數,然後編譯,因為該資訊是備記錄在核心中的。

除了跟檔案裝置外還需要指出根檔案系統在宿主裝置上的位置,這還需要利用rdev 來實作。該資訊也被記錄在核心中。(rdev指令很豐富,回憶我們前面談到的改變ram盤大小的任務都可以通過rdev來修改)

可能很多朋友奇怪自己根本沒用過這個指令,這麼多年還不照樣把系統生級了無數次。的确我們不大使用該指令,因為我們有更酷的工具lilo(當然grub好像現在更流行了),在lilo.conf中的配置如root=* 這些選項其實就是告訴lilo将上述資訊寫道核心中。

确定了根檔案系統位置,将其安裝到根目錄下,然後找到其中init程式,開始執行系統初始化工作。

安裝啟動鏡像

大家多數都應該對 lilo.conf下的intrd=initrd.img.*有印象吧。你知道initrd.img是幹嘛用的嗎?

這個檔案實際上就是個檔案系統鏡像,有興趣的話你可以将它登陸到/mnt下,去看看,它毫無疑問是個微縮的檔案系統(該檔案使用gzip壓縮的,是以先要解壓才能安裝它。(

mv initrd.img initrd.img.gz;gunzip initrd.img.gz;mount –o loop initrd.img /mnt)。這個檔案裡的各目錄和我們檔案系統是完全一樣的,但是由于initrd.img是在系統啟動後在Ram盤裡運作的是以它隻包含系統啟動時需要的最小指令和庫的集合。使用這個萎縮檔案系統的目的通常是為了系統啟動是尚未安裝根檔案系統前,用來運作系統以便利用insmod指令裝入安裝根檔案系統需要的子產品——比如ext3.o,Buslogic.o等(如果根裝置是SCSI或根檔案系統是EXT3等,而核心并為将這些功能編譯進去,隻能以子產品方式載入),是以在initrd.img中的lib下會包含需要再入的子產品。系統啟動後運作intrd.img中的linuxrc腳本來執行子產品載入後将根檔案系統切換到實際檔案系統中(使用pivot_root指令)。

對于我們實驗系統來說,因為已經将SCSI和EXT3等子產品直接編譯進了核心,是以不必通過initrd.img的途徑來進行先期子產品載入。是以正常情況下initrd.img是不需要的。但是要知道我們制作的根檔案系統鏡像是放在源标準系統根檔案系統下的。是以要使得系統拍托實際實體裝置,進入ram盤工作運作,就需要利用intrd.img鏡像檔案系統在啟動期間将實體盤上根檔案系統鏡像載入ram盤中,然後進入執行。這個工作我們利用linuxrc腳本來實作,具體地講就是mount源根檔案系統,将試驗檔案系統鏡像解壓傳送到/dev/ram中,然後umount 源根檔案系統。從此系統進入我們的實驗檔案系統開始運作。

Initrd.img也是屬于檔案系統鏡像,它的制作方法和制作根檔案系統大通小異。先拷貝需要檔案,在編輯腳本(linuxrc),然後制作檔案系統鏡像。詳細過程不再羅索了。

别着急,還有關鍵一步那就是修改lilo .conf 為實驗系統配置啟動選項。

boot =實驗系統核心

label = ramlinux

initrd = 剛做的initrd鏡像

root = /dev/ram

append = “Ramdisk_size =

20000”

最後,執行lilo –r /rootfs 。

ok !

有關核心引導請見附件。

下載下傳搭建腳本和實驗系統系統

雖然搭建系統技術簡單,但是過程很繁瑣,搞不好會丢三拉四,錯誤百出。為了節約大家的體力,我們編寫了幾個小腳本幫助搭建系統。利用這幾個腳本大家可以輕松地自動建立實驗系統。

我們的制作腳本可分為下面幾個部分:

mkrootfs.sh—— 收集制作root檔案系統所需的所有材料到指定目錄。

mkinitrdfs.sh——收集制作initrd鏡像所需的所有材料到指定目錄。

setup.sh——制作root檔案系統鏡像和initrd鏡像,改寫lilo配置檔案添加ramlinux啟動選項。

連同腳本一同提供給你的還有myboot,myetc 和myroot目錄。boot裡含有編譯好的核心(注意核心是與系統硬體相關的,我的機器是奔三處理器,如果你系統和我不同,那你還是自己在本機上編譯試驗系統核心吧!不過可以使用我們提供的核心配置檔案MinSys.config來選擇核心功能,編譯完成把核心考貝到myboot下就可以了——或修改mkimage.sh腳本,在最後面的地方修改lilo.conf部分,将”boot=×××”中的XXX用你自己編譯的核心代替)、子產品和核心配置檔案MiniSys.config。etc下包含了供試驗系統使用的、已經修改好的配置檔案和服務腳本。Boot下是兩個bash的配置檔案--全部腳本和必要配置檔案打包為work.tar.gz。

此外,我們也将按上述方法裁減出來的root檔案系統(rootfs.tar.gz)和root檔案系統鏡像ramlinux.img.gz放在網上以供下載下傳,同時也把initrd的内容(initrdfs.tar.gz)和鏡像(initrd.img.gz)放在網上。

如果你要添加或删除檔案系統中的某些檔案,應該展開roofs.tar.gz,然後在rootfs裡面修改,不要把檔案系統鏡像檔案(img檔案)以loop方式安裝後進行修改,因為我們曾經用zero裝置填充過檔案系統,是以如果新添加或删除某些内容,可能會破壞裡面的一些資料對齊,系統反映給你的就可能會有類似于“bus error”等一類莫名奇妙的錯誤。

如果你按要求解開了rootfs.tar.gz和initrdfs.tar.gz,那麼執行setup.sh既可生成對應的鏡像檔案ramlinux.img.gz和initrd.img.gz,并會在lilo中添加好對應于試驗系統的選項。

Step by step——享受你的作業系統吧

從啟動菜單中選擇ramlinux開始運作吧!

等等,要輸入管理者密碼?是的,我們是從原有的系統中裁減的,是以密碼自然會繼承下來。(如果你用的是我做好的系統,那麼使用者名自然是root,密碼 threeyear)

      看見了嗎,親手打造的作業系統已經快步向你走來,有成就感嗎?享受你的傑作吧!

附件——Linux系統啟動的标準流程

系統的啟動是指從計算機加電到顯示使用者登陸提示的整個過程。我們将在這裡對整個流程以及關系到的一些内容做讨論。過程主要可以分為兩個階段:載入核心和準備運作環境,我們分别進行讨論。本部分的讨論隻基于i386硬 件架構,但大部分内容是有共通性的。

圖一 啟動過程 綜述

載入核心(将核心載入記憶體,并将控制權傳遞給它)

       計算機加電到Boot Loader開始工作,硬體含量遠大于軟體含量,是以這裡暫不提及,如果實在有關心的朋友,請先别着急,我們将在下期裡讨論它。

這一階段是 Boot Loader 的主戰場。它必須将可執行的核心映像和核心啟動所需的額外資料資訊從存儲媒體上載入記憶體,這并不是件簡單的工作,因為除了從硬碟載入,可能還會需要從網絡引導伺服器這樣的外部媒體上載入。各種紛繁蕪雜的檔案系統類型也給載入帶來了巨大的挑戰。

Boot Loader 可能還需要改變CPU的運作特權級别,然後就可以讓核心投入運作了。

除此之外, Boot Loader 還要完成一些其它功能,比如從BIOS中擷取系統資訊,或者從啟動時的指令行參數中提取資訊等。有的 Boot Loader 還要扮演引導選擇工具的角色,友善使用者選擇不同的作業系統。

Boot Loader的 職責:

判斷到底要載 入什麼,這可以要求使用者進行選擇

載入核心和它 可能需要用到的相關資料,比如initrd或者其它參數

為核心準備好 運作環境,比如,讓CPU進入特權模式

讓核心投入運 行

Boot Loader的 曆史變遷:

早期的Linux隻支援軟碟引導扇區和 Shoelace 兩種 Boot Loader。 Shoelace 是從Minix繼承下來的、對檔案系統相關的 Boot Loader。它隻支援 Minix 檔案系統。當時Linux隻使用 Minix 一種檔案系統,是以這樣做并沒什麼問題。可是, Minix 檔案系統存在不能儲存建立、修改和通路時間資訊;檔案名長度限制在14個位元組等問題。随着Linux的發展,這些與傳統Unix檔案系統大相徑庭的缺陷越來越讓人難以忍受,它已經不适合作為Linux的主要檔案系統了。

為了支援其它檔案系統的實作,Linux引入了VFS(虛拟檔案系統)。這個舉措很快就引起了熱烈的反響,一大批新的檔案系統實作出現了。其中一個 Minix 檔案系統的變體,擴充檔案系統 Xiafs (根據它的作者命名)突破了 Minix 檔案系統的檔案名長度限制,将此長度一舉提高到全部30個字元。當時檔案系統之間的競争着實激烈,很難看出誰會勝出,甚至搞不清楚會不會有一個最終的“赢家”。

       盡管不确定性很大,但是有一點卻是清楚的:不管最後哪種檔案系統會受到青睐,但是除了 Minix 作為根檔案系統,誰也不能從硬碟上啟動,因為 Shoelace 隻支援Minix檔案系統。LILO應運而生了。由于支援多種檔案系統(當時核心支援的主流檔案系統已經有 Minix ,擴充檔案系統 ext , Xiafs 。還有人在移植 BSD 的 FFS ,根本看不出來什麼時候是個盡頭)在實作和維護上難度太大,而 Boot Loader 也不應該成為人們試驗新的檔案系統的絆腳石,是以LILO采取了和檔案系統無關的設計。

這種設計經受住了時間的考驗,被證明是非常成功的。即使在今天,LILO仍舊可以從核心支援的絕大部分檔案系統的硬碟上啟動。但是,由于ext2曆經這麼長的時間一直沒有大的演變,成為了事實上的标準,是以跟檔案系統相關的Boot Loader又漸漸流行了起來。

盡管ext2已經能滿足大部分人的日常需要,但是檔案系統的設計者們還是在研制以日志機制為特征的新的檔案系統,并且已經取得了相當大的進展。考慮到目前又有可能出現多種檔案系統的實作同時并存的情況,是以對與檔案系統無關的Boot Loader的需求可能會再次變得強勁。

初始化基本的操作環境

一旦核心開始運作,它會初始化内部的資料結構,檢測硬體,并且激活相應的驅動程式,為應用軟體的準備運作環境。其間包含一個重要操作——應用軟體的運作環境必須要有一個檔案系統,是以核心必須首先裝載root檔案系統。由于我們的目的是介紹基本流程,是以相關的硬體初始化細節就不再讨論,相關内容在下一期雜志中會有詳細介紹。

硬體初始化完成後,核心着手建立第一個程序——初始程序。說是建立,其實也不盡然,該程序其實是整個硬體上電初始化過程的延續,隻不過執行到這裡,程序的邏輯已經完備,是以我們就按照程序的建立方式給它進行了“規格化” ——我們把這個初始程序也叫做“硬體程序”,它會占據程序描述符表的第一個位置,是以可以用task[0] 或INIT_TASK表示。該程序進而會再建立一個新程序去執行init()函數,其實,這個新程序才是系統第一個實際有用的程序,它會負責接着執行下一個階段的初始化操作;而初始程序(INIT_TASK)自己則會開始執行idle循環,也就是說,核心初始化完成之後,初始程序唯一的任務就是在沒有任何其它程序需要執行的時候,消耗空閑的CPU時 間(是以初始程序也被稱為idle程序)。

下一階段的初始化工作要比前一階段輕松一點,因為現在是由一個真正程序接手負責完成它們了,而前一階段都是由“硬體程序”手工去做的。在此階段,這個由INIT_TASK建立的新程序需要初始化總線、網絡并啟動系統中的各種系統核心背景線程,然後再初始化外設、設定檔案格式,在這之後,它要為進入系統做最後的準備——初始化檔案系統,安裝root檔案系統,打開/dev/console裝置,重定向stdin、stdout和stderr到控制台,然後搜尋檔案系統中的init程式,并使用 execve()系統調用加載執行init程 序。系統自此進入了使用者态。

裝載root檔案系統

為了裝載檔案系統,核心需要:1知道root檔案系統位于那個存儲媒體上;2有通路該種媒體的驅動程式。最常見的情況是root是ext2檔案系統,位于IDE硬碟上。這種情況下需要的操作很簡單:将裝置号作為參數給核心就可以了,IDE的裝置驅動程式通常都會編譯進核心的。

如果核心沒有相關媒體的驅動程式,問題就會變得更為複雜。而這種情況并不罕見,比如Linux的安裝盤使用的“通用”核心一般都會碰到。如果核心把所有支援的硬體的裝置驅動程式都包含進來,就會變成一個龐然大物;而且一些驅動程式在檢測硬體的時候會影響其它裝置。

這個問題可以通過initrd機制解決,它允許在裝載實際的root檔案系統之前先使用RAM檔案系統。除了上述兩個原因,引入initrd還可以解決核心的動态合成問題。(詳見參考資料一。)

不過我們應該注意到,init在整個啟動過程中并不是從來就有的,它可以說是一個插件,為了解決以上問題,而被加入啟動過程,象圖一所示,Linux系統在啟動時也可以不選擇它。

為什麼要引入initrd?

Linux啟動過程中肯定要載入核心鏡像,在此過程中有些要素必須考慮:

首先,核心鏡像不能太大。由于受到各種硬體和相容性的限制,Linux的核心鏡像不能太大,但是這并不容易做到。Linux核心的核心部分本身就不小了;而且還必須加入會使用到的驅動程式。

其次,要支援盡可能多的硬體裝置。我們在啟動過程中有一件重要工作:挂載root檔案系統,因為進一步的資料和應用軟體都在其上,是以我們的核心必須能夠通路root檔案系統。對于一般使用者,如果他們使用IDE硬碟上的ext2檔案分區作為root檔案系統,不會有什麼問題。因為不管是IDE硬碟還是ext2檔案系統,它們的驅動肯定會包含在核心鏡像自身裡面。但是,确實存在一些特殊情況:比如說我們希望發行Linux系 統的安裝CD光牒,那麼對CD光牒的驅動,就不一定包含在核心裡面了。(有人可能要奇怪了,咦,CD光牒中的核心鏡像不都已經讀進來了嗎,怎麼核心還通路不了CD光牒呢?注意,讀入核心鏡像的是 Boot Loader ,核心并不具備 Boot Loader 的功能。)如果沒有CD光牒的驅動,我們又怎麼把CD光牒裡的軟體包安裝到使用者的計算機裡呢?把驅動程式預先編譯到核心裡?聽起來還不錯,可是如果我們除了CD光牒還有一些其它的安裝媒體,那麼所有這些驅動就會讓核心鏡像龐大不堪。

而且,還有更嚴重的問題,各種不同的驅動程式很有可能會發生沖突,特别是以前ISA裝置占市場主導地位的時候,這種沖突簡直難以避免。

那時的解決辦法是發行商提供預先編譯好的支援各種裝置的不同核心,把每個核心放進一張軟碟,随發行包一起交給使用者,使用者自己選擇裝有合适核心的軟碟進行引導。或者給使用者提供制作引導盤的工具,讓使用者在安裝前制作自己的啟動盤。當然,哪一種辦法都不能讓人滿意。

唯一的希望在于使用子產品化機制。在核心啟動的時候調用相應的子產品加載驅動程式,然後通路root檔案系統。無論是通過核心對裝置做進一步的分析還是直接從使用者那裡得到配置資訊,先配置再加載子產品的辦法,都能有效地避免沖突的發生。

除了在安裝的時候需要在挂載root檔案系統之前調用相應的子產品之外,在完成安裝的系統上,我們可能仍然需要在挂載root檔案系統之前調用一些子產品。這主要是為給計算機進行配置——一般都要針對不同的計算機進行核心配置。

理想情況下,使用者按照自己的實際情況配置編譯檔案,重新編譯核心,一步步完成這種工作。但是沒有幾個使用者喜歡這種冗長并且極易出現錯誤的工作。而且編譯和生成核心需要相應的工具,可是大部分使用者不需要這些工具。

在安裝的過程中可以直接編譯一個整體式的核心,但這并不能很好的解決問題:首先,所有的編譯工具還需要,其次,編譯過程中出現差錯導緻無法完成任務的機率太大了。是以,我們仍然要使用子產品機制:子產品機制很可靠,出了錯誤也隻不過不加載對應的子產品而已,不會使整個任務失敗。而載入子產品,象前面說的,也是在挂載root檔案系統之前就要得到子產品的。

基于以上理由,Linux引入了initrd機制。

initrd做什麼

initrd允許系統在啟動的時候載入一個RAM盤,這個RAM盤可以被當作一個root檔案系統,程式可以在其上運作。(有兩重含義,第一,程式在上面;第二,程式的檔案系統環境也在上面。)在此之後,可以從别的裝置上挂載一個新的root檔案系統,先前的root檔案系統(initrd)就會被移動到一個目錄上去,最終被解除安裝掉。

為什麼要使用RAM盤呢?首先,使用RAM盤能友善的支援以後可能發生的變化;另外,也是為了保持 Boot Loader 工作盡可能的簡單。在系統引導時,除了核心鏡像之外,Boot Loader把所有相關的資訊作為一個檔案讀入記憶體,核心在啟動中将該檔案作為一段連續的記憶體塊看待。也就是把它當作RAM盤來 使用了。正因為如此,這種機制被稱作“初始 RAM 盤 (initial RAM Disk)”,縮 寫成 initrd。

initrd主要用來把系統的啟動劃分為兩個階段:初始啟動的核心隻需保留最精簡的驅動程式最小集,此後,在啟動必須加載附加的子產品時,從initrd中加載。

initrd進行的操作

使用initrd的時候,典型的系統啟動的流程變為:

Boot Loader讀入核心鏡像以及initrd檔案

核心将initrd檔案轉成“普通”的RAM盤,并且釋放掉initrd檔案占用的記憶體。

initrd被當作root檔案系統,以可讀可寫(read-write)方式安裝。

/linuxrc被執行(它可以是任何可執行檔案,包括腳本在内;它以uid0身份執行,基本上能完成所有init程式可以做的工作)

linuxrc安裝“實際” 的root檔案系統

linuxrc通過pivot_root系統調用将root檔案系統放置在root目錄下。

常用的啟動流 程(比如調用/sbin/init)開始執行。

繼續閱讀