freepeople性欧美熟妇, 色戒完整版无删减158分钟hd, 无码精品国产vα在线观看DVD, 丰满少妇伦精品无码专区在线观看,艾栗栗与纹身男宾馆3p50分钟,国产AV片在线观看,黑人与美女高潮,18岁女RAPPERDISSSUBS,国产手机在机看影片

正文內(nèi)容

linux操作系統(tǒng)源代碼詳細分析(編輯修改稿)

2025-07-26 08:01 本頁面
 

【文章內(nèi)容簡介】 用的習(xí)慣用語,并把這些習(xí)慣用語應(yīng)用到你的代碼中。當(dāng)通讀本書(或者代碼)時,看看你還能找到多少習(xí)慣用語。 為了討論這些習(xí)慣用語,我們首先需要對它們進行命名。為了便于討論,筆者創(chuàng)造了這些名字。而在實際中,大家不一定非要參考這些用語,它們只是對內(nèi)核工作方式的描述而已。 一個普通的習(xí)慣用語,筆者稱之為“資源獲取”(resource acquisition idiom)。在這個用語中,一個函數(shù)必須實現(xiàn)一系列資源的獲取,包括內(nèi)存、鎖等等(這些資源的類型未必相同)。只有成功地獲取當(dāng)前所需要的資源之后,才能處理后面的資源請求。最后,該函數(shù)還必須釋放所有已經(jīng)獲取的資源,而不必考慮沒有獲取的資源。 我采用“錯誤變量”這一用語(error variable idiom)來輔助說明資源獲取用語,它使用一個臨時變量來記錄函數(shù)的期望返回值。當(dāng)然,相當(dāng)多的函數(shù)都能實現(xiàn)這個功能。但是錯誤變量的不同點在于它通常是用來處理由于速度的因素而變得非常復(fù)雜的流程控制中的問題。錯誤變量有兩個典型的值,0(表示成功)和負數(shù)(表示有錯)。 如果執(zhí)行到標(biāo)號out2,則都已經(jīng)獲取了r1和r2資源,而且也都需要進行釋放。如果執(zhí)行到標(biāo)號out1(不管是順序執(zhí)行還是使用goto語句進行跳轉(zhuǎn)到),則r2資源是無效的(也可能剛被釋放),但是r1資源卻是有效的,而且必需在此將其釋放。同理,如果標(biāo)號out能被執(zhí)行,則r1和r2資源都無效,err所返回的是錯誤或成功標(biāo)志。 在這個簡單的例子中,對err的一些賦值是沒有必要的。在實踐中,實際代碼必須遵守這種模式。這樣做的原因主要在于同一行中可能包含有多種測試,而這些測試應(yīng)該返回相同的錯誤代碼,因此對錯誤變量統(tǒng)一賦值要比多次賦值更為簡單。雖然在這個例子中對于這種屬性的必要性并不非常迫切,但是我還是傾向于保留這種特點。有關(guān)的實際應(yīng)用可以參考sys_shmctl(第21654行),在第9章中還將詳細介紹這個例子。 減少if和ifdef的使用 現(xiàn)在的Linux內(nèi)核已經(jīng)移植到不同的平臺上,但是我們還必須解決移植過程中所出現(xiàn)的問題。大部分支持各種不同平臺的代碼由于包含許多預(yù)處理代碼而已經(jīng)變得非常不規(guī)范,例如: 這個例子試圖實現(xiàn)操作系統(tǒng)的可移植性,雖然Linux關(guān)注的焦點很明顯是實現(xiàn)代碼在各種CPU上的可移植性,但是二者的基本原理是一致的。對于這類問題來說,預(yù)處理器是一種錯誤的解決方式。這些雜亂的問題使得代碼晦澀難懂。更為糟糕的是,增加對新平臺的支持有可能要求重新遍歷這些雜亂分布的低質(zhì)量代碼段(實際上你很難能找到這類代碼段的全部)。 中國與現(xiàn)有方式不同的是,Linux一般通過簡單函數(shù)(或者是宏)調(diào)用來抽象出不同平臺間的差異。內(nèi)核的移植可以通過實現(xiàn)適合于相應(yīng)平臺的函數(shù)(或宏)來實現(xiàn)。這樣不僅使代碼的主體簡單易懂,而且在移植的過程中還可以比較容易地自動檢測出你沒有注意到的內(nèi)容:如引用未聲明函數(shù)時會出現(xiàn)鏈接錯誤。有時用預(yù)處理器來支持不同的體系結(jié)構(gòu),但這種方式并不常用,而相對于代碼風(fēng)格的變化就更是微不足道了。 順便說一下,我們可以注意到這種解決方法和使用用戶對象(或者C語言中充滿函數(shù)指針的struct結(jié)構(gòu))來代替離散的switch語句處理不同類型的方法十分相似。在某些層次上,這些問題和解決方法是統(tǒng)一的。 可移植性的問題并不僅限于平臺和CPU的移植,編譯器也是一個重要的問題。此處為了簡化,假設(shè)Linux只使用gcc來編譯。由于Linux只使用同一個編譯器,所以就沒有必要使用if塊(或者ifdef塊)來選擇不同的編譯器。 內(nèi)核代碼主要使用ifdef來區(qū)分需要編譯或不需要編譯的部分,從而對不同的結(jié)構(gòu)提供支持。例如,代碼經(jīng)常測試SMP宏是否定義過,從而決定是否支持SMP機。 代碼樣例 了解Linux代碼風(fēng)格最好的方法就是實際研究一下它的部分代碼。即使你不完全理解本節(jié)所討論代碼的細節(jié)也無關(guān)緊要,畢竟本節(jié)的主要目的不是理解代碼,一些讀者可以只對本節(jié)進行瀏覽。本節(jié)的主要目的是讓讀者對Linux代碼進行初步了解,為今后的工作提供必要基礎(chǔ)。該討論將涉及部分廣泛使用的內(nèi)核代碼。 printk printk(25836行)是內(nèi)核內(nèi)部消息日志記錄函數(shù)。在出現(xiàn)諸如內(nèi)核檢測到其數(shù)據(jù)結(jié)構(gòu)出現(xiàn)不一致的事件時,內(nèi)核會使用printk把相關(guān)信息打印到系統(tǒng)控制臺上。對于printk的調(diào)用一般分為如下幾類: * 緊急事件(emergency)—例如,panic函數(shù)(25563行)多次使用了printk。當(dāng)內(nèi)核檢測到發(fā)生不可恢復(fù)的內(nèi)部錯誤時就會調(diào)用panic函數(shù),然后盡其所能地安全關(guān)閉計算機。這個函數(shù)中調(diào)用printk以提示用戶系統(tǒng)將要關(guān)閉。 * 調(diào)試—從3816行開始的ifdef塊使用printk來打印SMP邏輯單元(box)中每一個處理器的相關(guān)配置信息,但是此過程只有在使用SMP_DEBUG標(biāo)志編譯代碼的情況下才能夠被執(zhí)行。 * 普通信息—例如,當(dāng)機器啟動時,內(nèi)核必須估計系統(tǒng)速度以確保設(shè)備驅(qū)動程序能夠忙等待(busywait)一個精確的極短周期。計算這種估計值的函數(shù)名為calibrate_delay(19654行),它既在19661行使用printk聲明馬上開始計算,又在19693行報告計算結(jié)果。另外,在第4章將詳細的介紹calibrate_delay函數(shù)。 如果你已經(jīng)瀏覽過這些參照行,你可能已經(jīng)注意到printk和printf的參數(shù)十分類似:一個格式化字符串,后跟零個或者多個參數(shù)加入字符串中。格式化字符串可能是以一組“”開始,這里的N是從0到7的數(shù)字,包括0和7在內(nèi)。數(shù)字區(qū)分了消息的日志等級(log level),只有當(dāng)日志等級高于當(dāng)前控制臺定義的日志等級(console_loglevel,25650行)時,才會打印消息。root可以通過適當(dāng)減小控制臺的日志等級來過濾不是很緊急的消息。如果內(nèi)核在格式化字符串中檢測不到日志等級序列,那么就會一直打印消息(實際上,日志等級序列并不一定要在格式化字符串中出現(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ù),variadic function”)。另外,在安裝的GNU/Linux中的stdarg幫助里也包含了一個有關(guān)變參函數(shù)的簡明描述,在這兒只需要敲入“man stdarg”就可以看到。 簡單地說,“...”部分提示編譯器fmt后面可能緊跟著數(shù)量不定的任何類型的參數(shù)。由于這些參數(shù)在編譯的時候還沒有類型和名字,內(nèi)核使用由三個宏va_start、va_arg和va_end組成的特殊組及一個特殊類型—va_list對它們進行處理。 25842:msg_level記錄了當(dāng)前消息的日志等級。它是靜態(tài)的,這看起來可能會有些奇怪—為什么下一次對printk的調(diào)用需要記錄日志等級呢?問題的答案是只有打印出新行(\n)或者賦給一個新的日志等級序列以后,當(dāng)前消息才會結(jié)束。這樣,通過在包含消息結(jié)束的新行里調(diào)用printk,就保證了在多個短期沖突的情況下,調(diào)用者只打印唯一一個長消息。 25845:在SMP邏輯單元中,內(nèi)核可能試圖從不同的CPU向控制臺同時打印信息(有時在單處理機(UP)邏輯單元中也會發(fā)生同樣問題,但由于中斷還未被覆蓋掉,所以問題也并不十分明顯)。如果不進行任何協(xié)同的話,結(jié)果就將處于完全無法讓人了解的雜亂無章的狀態(tài),每個消息的各個部分都和其他消息的各個部分混雜交織在一起。 相反,內(nèi)核使用旋轉(zhuǎn)鎖(spinlock)來控制對控制臺的訪問。旋轉(zhuǎn)鎖將在第10章進行深入介紹。 如果你對flags 在傳送給spin_lock_irqsave之前為什么不對它初始化感到疑惑,請不要擔(dān)心:spin_lock_irqsave(對于不同的版本請分別參看12614行,12637行,12716行和12837行)是一個宏,而不是一個函數(shù)。該宏實際上是將值寫入flags中,而不是從flags中讀出值(在25895行中,存儲在flags中的信息被spin_unlock_irqrestore回讀,請參看12616行,12639行,12728行和12841行)。 25846:初始化變量args,該變量代表printk參數(shù)中的“...”部分。 25848:調(diào)用內(nèi)核自身的vsprintf(為節(jié)省空間而省略)實現(xiàn)。該函數(shù)的功能與標(biāo)準(zhǔn)vsprintf函數(shù)非常相似,向buf中寫入格式化文本(25634行)并返回寫入字符串的長度(長度不包括最后一位終止字符0字節(jié))。很快,你將可以看到為什么這種機制會忽略buf的前三個字符。 (正如25847行的注釋中所述)我們應(yīng)該注意到在這里并沒有采取嚴(yán)格的措施來保證緩沖器不會過載。這里系統(tǒng)假定1024個字符長度的buf已經(jīng)足夠使用(參閱25634行)。如果內(nèi)核在這里能夠使用vsnprintf函數(shù)的話,情況就會好許多。然而,vsnprintf還有另外一個參數(shù)限制了它能夠?qū)懭刖彌_器的字符長度。 25849:計算buf中最近使用的元素,調(diào)用va_end終止對“...”參數(shù)的處理。 25851:開始格式化消息的循環(huán)。其中存在一個內(nèi)部循環(huán)能夠處理更多內(nèi)容(這一點隨后就能看到),因此,每次內(nèi)循環(huán)開始,都開始一個新的打印行。由于通常情況下printk只用于打印單行,所以在每次調(diào)用中,這種循環(huán)通常只執(zhí)行一次。 25853:如果預(yù)先不知道消息的日志等級,printk會檢查當(dāng)前行是否以日志等級序列開頭。 25860:如果不是,buf中開始未使用的三個字符就能夠起作用了(第一次以后的每次循環(huán),都會覆蓋部分消息文本,但是這樣并不會引起問題,因為這里的文本只是前面行中的一部分,它們已經(jīng)被打印過,而且以后也不再需要了)。這樣,就可以將日志等級插入buf中。 中國 25866:此處有如下屬性:p指向日志等級序列(消息文本緊隨其后),msg指向消息文本—請注意25852行和25865行中對msg的賦值。 中國 由于已知p用來指示日志等級序列的開頭—該日志等級序列可能是由函數(shù)自身所創(chuàng)建的,日志等級可以從p中抽出并存到msg_level中。 25868:沒有檢測到新行,清空line_feed標(biāo)志。 25869:這是前面談到過的內(nèi)循環(huán),循環(huán)將運行到本行結(jié)束(也就是檢測到新行標(biāo)志)或者緩沖器的末尾為止。 25870:除了將消息打印到控制臺之外,printk還能夠記錄最近打印的長度為LOG_ BUF_LEN的字符組(LOG_BUF_LEN為16K,請參看25632行)。如果在控制臺打開之前,內(nèi)核就已經(jīng)調(diào)用printk,則顯然不能在控制臺上正確打印消息,但是這些消息將被盡可能地存儲到log_buf中(25656行)。當(dāng)控制臺打開以后,緩存在log_buf中的數(shù)據(jù)就可以轉(zhuǎn)儲并在控制臺上打印出來,請參看25988行。 log_buf是一個循環(huán)緩沖器,log_start和log_size變量(25657行和25646行)分別記錄當(dāng)前緩沖器的開始位置和長度。本行中的按位與(AND)操作實際上是快速求模(%)運算,它的正確性依賴于LOG_BUF_LEN的值是2的冪。 中國25872:保存變量跟蹤記錄循環(huán)日志的值。顯然,日志大小會不斷增長,直至達到LOG_BUF_LEN的值為止。此后,log_size將保持不變,而插入新字符將導(dǎo)致log_start的增長。 25878:請注意logged_chars(25658行)記錄從機器啟動之后由printk寫入的所有字符的長度,它在每次循環(huán)中都會被更新,而不是在循環(huán)結(jié)束后才改變一次?;谕瑯拥牡览?,log_start和log_size的處理方式也是一樣。這實際上是一種優(yōu)化的時機,本書將在結(jié)束對函數(shù)的介紹之后再對它進行詳細討論。 25879:消息被分為若干行,這當(dāng)然要使用新行標(biāo)志符來進行分割。一旦內(nèi)核檢測到新行標(biāo)志符,就寫入一個完整行,從而內(nèi)循環(huán)的執(zhí)行也可以提前終止。 25884:在這里我們先不考慮內(nèi)部循環(huán)是否會提前退出,從msg到p的字符序列是專門提供給控制臺使用的(這種字符序列我稱之為行,但是不要忘了,這里的行可能并不意味著新行終止,因為buf也許還沒有終止)。如果該行的日志等級高于系統(tǒng)控制臺定義的日志等級,而且當(dāng)前又有控制臺可供打印,那么就能夠正確打印該行。(記住,printk可能在所有控制臺打開之前就已經(jīng)被調(diào)用過了。) 如果在該消息塊中沒有發(fā)現(xiàn)日志等級序列,并且在前面的printk調(diào)用中也沒有對msg_level賦值,那么本行中的msg_level就是1。由于console_loglevel總不小于1(除非root通過sysctl接口鎖定),于是總是可以打印這些行。 25886:本行應(yīng)該能夠被打印。printk通過遍歷打開的控制臺驅(qū)動鏈表告知每一個控制臺驅(qū)動去打印當(dāng)前行設(shè)備驅(qū)動在本書的討論范圍之外,因此,控制臺驅(qū)動代碼則并不包含在內(nèi))。 25888:請注意這里消息文本的開頭使用的是msg而不是p,這樣就在沒有日志等級序列的情況下寫入消息了。然而,日志等級序列已經(jīng)被存儲到log_buf緩沖器中了。這樣就使后來能夠訪問log_buf以獲取消息日志等級的代碼(請參看25998行),不會再產(chǎn)生顯示混
點擊復(fù)制文檔內(nèi)容
化學(xué)相關(guān)推薦
文庫吧 www.dybbs8.com
備案圖片鄂ICP備17016276號-1