《Linux核心修煉之道》 之 高效學習Linux核心

fudan_abc發表於2010-07-15

世界悲結束了,章魚哥也退役了,連非誠勿擾中的拜金女也突然的少了很多。這本《Linux核心修煉之道》在卓越噹噹china-pub上也已經開賣了,雖然是嚴肅文學,但為了保證流暢性,大部分文字我還都是斟詞灼句,反覆的念幾遍才寫上去的,儘量考慮到寫上去的每段話能夠讓讀者產生什麼疑惑,然後也都會緊接著儘量的去進行解釋清楚,中間的很多概念也有反覆糾結過怎麼解釋能夠更容易的理解,力求即使對於初學者也可以有很少阻礙的一氣讀完。同時我也把書中一部分自己的感悟抽出來整理了精華版,share出來。當然水平有限,錯漏之處有發現而修訂時遺漏的,也有尚沒有發現的。這本書如果對您有用,乃我之幸事,如果無用,就在此先誠惶誠恐的向大家拜個不是了。

在6月份做過一次《高效學習Linux核心》的presentation,下面是前面的一部分內容及講義,或許對大家有用吧。至於剩餘的,因為和之前部落格中的部分文章內容差不多,就不貼了。

**********************************************************************

既然有高效,相對的就有低效。學習本身就是一件很玄乎的事情,有些人整天瀟瀟灑灑沒見怎麼用心就能夠獲得很好的成績,而有些人則相反,即使投懸樑錐刺骨也還是成績平平收穫平平。這裡面很大一部分的原因就是學習的方法。

但是學習方法這樣的題目並不好講,因為基於每個人不同的情況,並沒有那樣一個標準的方法存在,所以講起來就很容易成為一場大忽悠。就像我們的任志強先生前陣子演講賣房子 的方法時,就因為太像一場忽悠,從而被聽眾扔了鞋。

 

接下來我就通過自己的一些感悟,拋磚引玉來介紹一下如何比較高效的去學習 linux 核心。這些話並不侷限於某個部分的內容,很像一句句的口號,我們也可以將它們看作核心學習的大字報。

首先是第一句話:把核心當朋友。今年笑來老師有本新書,叫把時間當朋友,告訴我們只有把時間當朋友,才能更好的利用自己的時間做些有益的事情。同樣,我們只有把核心當朋友,把它放在對等的地位上,而不僅僅是一堆死氣沉沉的程式碼,我們才能夠更好的認識和理解到它的精髓。

然後是第二句話:先會使用它。意思就是我們在學習核心前首先要會用 linux ,依照一個由上至下循序漸進的過程,在能夠熟練的使用 Linux 作業系統之後再去研究核心中的實現。這也是 linus 本人的觀點。

第三句是依照四個層次進行核心學習。笛卡兒在 17 世紀的某一天,閒極無聊寫了這麼一本書,書名就叫《方法論》,在這本目前來說絕大部分人都不知道的書裡將方法上升到了理論的高度。笛卡兒在他的這本書裡將研究問題的方法歸納為簡單的一句話,就是 複雜問題要簡單化 。就是說要將複雜的問題分解為很多個簡單的小問題,一個個的分開解決。這句話當然可以借鑑運用到核心的學習上,不過需要做些改動,不是分解為多個簡單的小問題,而是將核心學習這麼一件很複雜的事情劃分為由低到高多個不同的層次,每一層次都有自己需要達到的目標和要求。這也是我自己認為比較好的認識學習核心的方法。

第四句是走出心理誤區。 對於學習這種複雜的事情來說,無論是我們在學校的課堂學習,還是這裡說的核心學習,它的效果好與壞,最主要取決於兩個方面:一個是學習的方法,另一個就是學習時的心理。注意,在這兒我無視了智商的差異,智商這玩意兒太玄了,可以將它歸於迷信的範疇。而我們在學習時經常會產生一系列的問題或者說誤區,只有走出這些誤區,在學習中養成一個堅強的心理,我們才能夠真正的做到高效。

第五句是使用 vim+cscope+ctags 瀏覽核心原始碼。其實這句話更主要的意思是說我們需要一個好的工具去瀏覽核心的程式碼。在 windows 下面,我們或許可以很容易的找到很多比較好的 IDE 可以用來瀏覽程式碼,比如 source insight ,它可以很方便的在程式碼之間進行關聯閱讀。但是對於 Linux 新人來說,有沒有一個功能類似的瀏覽程式碼的工具就成為一個很常見的問題。

