Linux內(nèi)核注釋之第2章代碼初識_第1頁
Linux內(nèi)核注釋之第2章代碼初識_第2頁
Linux內(nèi)核注釋之第2章代碼初識_第3頁
Linux內(nèi)核注釋之第2章代碼初識_第4頁
Linux內(nèi)核注釋之第2章代碼初識_第5頁
已閱讀5頁,還剩8頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

本文格式為Word版,下載可任意編輯——Linux內(nèi)核注釋之第2章代碼初識第2章代碼初識

第2章代碼初識

本章首先從較高層次介紹Linux內(nèi)核源程序的概況,這些都是大家關(guān)心的一些基本特點(diǎn)。隨后將簡要介紹一些實(shí)際代碼。最終以如何編譯內(nèi)核來檢驗(yàn)個(gè)人所進(jìn)行的修改的探討來作為本章的收尾。

Linux內(nèi)核源程序的部分特點(diǎn)

在過去的一段時(shí)期,Linux內(nèi)核同時(shí)使用C語言和匯編語言實(shí)現(xiàn)的。這兩種語言需要一定的平衡:C語言編寫的代碼移植性較好、易于維護(hù),而匯編語言編寫的程序則速度較快。一般只有在速度是關(guān)鍵因素或者一些因平臺相關(guān)特性而產(chǎn)生的特別要求(例如直接和內(nèi)存管理硬件進(jìn)行通訊)時(shí)才使用匯編語言。

正宛如實(shí)際中所做的,即使內(nèi)核并未使用C++的對象特性,部分內(nèi)核也可以在g++(GNU的C++編譯器)下進(jìn)行編譯。同其它面向?qū)ο蟮木幊陶Z言相比較,相對而言C++的開銷是較低的,但是對于內(nèi)核開發(fā)人員來說,這已經(jīng)足夠甚至太多了。

內(nèi)核開發(fā)人員不斷發(fā)展編程風(fēng)格,形成了Linux代碼獨(dú)有的特色。本節(jié)將探討其中的一些問題。

gcc特性的使用

Linux內(nèi)核被設(shè)計(jì)為必需使用GNU的C編譯器gcc來編譯,而不是任何一種C編譯器都可以使用。內(nèi)核代碼有時(shí)要使用gcc特性,伴隨著本書的進(jìn)程,我們將不斷介紹其中的一部分。

一些gcc特有代碼只是簡單地使用gcc語言擴(kuò)展,例如允許在C(不只是C++)中使用inline關(guān)鍵字指示內(nèi)聯(lián)函數(shù)。也就是說,代碼中被調(diào)用的函數(shù)在每次函數(shù)調(diào)用時(shí)都會被擴(kuò)展,因而就可以儉約實(shí)際函數(shù)調(diào)用的開銷。

更為普遍的狀況是代碼的編寫方式比較繁雜。由于對于某些類型的輸入,gcc能夠產(chǎn)生比其它輸入效率更高的執(zhí)行代碼。從理論上講,編譯器可以優(yōu)化具有一致功能的兩種對等的方法,并且得到一致的結(jié)果。因此,代碼的編寫方式是無關(guān)緊要的。但在實(shí)際上,用一些方法編寫所產(chǎn)生的代碼要比用其它方法編寫所產(chǎn)生的代碼的執(zhí)行速度快得多。內(nèi)核開發(fā)人員明白如何才能產(chǎn)生更高效的執(zhí)行代碼的方法,而且這種知識也不斷在他們編寫的代碼中反映出來。

