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

正文內(nèi)容

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

2025-07-26 08:01 本頁(yè)面
 

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