第六句是使用 kernel 地圖定位目的碼。應該說學習核心就是學習核心的原始碼,但是核心程式碼千千萬,又到處像個迷宮一樣,不迷路都很難,又怎麼去直面它?這時我們就需要這樣的一幅核心地圖來幫助我們去定位所要分析的目的碼,並縮小目的碼的範圍與程式碼量。

接下來是第七句話:分析核心原始碼,態度決定一切。我們很多人或許有這樣的困惑,也分析瀏覽了很多核心的原始碼,可總是覺得分析完瀏覽完腦子裡還是空空的,並沒有感覺到多大的收穫。這個時候我們或許可以去看看是不是自己在分析程式碼時的態度出現了問題。我們在分析核心原始碼時,只有遵循嚴謹的態度,去理解每一段程式碼的實現,多問多想多記,而不是抱著走馬觀花,得過且過的態度,最終必然會有很大的收穫。

最後一句是:以核心原始碼為中心,堅持學習資源建設。在我們核心學習的過程中,核心原始碼本身就是最好的參考資料,其他任何經典或非經典的書最多隻是起到個輔助作用,不能也不應該取代核心程式碼在我們學習過程中的主導地位。但是這些輔助的作用也是不可忽視的,我們需要以核心原始碼為中心,堅持各種學習資源的長期建設不動搖。

除了這裡的八句話,其他的可能會對大家有幫助的感悟或者方法還有很多

 

把核心當朋友,就是要把核心看成一個鮮活的生命體,而不是一堆死氣沉沉的程式碼。

具體一點來說,我們在學習與瀏覽核心的實現時,可以將它看成是現實世界的對映。核心是由現實中的人寫出來的,因此不管是有意還是無意,都會不可避免的包含了一些自己的現實感情,我們研究核心時可以體會下這種脈絡,這種隱藏在程式碼背後的哲學。比如,我們可以認為核心是個大世界,一個個程式就是這個世界中的一個個生命體,程式管理和排程就是這個大世界中的權力機關,記憶體是程式的家,核心的目標就是要做到使每個程式都居者有其屋。

既然要把核心看成是一個鮮活的個體,那麼我們認識它的第一件事就是了解它的一些基本資訊,就像我們人與人之間互相認識首先也是通過個人的基本資訊一樣。

首先從名字開始, kernel 在字典中主要有兩種定義,一種是 軟的,一個堅果可食用的部分 ,對 Linux kernel 來說,當然適用的是第二種定義: 某個東西的核心部分 。所以從廣義上來說, linux kernel 就是 linux 作業系統裡最為核心的部分,而從狹義上來說,它不過就是 Linus 那群人人寫的那點兒程式碼。

當然,這點兒程式碼是相當複雜的,單單從程式碼量上來說,早已經突破了千萬級。從結構上來說,也早就不是一個人窮自己一己之力就能夠全部理解的了。所以,現在強調第二句話:學核心切忌求大求全,選擇一點研究的足夠深入就很不容易了。

下面介紹介紹 kernel 的年齡, kernel 又不是一個懷春的少女,所以它的年齡並不需要保密,從 1991 年誕生開始,在去年剛剛舉行了它自己的成人禮,進入了成熟發展期。

就像我們人有自己的青春期、中年期等一樣, kernel 相應的也有很多不同的版本號,不過不同的是,我們的青春期一去就不復返了, kernel 不同的版本號卻是共存的。

很多年以來,核心的版本都是以 X.Y.Z 3 個數字的形式分配的,中間的偶數 Y 代表穩定版,奇數 Y 代表了不穩定的開發版。所謂的穩定版本是指核心的特性都已經固定,程式碼執行穩定可靠,不會再增加新的特性,要改進也只是修改程式碼中的錯誤。而不穩定版本是指相對於上一個穩定版本增加了新的特性,還處於發展之中,程式碼的執行不大可靠。

對於目前來說, 2.6 核心的釋出已經持續了很長時間,那麼什麼時候將會推出 2.7 Linus 本人的回答是,不會有 2.7 ,他不會再遵循舊的模式,新採用的模式會更好,不值得重複過去。他表示正在考慮新的編號方式,一種基於時間的版本號。比如用 2008.7 取代 2.6.26 ,中間第二個數字代表年, 2008 年就是 2.8 2009 年的第一個版本就是 2.9.1 ,之後 2010 年是 3.0 ,等等。