例如,考慮內(nèi)核中經(jīng)常使用的goto語句——為了提高速度,內(nèi)核中經(jīng)常大量使用這種一般要避免使用的語句。在本書中所包含的不到40,000行代碼中,一共有500多條goto語句,大約是每80行一個(gè)。除匯編文件外,確切的統(tǒng)計(jì)數(shù)字是接近每72行一個(gè)goto語句。公允的說,這是選擇偏向的結(jié)果:比例如此高的原因之一是本書中涉及的是內(nèi)核源程序的核心,在這里速度比其它因素都需要優(yōu)先考慮。整個(gè)內(nèi)核的比例大約是每260行一個(gè)goto語句。然而,這依舊是我不再使用Basic進(jìn)行編程以來見過的使用goto頻率最高的地方。

代碼必需受特定編譯器限制的特性不僅與普通應(yīng)用程序的開發(fā)有很大不同,而且也不同于大多數(shù)內(nèi)核的開發(fā)。大多數(shù)的開發(fā)人員使用C語言編寫代碼來保持較高的可移植性,即使在編寫操作系統(tǒng)時(shí)也是如此。這樣做的優(yōu)點(diǎn)是顯而易見的,最為重要的一點(diǎn)是一旦出現(xiàn)更

6

第2章代碼初識

好的編譯器,程序員們可以隨時(shí)進(jìn)行更換。

內(nèi)核對于gcc特性的完全依靠使得內(nèi)核向新的編譯器上的移植工作更加困難。最近Linus對這一問題在有關(guān)內(nèi)核的郵件列表上說明白自己的觀點(diǎn)?!坝涀?,編譯器只是一個(gè)工具。〞這是對依靠于gcc特性的一個(gè)很好的基本思想的表述:編譯器只是為了完成工作。假使通過遵守標(biāo)準(zhǔn)還不能達(dá)到工作要求,那么就不是工作要求有問題,而是對于標(biāo)準(zhǔn)的依靠有問題。

在大多數(shù)狀況下,這種觀點(diǎn)是不能夠被人所接受的。尋常狀況下,為了保證和程序語言標(biāo)準(zhǔn)的一致,開發(fā)人員可能需要犧牲某些特性、速度或者其它相關(guān)因素。其它的選擇可能會為后期開發(fā)造成很大的麻煩。

但是,在這種特定的狀況下,Linus是正確的。Linux內(nèi)核是一個(gè)特例,由于其執(zhí)行速度要比向其它編譯器的可移植性遠(yuǎn)為重要。假使設(shè)計(jì)目標(biāo)是編寫一個(gè)可移植性好而不要求快速運(yùn)行的內(nèi)核,或者是編寫一個(gè)任何人都可以使用自己喜歡的編譯器進(jìn)行編譯的內(nèi)核,那么結(jié)論就可能會有所不同了;而這些恰好不是Linux的設(shè)計(jì)目標(biāo)。實(shí)際上,gcc幾乎可以為所有能夠運(yùn)行Linux的CPU生成代碼,因此,對于gcc的依靠并不是可移植性的嚴(yán)重障礙。

在第3章中我們將對內(nèi)核設(shè)計(jì)目標(biāo)進(jìn)行詳細(xì)說明。

內(nèi)核代碼習(xí)慣用語

內(nèi)核代碼中使用了一些顯著的習(xí)慣用語,本節(jié)將介紹常用的幾個(gè)。當(dāng)你通讀源程序代碼時(shí),真正重要的問題是并不在這些習(xí)慣用語本身,而是這種類型的習(xí)慣用語的確存在,而且是不斷被使用和發(fā)展的。假使你需要編寫內(nèi)核代碼,你應(yīng)當(dāng)注意到內(nèi)核中所使用的習(xí)慣用語,并把這些習(xí)慣用語應(yīng)用到你的代碼中。當(dāng)通讀本書(或者代碼)時(shí),注意你還能找到多少習(xí)慣用語。

為了探討這些習(xí)慣用語,我們首先需要對它們進(jìn)行命名。為了便于探討,筆者創(chuàng)造了這些名字。而在實(shí)際中,大家不一定非要參考這些用語,它們只是對內(nèi)核工作方式的描述而已。

