Linux內核編碼風(fēng)格(linus親筆撰寫(xiě))
這篇簡(jiǎn)短的文章描述了Linux內核首選的編碼風(fēng)格。編碼風(fēng)格是很個(gè)人化的東西,我不會(huì )把自己的觀(guān)點(diǎn)強加給任何人。但是,Linux內核的代碼畢竟是我必須有能力維護的,因此我寧愿它的編碼風(fēng)格是我喜歡的。請至少考慮一下這一點(diǎn)。
本文引用地址:http://dyxdggzs.com/article/257981.htm首先,我建議打印一份《GNU編碼標準》,不要閱讀它。燒掉它,它不過(guò)是象征性的姿態(tài)。
然后,請看:
第 1 章: 縮進(jìn)
Tabs(制表符)是8個(gè)字符的大小,因此縮進(jìn)也應該是8個(gè)字符的大小。有些叛逆主張試圖把縮進(jìn)變成4個(gè)(甚至是2個(gè)!)字符的長(cháng)度,這就好象試圖把PI(案,圓周率)定義成3是一樣的。
依據:縮進(jìn)背后的思想是:清楚地定義一個(gè)控制塊從哪里開(kāi)始,到哪里結束。尤其是在你連續不斷的盯了20個(gè)小時(shí)的屏幕后,如果你有大尺寸的縮進(jìn)。你將更容易發(fā)現縮進(jìn)的好處。
現在,有些人說(shuō)8個(gè)字符大小的縮進(jìn)導致代碼太偏右了,并且在一個(gè)80字符寬的終端屏幕上看著(zhù)很不舒服。對這個(gè)問(wèn)題的回答是:如果你有超過(guò)3個(gè)級別的縮進(jìn),你就有點(diǎn)犯糊涂了,應當修改你的程序。
簡(jiǎn)而言之,8個(gè)字符的縮進(jìn)使程序更易讀,而且當你把功能隱藏的太深時(shí),多層次的縮進(jìn)還會(huì )對此很直觀(guān)的給出警告。要留心這種警告信息。
第 2 章: 放置花括號
C程序中另一個(gè)要主意的就是花括號的放置。與縮進(jìn)尺寸不同的是,關(guān)于如何放置花括號沒(méi)有技術(shù)上的理由。但是,首選的方法是象先知Brain Kernighan和Dennis Ritchie展現的那樣:把左括號放在行尾,右括號放在行首。也就是:
if (x is true) {
we do y
}
然而,還有另外一種情況,就是函數:函數應當把左右括號都放在行首。也就是:
int function(int x)
{
body of function
}
叛逆的人們所在皆有。他們說(shuō),這樣會(huì )導致…嗯,不一致性(案,指函數的花括號使用與其他情況不統一)。但是所有正確思考的人都知道:(1) KR是正確的;(2) KR還是正確的。 而且,函數與別任何東西都不一樣(在C語(yǔ)言中你沒(méi)法隱藏它)。
注意,右括號所在的行不應當有其它東西,除非跟隨著(zhù)一個(gè)條件判斷。也就是do-while語(yǔ)句中的“while”和if-else語(yǔ)句中的“else”。象這樣:
do {
body of do-loop
}while (condition);
和:
if (x == y) {
..
}else if (x > y) {
...
}else {
....
}
依據: KR。
而且,注意這種花括號的放置減少了空行的數目,并沒(méi)損害可讀性。因此,當屏幕上不可以有很多空行時(shí)(試想25行的終端屏幕),你就有更多的空行來(lái)安插注釋。
第 3 章: 命名
C是一門(mén)樸素的語(yǔ)言,你使用的命名也應該這樣。與Modula-2和Pascal程序員不同,C程序員不使用諸如“ThisVariableIsATemporaryCounter”這樣“聰明”的名字。C程序員應該叫它“tmp”,這寫(xiě)起來(lái)更簡(jiǎn)單,也不會(huì )更難懂。
然而,當面對復雜情況時(shí)就有些棘手,給全局變量取一個(gè)描述性的名字是必要的。把一個(gè)全局函數叫做“foo”是一種目光短淺的行為。
全局變量(只當你確實(shí)需要時(shí)才用)應該有描述性的名字,全局函數也一樣。如果你有一個(gè)統計當前用戶(hù)個(gè)數的函數,應當把它命名為“count_active_user()”或者簡(jiǎn)單點(diǎn)些的類(lèi)似名稱(chēng),不應該命名為“cntusr()”。
把函數類(lèi)型寫(xiě)進(jìn)函數名(即所謂的“匈牙利命名法”)簡(jiǎn)直就是大腦有問(wèn)題──編譯器總是知道函數的類(lèi)型并且能加以檢查,這種命名法只會(huì )弄糊涂程序員自己。怪不得微軟總是制造充滿(mǎn)bug的程序。
局部變量的名字應該盡量短,而且說(shuō)到點(diǎn)子上。如果你有個(gè)普通的整型循環(huán)計數變量,應當命名為“i”。命名為“loop_counter”并不能帶來(lái)任何成效,如果它不被誤解的話(huà)(案,這里的言外之意是說(shuō),如果被誤解就更慘了)。與此類(lèi)似,“tmp”可以作為一個(gè)用來(lái)存儲任何類(lèi)型臨時(shí)值的變量的名字。
如果你害怕弄混淆局部變量(s)的名字,你就面臨著(zhù)另一個(gè)問(wèn)題,也叫作“函數增長(cháng)荷爾蒙失調綜合癥”。請參考下一章。
第 4 章: 函數
函數應當短而精美,而且只做一件事。它們應當占滿(mǎn)1或2個(gè)屏幕(就象我們知道的那樣,ISO/ANSI的屏幕大小是80X24),只做一件事并且把它做好。
一個(gè)函數的最大長(cháng)度與它的復雜度和縮進(jìn)級別成反比。所以,如果如果你有一個(gè)概念上簡(jiǎn)單(案,“簡(jiǎn)單”是simple而不是easy)的函數,它恰恰包含著(zhù)一個(gè)很長(cháng)的case語(yǔ)句,這樣你不得不為不同的情況準備不懂的處理,那么這樣的長(cháng)函數是沒(méi)問(wèn)題的。
然而,如果你有一個(gè)復雜的函數,你猜想一個(gè)并非天才的高一學(xué)生可能看不懂得這個(gè)函數,你就應當努力把它減縮得更接近前面提到的最大函數長(cháng)度限制??梢允褂靡恍┹o助函數,給它們取描述性的名字(如果你認為這些輔助函數的調用是性能關(guān)鍵的,可以讓編譯器把它們內聯(lián)進(jìn)來(lái),這比在單個(gè)函數內完成所有的事情通常要好些)。
對函數還存在另一個(gè)測量標準:局部變量的數目。這不該超過(guò)5到10個(gè),否則你可能會(huì )弄錯。應當重新考慮這個(gè)函數,把它分解成小片。人類(lèi)的大腦一般能同時(shí)記住7個(gè)不同的東西,超過(guò)這個(gè)數目就會(huì )犯糊涂?;蛟S你認為自己很聰明,那么請你理解一下從現在開(kāi)始的2周時(shí)間你都做什么了。
第 5 章:注釋
注釋是有用的,但過(guò)量的注釋則是有害的。不要試圖在注釋中解釋你的代碼是如何工作的:把代碼是如何工作的視為一件顯然的事情會(huì )更好些,而且,給糟糕的代碼作注釋則是在浪費時(shí)間。
通常,你愿意自己的注釋說(shuō)出代碼是做什么的,而不是如何做。還有,盡量避免在函數體內作注釋?zhuān)喝绻瘮岛軓碗s,你很可能需要分開(kāi)來(lái)注釋?zhuān)仡^到第4章去看看吧。你可以給一段代碼──漂亮的或丑陋的──作注釋以引起注意或警告,但是不要過(guò)量。取而代之,應當把注釋放在函數首部,告訴人們該函數作什么,而不是為什么這樣做。
第 6 章:你把事情弄亂了
好吧,我們來(lái)看看。很可能有長(cháng)期使用UNIX的人告訴過(guò)你,“GNU emacs”能自動(dòng)為你格式化C程序源代碼,你注意到這是真的,它確實(shí)能做到,但是缺省情況下它的用處遠遠小于期望值──鍵入無(wú)數的monkeys到GNU emacs中絕不可能造出好的程序。
因此,你可以或者刪除GNU emacs,或者對它進(jìn)行理智的配置。對于后者,可以把下面的行粘貼到你的.emacs文件中:
(defun linux-c-mode ()
C mode with adjusted defaults for use with the Linux kernel.
(interactive)
(c-mode)
(c-set-style KR)
(setq c-basic-offset 8))
這將會(huì )定義一個(gè)把C代碼弄成linux風(fēng)格的命令。當hacking一個(gè)模塊時(shí),如果你把“-*- linux-c-*-”放到了最初的兩行,這個(gè)模塊將被自動(dòng)調用。而且,如果你打算每當在/usr/src/linux下編輯源文件時(shí)就自動(dòng)調用它,也許你會(huì )把下面的命令:
(setq auto-mode-alist (cons '(/usr/src/linux.*/.*\.ch$ . linux-c-mode)
auto-mode-alist))
添加進(jìn)你的.emacs文件。
但是,即使你沒(méi)能讓emacs正確做到格式化,也并非將就此一無(wú)所有:還有“indent”程序呢。
嗯,再提醒一下,GNU indent跟GNU emacs有同樣的毛病,這就需要你給它一些命令行選項。然而,這不是很糟糕的事,因為即使是GNU indent也承認KR的權威性(GNU的人不是魔鬼,他們只是在這里太過(guò)嚴格了,以致于誤導人),所以你可以只需給indent這樣的選項:“-kr -i8”(表示“KR風(fēng)格,8個(gè)字符的縮進(jìn)”)。
“indent”程序有很多選項,特別是當為重排過(guò)的程序作注釋的時(shí)候,你需要看一下它的手冊。記?。?ldquo;indent”可不是修正糟糕程序的萬(wàn)能鑰匙。
第 7 章: 配置文件(configuration-files)
對配置選項來(lái)說(shuō)(arch/xxx/config.in和所有的Config.in文件),使用不同的縮進(jìn)風(fēng)格。
若代碼中的縮進(jìn)級別為3,配置選項就應該為2,這樣可以暗示出依賴(lài)關(guān)系。后者只是用于bool/tristate(即二態(tài)/三態(tài))的選項。對其它情況用常識就行了。舉例來(lái)說(shuō):
if $CONFIG_EXPERIMENTAL = y ; then
tristate 'Apply nitroglycerine inside the keyboard (DANGEROUS)' CONFIG_BOOM
if $CONFIG_BOOM != n ; then
bool 'Output nice messages when you explode' CONFIG_CHEER
fi
fi
通常CONFIG_EXPERIMENTAL應當在所有不穩定的選項的周?chē)霈F。所有已知會(huì )破壞數據的選項(如文件系統的實(shí)驗性的寫(xiě)支持功能)應當被標記為(DANGEROUS),其他實(shí)驗性的選項應當被標記為(EXPERIMENTAL)。
第 8 章: 數據結構
假如數據結構在其被創(chuàng )建/銷(xiāo)毀的線(xiàn)程環(huán)境(案:這里說(shuō)的線(xiàn)程是一個(gè)執行實(shí)體,可能是進(jìn)程、內核線(xiàn)程或其它)之外還具有可見(jiàn)性,那么他們都該有引用計數。
在內核中沒(méi)有垃圾收集機制(而且內核之外的垃圾收集也是緩慢而低效的),這意味著(zhù)你絕對應該為每一次使用進(jìn)行引用計數。
引用計數意味著(zhù)你可以避開(kāi)鎖,還能允許多個(gè)線(xiàn)程并行訪(fǎng)問(wèn)該數據結構──而且不用擔心僅僅因為訪(fǎng)問(wèn)數據結構的線(xiàn)程睡眠了一會(huì )兒或者干別的去了,它們就會(huì )消失。
注意,鎖不是引用計數的替代品。鎖是用來(lái)保持數據結構的一致性的,而引用計數是一種內存管理技術(shù)。通常二者都需要,而且不會(huì )彼此混淆。
確實(shí)有許多數據結構可以有兩個(gè)級別的引用計數,當使用者具有不同的“等級”(classes)時(shí)就是這樣。子等級(subclass)記錄了處于該等級的使用者個(gè)數,而且當它減到零的時(shí)候就把總體計數(global count)減一。
這種“多級引用計數”(multi-reference-counting)的一個(gè)實(shí)例可以在內存管理子系統(struct mm_struct:mm_users和mm_count)中找到,也可以在文件系統的代碼中(struct super_block:s_count和s_active)找到。
記?。喝绻硪粋€(gè)線(xiàn)程能找到你的數據結構,而你有沒(méi)對它做引用計數,那幾乎可以肯定:這是一個(gè)bug。
linux操作系統文章專(zhuān)題:linux操作系統詳解(linux不再難懂)
評論