最後不得不提到是那些眼花繚亂的發行版,核心與發行版的關係就類似那種雙生花,彼此互相依賴互相扶持共同成長。沒有那些發行版,核心就只能是束之高閣的一個貌似好看的玩具,並不能真正的走進我們的工作生活,而沒有核心,那些發行版就缺少了存在的地基,就只能是個豆腐渣工程。

 

現在我們瞭解了核心這個朋友的外表,這個時候我們不能像非誠勿擾中的那些拜金女一樣只關心外在的資訊,我們還要接著瞭解核心的內涵,也就是核心的體系結構以及核心是如何工作的。

首先看第一張圖,它向我們傳遞了這樣的資訊 —— 核心將應用程式和硬體分離開來。核心一方面負責與計算機硬體進行互動,實現對硬體的控制,排程對硬體資源的訪問,另一方面為使用者應用程式提供一個高階的執行環境和訪問硬體的虛擬介面。

提供硬體的相容性是核心的設計目標之一,幾乎所有的硬體,只要不是為其他作業系統所定製的,都可以得到 Linux 的支援。

與硬體相容性相關的是可移植性,也就是在不同的硬體平臺上執行 Linux 的能力。從最初只支援標準 IBM 相容機上的 Intel X86 架構到現在可以支援 ARM MIPS PowerPC 等幾乎所有硬體平臺,如此廣泛的平臺支援之所以能夠成功,部分原因在於,核心清晰地劃分為了體系相關部分和體系無關部分。因此也就有了第二張圖。

體系無關部分通常會定義與體系相關部分的介面,這樣,核心向新的體系結構移植的過程就變成確認這些介面的特性並將它們加以實現的過程。

同時,使用者應用程式和核心之間的聯絡,一般是通過它和核心的中間層 —— 標準 C 庫來實現,而標準 C 庫函式本身,則是建立在核心提供的系統呼叫基礎之上。通過標準 C 庫,以及核心體系無關部分與體系相關部分的介面,使用者應用程式和部分核心都成為可移植的。

因此更為準確的是第三張圖。其中,程式管理部分實現了一個程式世界的抽象,這個程式世界類似於我們的人類世界,只不過我們的世界裡的個體是人,而在程式世界裡則是一個一個的程式,我們人與人之間通過書信、手機、網路等交通往來,而各個程式之間則是通過不同方式的程式間通訊,我們所有人都在分享同一個地球,而所有程式都在分享一個或多個 CPU

在這個程式的世界裡,記憶體是重要的資源之一,就好似我們的土地。因此,管理記憶體的策略與方式,也就是記憶體管理是決定系統效能的一個關鍵因素。

 

瞭解了核心的體系結構,我們再來看看核心是如何工作的。

首先,核心通過系統呼叫來使得執行在它上面的應用程式可用。系統呼叫是核心和應用程式之間的橋樑,比如圖中的針對檔案操作的 open() close() read() write() ,針對程式操作的 fork() wait() ,還有針對網路操作的 socket() 等等,它們提供了對硬體的抽象,所以有時也被稱為 linux 虛擬機器。

核心提供的最接近實際使用者的明顯抽象是檔案系統,我們很容易能夠利用 open() 等幾個系統呼叫編寫一段程式開啟一個檔案並將它的內容拷貝到標準輸出。核心通過這些系統呼叫為使用者提供了一個檔案的 " 錯覺 " ,而實際上它不過是一堆資料有了個名字,這樣一來就不必去與硬體底層的堆疊、分割槽和指標等交涉,這也就是我們經常所說的抽象 (abstraction) ,將底層的東西以更易懂的方式表達出來。

檔案系統是核心提供的比較明顯的一種抽象,我們可以說它是位於臺前的,而相對還有一些是位於幕後的,比如程式排程。在 linux 上,任何 一個時間,都可能有好幾個程式或者程式等待著執行。核心的時間排程給每個程式分配 CPU 時間,所以就一段時間內來說,我們會有種錯覺:電腦同一時間執行好幾個程式。

再比如另外一個位於幕後的記憶體管理,幕後到應用開發者都不易察覺的地步。每個程式執行得都好像它有個自己的地址空間來呼叫一樣,實際上它跟其他程式一樣共享計算機的物理儲存。記憶體管理的另外一個方面是防止一個程式訪問其他程式的地址空間 —— 對於多程式作業系統來說這是很必要的一個防範措施。

當然位於臺前幕後的還有其他的角色,比如網路等。

我們現在再來簡單看下它的物理組成。早期版本的核心是整體式的,也就是說所有的部分都靜態地連線成一個很大的執行檔案。

 

相關文章