一個(gè)普通的習(xí)慣用語筆者稱之為“資源獲取〞(resourceacquisitionidiom)。在這個(gè)用語中,一個(gè)函數(shù)必需實(shí)現(xiàn)一系列資源的獲取,包括內(nèi)存、鎖等等(這些資源的類型未必一致)。只有成功地獲取當(dāng)前所需要的資源之后,才能處理后面的資源請求。最終,該函數(shù)還必需釋放所有已經(jīng)獲取的資源,而不必對沒有獲取的資源進(jìn)行考慮。

我采用“錯(cuò)誤變量〞這一用語(errorvariableidiom)來輔助說明資源獲取用語,它使用一個(gè)臨時(shí)變量來記錄函數(shù)的期望返回值。當(dāng)然,相當(dāng)多的函數(shù)都能實(shí)現(xiàn)這個(gè)功能。但是錯(cuò)誤變量的不同點(diǎn)在于它尋常是用來處理由于速度的因素而變得十分繁雜的流程控制中的問題。錯(cuò)誤變量有兩個(gè)典型的值,0(表示成功)和負(fù)數(shù)(表示有錯(cuò))。

這兩個(gè)用語結(jié)合使用,我們就可以十分自然地得到符合模式的代碼如下:

Intf(void){

interr;

resource*r1,*r2;

err=-ERR1/*assumefailure*/r1=acquire_resource();

if(!r1)/*notaquired*/

gotoout/*returns-ERR1*/

Gotresourcer1,tryforr2.*/

7

第2章代碼初識

err=-ERR2;

r2=acquire_resource2();if(!r2)/*notaquired*/

gotoout1/*returns–ERR2*/

/*havebothr1andr2.*/err=0;

/*…user1andr2…*/

out2:

release_resource(r2)

out2:

release_resource(r2)out:

returnerr;}

(注意變量err是使用錯(cuò)誤變量的一個(gè)明確實(shí)例,同樣,諸如out之類的標(biāo)號則指明白資源獲取用語的使用。)

假使執(zhí)行到標(biāo)號out2,則都已經(jīng)獲取了r1和r2資源,而且也都需要進(jìn)行釋放。假使執(zhí)行到標(biāo)號out1(不管是順序執(zhí)行還是使用goto語句進(jìn)行跳轉(zhuǎn)到),則r2資源是無效的(也可能剛被釋放),但是r1資源卻是有效的,而且必需在此將其釋放。同理,假使標(biāo)號out能被執(zhí)行,則r1和r2資源都無效,err所返回的是錯(cuò)誤或成功標(biāo)志。

在這個(gè)簡單的例子中,對于err的一些賦值是沒有必要的。在實(shí)踐中,實(shí)際代碼必需遵守這種模式。這樣做的原因主要在于同一行中可能包含有多種測試,而這些測試應(yīng)當(dāng)返回一致的錯(cuò)誤代碼,因此對錯(cuò)誤變量統(tǒng)一賦值要比屢屢賦值更為簡單。雖然在這個(gè)例子中對于這種屬性的必要性并不十分迫切,但是我還是傾向于保存這種特點(diǎn)。有關(guān)的實(shí)際應(yīng)用可以參考sys_shmctl(第21654行),在第9章中還將詳細(xì)介紹這個(gè)例子。

減少#if和#ifdef的使用

現(xiàn)在的Linux內(nèi)核已經(jīng)移植到不同的平臺上,但是我們還必需解決移植過程中所出現(xiàn)的問題。大部分支持各種不同平臺的代碼由于包含大量預(yù)處理代碼現(xiàn)都已變得十分不規(guī)范,例如:

#ifdefined(SOLARIS)

/*…dothingsthesolarisway…*/#elifdefined(HPUX)

/*…dothingstheHP-UXway…*/#elifdefined(LINUX)

/*…dothingstherightway…*/

8

第2章代碼初識

#endif

這個(gè)例子試圖實(shí)現(xiàn)操作系統(tǒng)的可移植性,雖然Linux關(guān)注的焦點(diǎn)很明顯是實(shí)現(xiàn)代碼在各種CPU上的可移植性,但是二者的基本原理是一致的。對于這類問題來說,預(yù)處理器是一種錯(cuò)誤的解決方式。這些雜亂的問題使得代碼晦澀難懂。更為糟糕的是,增加對新平臺的支持有可能要求重新遍歷這些雜亂分布的低質(zhì)量代碼段(實(shí)際上你很難能找到這類代碼段的全部)。

與現(xiàn)有方式不同的是,Linux一般通過簡單函數(shù)(或者是宏)調(diào)用來抽象出不同平臺間的差異。內(nèi)核的移植可以通過實(shí)現(xiàn)適合于相應(yīng)平臺的函數(shù)(或宏)來實(shí)現(xiàn)。這樣不僅使代碼的主體簡單易懂,而且在移植的過程中還可以比較簡單地自動檢測出你沒有注意到的內(nèi)容:如引用未聲明函數(shù)時(shí)會出現(xiàn)鏈接錯(cuò)誤。有時(shí)用預(yù)處理器來支持不同的體系結(jié)構(gòu),但這種方式并不常用,而相對于代碼風(fēng)格的變化就更是微不足道了。

順便說一下,我們可以注意到這種解決方法和使用用戶對象(或者C語言中充滿函數(shù)指針的struct結(jié)構(gòu))來代替離散的switch語句處理不同類型的方法十分相像。在某些層次上,這些問題和解決方法是統(tǒng)一的。

可移植性的問題并不僅限于平臺和CPU的移植,編譯器也是一個(gè)重要的問題。此處為了簡化,假設(shè)Linux只使用gcc來編譯。由于Linux只使用同一個(gè)編譯器,所以就沒有必要使用#if塊(或者#ifdef塊)來選擇不同的編譯器。

內(nèi)核代碼主要使用#ifdef來區(qū)分需要編譯或不需要編譯的部分,從而對不同的結(jié)構(gòu)提供支持。例如,代碼經(jīng)常測試SMP宏是否定義過,從而決定是否支持SMP機(jī)。

代碼樣例

上一節(jié)僅僅是一些探討,了解Linux代碼風(fēng)格最好的方法就是實(shí)際研究一下它的部分代碼。即使你不完全理解本節(jié)所探討代碼的細(xì)節(jié)也無關(guān)緊要,終究本節(jié)的主要目的不是理解代碼,一些讀者可以只對本節(jié)進(jìn)行瀏覽。本節(jié)的主要目的是讓讀者對Linux代碼進(jìn)行初步了解,對今后的工作提供必要基礎(chǔ)。而探討將涉及部分廣泛使用到的內(nèi)核代碼。

printk

printk(25836行)是內(nèi)核內(nèi)部消息日志記錄函數(shù)。在出現(xiàn)諸如內(nèi)核檢測到其數(shù)據(jù)結(jié)構(gòu)出現(xiàn)不一致的事件時(shí),內(nèi)核會使用printk把相關(guān)信息打印到系統(tǒng)控制臺上。對于printk的調(diào)用一般分為如下幾類:

?緊急事件(emergency)――例如,panic函數(shù)(25563行)屢屢使用了printk。當(dāng)

內(nèi)核檢測到發(fā)生不可恢復(fù)的內(nèi)部錯(cuò)誤時(shí)就會調(diào)用panic函數(shù),然后盡其所能的安全關(guān)閉計(jì)算機(jī)。這個(gè)函數(shù)中調(diào)用printk以提醒用戶系統(tǒng)將要關(guān)閉。

?調(diào)試――從3816行開始的#ifdef塊使用printk來打印SMP規(guī)律單元(box)中每

一個(gè)處理器的相關(guān)配置信息,但是此過程只有在使用SMP_DEBUG標(biāo)志編譯代碼的狀況下才能夠被執(zhí)行。?普通信息――例如,當(dāng)機(jī)器啟動時(shí),內(nèi)核必需估計(jì)系統(tǒng)速度以確保設(shè)備驅(qū)動程序能

夠忙等待(busy-waiting)一個(gè)確切的極短周期。計(jì)算這種估計(jì)值的函數(shù)名為calibrate_delay(19654行),它既在19661行使用printk聲明馬上開始計(jì)算,又在19693行報(bào)告計(jì)算結(jié)果。另外,在第4章將詳細(xì)的介紹calibrate_delay函數(shù)。假使你已經(jīng)瀏覽過這些參照代碼,你可能已經(jīng)注意到printk和printf的參數(shù)十分類似:

9

第2章代碼初識

一個(gè)格式化字符串,后跟零個(gè)或者多個(gè)參數(shù)參與字符串中。格式化字符串可能是以一組“〞開始,這里的N是從0到7的數(shù)字,包括0和7在內(nèi)。數(shù)字區(qū)分了消息的日志等級(loglevel),只有當(dāng)日志等級高于當(dāng)前控制臺定義的日志等級(console_loglevel,25650行)時(shí),才會打印消息。root可以通過適當(dāng)減小控制臺的日志等級來過濾不是很緊急的消息。假使內(nèi)核在格式化字符串中檢測不到日志等級序列,那么就會一直打印消息。(實(shí)際上,日志等級序列并不一定要在格式化字符串中出現(xiàn),可以在格式化文本中查找到它的代碼。)

從14946行開始的#define塊說明白這些特別序列,這些定義可以幫助調(diào)用者正確區(qū)分對printk的調(diào)用。簡單的說,我稱日志等級0到4為“緊急事件〞,從等級5到等級6為“普通信息〞,等級7自然就是我所說的“調(diào)試〞。(這種分類方法并不意味著其它更好的分類方法沒有用處,而只是目前我們還不關(guān)心它而已。)

在上面探討的基礎(chǔ)上,我們研究一下代碼本身。

printk

25836:參數(shù)fmt是printf類型的格式化字符串。假使你對“…〞部分的內(nèi)容不熟悉,那就

需要參閱一本好的C語言參考書(在其索引中查找“變參函數(shù),variadicfunction〞)。另外,在安裝的GNU/Linux中的stdarg幫助里也包含了一個(gè)有關(guān)變參函數(shù)的簡明描述,在這兒只需要敲入“manstdarg〞就可以看到。

簡單的說,“…〞部分提醒編譯器fmt后面可能緊跟著數(shù)量不定的任何類型的參數(shù)。

由于這些參數(shù)在編譯的時(shí)候還沒有類型和名字,內(nèi)核使用由三個(gè)宏va_start,va_arg和va_end組成的特別組以及一個(gè)特別類型――va_list對它們進(jìn)行處理。

25842:msg_level記錄了當(dāng)前消息的日志等級。它是靜態(tài)的,這看起來可能會有些奇怪――

為什么下一次對printk的調(diào)用需要記錄日志等級呢?問題的答案是只有打印出新行(\\n)或者賦給一個(gè)新的日志等級序列以后,當(dāng)前消息才會終止。這樣通過在包含消息終止的新行里調(diào)用printk,就保證了在多個(gè)短期沖突的狀況下,調(diào)用者只打印唯一一個(gè)長消息。

25845:在SMP規(guī)律單元中,內(nèi)核可能試圖從不同的CPU向控制臺同時(shí)打印信息。(有時(shí)在

單處理機(jī)(UP)規(guī)律單元中也會發(fā)生同樣問題,但由于中斷還未被覆蓋掉,所以問題也并不十明顯顯。)假使不進(jìn)行任何協(xié)同的話,結(jié)果就將處于完全無法讓人了解的雜亂無章的狀態(tài),每個(gè)消息的各個(gè)部分都和其它消息的

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論