網(wǎng)上有很多關(guān)于pos機(jī)操作手冊(cè)分解,Linux 內(nèi)核編碼風(fēng)格的知識(shí),也有很多人為大家解答關(guān)于pos機(jī)操作手冊(cè)分解的問(wèn)題,今天pos機(jī)之家(www.shineka.com)為大家整理了關(guān)于這方面的知識(shí),讓我們一起來(lái)看下吧!
本文目錄一覽:
pos機(jī)操作手冊(cè)分解
本文是翻譯自內(nèi)核源碼的Documentation/process/coding-style.rst(最后的提交時(shí)間是 2021 年 2 月 12 日)。
網(wǎng)上雖然已經(jīng)有很多人做了很好的翻譯,但都是很早的版本,所以我還是想自己翻譯一次最新的編碼風(fēng)格(當(dāng)然,我借助了某歌翻譯),讓自己加深印象。
如果發(fā)現(xiàn)有看不懂的地方,不用懷疑,是我翻譯得不對(duì),請(qǐng)告訴我:lioneie@163.com。
這是一個(gè)簡(jiǎn)短的文檔,描述了 Linux 內(nèi)核的首選編碼風(fēng)格。 編碼風(fēng)格非常個(gè)人化,我不會(huì)對(duì)任何人強(qiáng)加我的見(jiàn)解,但這是我必須要維護(hù)的代碼(指 linux 內(nèi)核代碼)的編碼風(fēng)格,對(duì)于其他項(xiàng)目代碼,我也希望使用它。 寫(xiě)內(nèi)核代碼時(shí)請(qǐng)至少考慮本文提出的風(fēng)格。
首先,我建議打印出 GNU 編碼標(biāo)準(zhǔn),然后不要閱讀。 燒掉它們,這是一個(gè)很棒的象征性動(dòng)作。
無(wú)論如何,我們開(kāi)始:
1) 縮進(jìn)制表符(Tab鍵)是 8 個(gè)字符,因此縮進(jìn)也是 8 個(gè)字符。 有一些異端做法試圖使制表符變成 4 個(gè)(甚至 2 個(gè)?。┳址?,這類似于嘗試將 PI 的值定義為 3。
理由:縮進(jìn)的目的是明確定義控制塊的開(kāi)始和結(jié)束位置。 特別是當(dāng)你連續(xù)看了 20 個(gè)小時(shí)的屏幕后,如果縮進(jìn)較大則作用更大(指更容易分辨縮進(jìn))。
現(xiàn)在,有些人會(huì)聲稱具有 8 個(gè)字符的縮進(jìn)會(huì)使代碼向右移得太遠(yuǎn),并使得在 80 個(gè)字符的終端屏幕上難以閱讀。 答案是,如果你需要三個(gè)以上的縮進(jìn)級(jí)別,那么無(wú)論如何你的代碼有問(wèn)題了,應(yīng)該修復(fù)程序。
簡(jiǎn)而言之,8 字符縮進(jìn)使內(nèi)容更易于閱讀,并具有在嵌套函數(shù)太深時(shí)發(fā)出警告的作用。 注意該警告。
緩解 switch語(yǔ)句中多個(gè)縮進(jìn)級(jí)別的首選方法是在同一列中對(duì)齊switch及其從屬case標(biāo)簽,而不是對(duì)case標(biāo)簽進(jìn)行兩次縮進(jìn)。 例如:
switch (suffix) {case \'G\':case \'g\':mem
除非要隱藏某些內(nèi)容,否則不要在一行上放置多個(gè)語(yǔ)句:
if (condition) do_this; do_something_everytime;
不要使用逗號(hào)來(lái)避免使用花括號(hào):
if (condition)do_this, do_that;
始終對(duì)多個(gè)語(yǔ)句使用花括號(hào):
if (condition) {do_this;do_that;}
也不要將多個(gè)賦值語(yǔ)句放在一行上。 內(nèi)核編碼風(fēng)格非常簡(jiǎn)單。 避免使用棘手的表達(dá)式。
除了注釋,文檔和 Kconfig 外,空格都不用于縮進(jìn),前面的例子是故意的。
選用一個(gè)好的編輯器,不要在行尾留空格。
2) 把長(zhǎng)的行和字符串打散編碼風(fēng)格是關(guān)于使用通用工具來(lái)維持可讀性和可維護(hù)性。
單行長(zhǎng)度的首選限制是 80 列。
長(zhǎng)度超過(guò) 80 列的語(yǔ)句應(yīng)分為合理的片段,除非超過(guò) 80 列會(huì)顯著提高可讀性且不會(huì)隱藏信息。
后面的片段應(yīng)該短于原來(lái)的語(yǔ)句,并且基本上位于靠右放置。 一個(gè)典型的例子是將后面的片段與函數(shù)左括號(hào)對(duì)齊。
這些相同的規(guī)則適用于帶有長(zhǎng)參數(shù)列表的函數(shù)頭,如下所示:
/* 注意:這個(gè)例子是我(陳孝松)寫(xiě)的 */void func(int a, int b, int c, int d, int e, int f, int g, int h, int i int j, int k){...}
但是,切勿破壞諸如 printk消息之類的用戶可見(jiàn)的字符串,因?yàn)檫@會(huì)破壞grep為它們顯示的功能。
3) 大括號(hào)和空格的放置3.1) 大括號(hào)C 樣式中經(jīng)常出現(xiàn)的另一個(gè)問(wèn)題是大括號(hào)的位置。 與縮進(jìn)尺寸不同,沒(méi)有什么技術(shù)上的原因可以選擇一種放置策略而不是另一種,但是正如 Kernighan 和 Ritchie 向我們展示的,首選方式是將起始大括號(hào)放在行尾,然后將結(jié)束大括號(hào)放在行首,所以:
if (x is true) {we do y}
這適用于所有非函數(shù)語(yǔ)句塊(if、switch、for、while、do)。 例如:
switch (action) {case KOBJ_ADD:return "add";case KOBJ_REMOVE:return "remove";case KOBJ_CHANGE:return "change";default:return NULL;}
但是,有一個(gè)特殊情況,即函數(shù):在下一行的開(kāi)頭放置起始大括號(hào),因此:
int function(int x){body of function}
全世界的異端人士都聲稱這種不一致性是……嗯……是不一致的,但是所有思維健全的人都知道(a)K&R 是正確的,(b)K&R 是正確的。 此外,函數(shù)是很特殊的(在 C 語(yǔ)言中函數(shù)是不能嵌套的)。
陳孝松注:K & R:《The C Programming Language》一書(shū)的作者 Kernighan 和 Ritchie。
請(qǐng)注意,結(jié)束大括號(hào)單獨(dú)一行,除非在其后跟著同一條語(yǔ)句的剩余部分,也就是do語(yǔ)句中的while,或者if語(yǔ)句中的else,例如:
do {body of do-loop} while (condition);
還有
if (x == y) {..} else if (x > y) {...} else {....}
理由:K&R。
另外,請(qǐng)注意,這種大括號(hào)的放置方式還可以最大程度地減少空(或幾乎空)行的數(shù)量,而不會(huì)損失任何可讀性。 因此,由于屏幕上的新行是不可再生資源(請(qǐng)考慮 25 行的終端屏幕),因此你有更多的空行可以放置注釋。
在單個(gè)語(yǔ)句使用的地方,不用加不必要的大括號(hào)。
if (condition)action;
和
if (condition)do_this;elsedo_that;
如果條件語(yǔ)句的只有一個(gè)分支是單個(gè)語(yǔ)句,則不適用; 這時(shí)請(qǐng)?jiān)趦蓚€(gè)分支中都使用大括號(hào):
if (condition) {do_this;do_that;} else {otherwise;}
另外,當(dāng)循環(huán)中包含了多個(gè)單行的簡(jiǎn)單語(yǔ)句時(shí),請(qǐng)使用大括號(hào):
while (condition) {if (test)do_something;}3.2) 空格
Linux 內(nèi)核使用空格的方式(主要)取決于是用于函數(shù)還是關(guān)鍵字。 (大多數(shù))在關(guān)鍵字之后加一個(gè)空格。 值得注意的例外是 sizeof、typeof、alignof和attribute,它們看起來(lái)有點(diǎn)像函數(shù)(并且在 Linux 中通常與小括號(hào)一起使用,盡管它們?cè)谡Z(yǔ)言中不是必需的,例如:struct fileinfo info;聲明后的sizeof info) 。
因此,在這些關(guān)鍵字之后加一個(gè)空格:
if, switch, case, for, do, while
但不能在 sizeof、typeof、alignof或attribute之后加空格。 例如:
s = sizeof(struct file);
不要在小括號(hào)的表達(dá)式兩側(cè)(內(nèi)部)添加空格。 這是反例:
s = sizeof( struct file );
在聲明指針數(shù)據(jù)類型或返回指針類型的函數(shù)時(shí),*的首選用法是與數(shù)據(jù)名稱或函數(shù)名稱相鄰,而不與類型名稱相鄰。 例子:
char *linux_banner;unsigned long long memparse(char *ptr, char **retptr);char *match_strdup(substring_t *s);
在大多數(shù)二元和三元運(yùn)算符的兩側(cè)(每邊)使用一個(gè)空格,例如以下任意一個(gè):
= + - * / % | & ^ = == != ? :
但一元運(yùn)算符后不要加空格:
& * + - ~ ! sizeof typeof alignof __attribute__ defined
后綴遞增和遞減一元運(yùn)算符前沒(méi)有空格:
++ --
前綴遞增和遞減一元運(yùn)算符后沒(méi)有空格:
++ --
.和->結(jié)構(gòu)成員操作符前后沒(méi)有空格。
不要在行尾留空格。 某些具有“智能”縮進(jìn)的編輯器將在適當(dāng)?shù)那闆r下在新行的開(kāi)頭插入空格,因此你可以立即開(kāi)始鍵入下一行代碼。 但是,如果沒(méi)有在這一行輸入代碼,則某些編輯器不會(huì)刪除空格,就像你留下一個(gè)只有空白的行。 結(jié)果,行尾帶有空格的行就產(chǎn)生了。
當(dāng) git 發(fā)現(xiàn)補(bǔ)丁包含了行尾空格的時(shí)候會(huì)警告你,并且可以有選擇地為你去掉尾隨空格; 但是,如果打一系列補(bǔ)丁,這樣做會(huì)導(dǎo)致后面的補(bǔ)丁失敗,因?yàn)槟愀淖兞搜a(bǔ)丁的上下文。
4) 命名C 是一種簡(jiǎn)樸的語(yǔ)言,你的命名也應(yīng)是這樣。 與 Modula-2 和 Pascal 程序員不同,C 程序員不會(huì)使用諸如 ThisVariableIsATemporaryCounter之類的可愛(ài)名稱。 C 程序員將該變量命名為tmp,該變量更容易編寫(xiě),而且更容易理解。
但是,雖然不贊成使用大小寫(xiě)混合的名稱,但全局變量還是需要使用具備描述性的名稱。 把全局函數(shù)命名為 foo是一種難以饒恕的錯(cuò)誤。
全局變量(僅在確實(shí)需要它們時(shí)才使用)與全局函數(shù)一樣,都需要具有描述性名稱。 如果你有一個(gè)統(tǒng)計(jì)活動(dòng)用戶數(shù)量的函數(shù),則應(yīng)命名為count_active_users或類似名稱,而不應(yīng)該命名為cntusr。
在函數(shù)名中包含函數(shù)類型(所謂的匈牙利命名法)是愚蠢的 - 編譯器知道類型而且能夠檢查類型,這樣做只能把程序員弄糊涂。
陳孝松注:這里曾經(jīng)還有一句話:難怪微軟總是制造出有問(wèn)題的程序。在 2021 年 2 月 12 日這句話被刪除了。
局部變量名稱應(yīng)簡(jiǎn)短明了。 如果你有一些隨機(jī)整數(shù)循環(huán)計(jì)數(shù)器,則應(yīng)命名為i。 如果沒(méi)有可能被誤解,則命名為loop_counter是無(wú)用的。 同樣,tmp可以用來(lái)命名任意類型的臨時(shí)變量。
如果你害怕混淆你的局部變量名稱,那么你會(huì)遇到另一個(gè)問(wèn)題,稱為叫做函數(shù)增長(zhǎng)荷爾蒙失衡綜合癥function-growth-hormone-imbalance syndrome。 請(qǐng)參見(jiàn)第 6 章(函數(shù))。
對(duì)于符號(hào)名稱和文檔,請(qǐng)避免引入“主/從”(或獨(dú)立于“主”的“從”)和“黑名單/白名單”的新用法。
推薦的“主master/從slave”替代方案是:
\'{primary,main} / {secondary,replica,subordinate}\' \'{initiator,requester} / {target,responder}\' \'{controller,host} / {device,worker,proxy}\' \'leader / follower\' \'director / performer\'
推薦的“黑名單blacklist/白名單whitelist”替代方案是:
\'denylist / allowlist\' \'blocklist / passlist\'
引入新用法的例外情況是維護(hù)用戶空間 ABI/API,或者更新用于強(qiáng)制使用這些術(shù)語(yǔ)的現(xiàn)有(截至 2020年)硬件或協(xié)議規(guī)范的代碼。 對(duì)于新規(guī)范,盡可能將術(shù)語(yǔ)的規(guī)范用法轉(zhuǎn)換為內(nèi)核編碼標(biāo)準(zhǔn)。
5) typedef請(qǐng)不要使用 vps_t之類的東西。 對(duì)結(jié)構(gòu)體和指針使用typedef是錯(cuò)誤的。 當(dāng)你看到
vps_t a;
出現(xiàn)在代碼中,是什么意思? 相反,如果這樣
struct virtual_container *a;
你就知道 a是什么了。
許多人認(rèn)為 typedef有助于提高可讀性。 不是這樣的。它們僅在下列情況下有用:
完全不透明的對(duì)象(這時(shí) typedef主動(dòng)用于隱藏對(duì)象是什么)。例如:pte_t等不透明對(duì)象,你只能使用適當(dāng)?shù)脑L函數(shù)來(lái)訪問(wèn)他們。注意:不透明和“訪問(wèn)函數(shù)”本身并不好。 之所以使用諸如pte_t等類型的原因在于真的是完全沒(méi)有任何共用的可訪問(wèn)信息。清楚的整數(shù)類型,這層抽象有助于避免混淆到底是int還是long。u8/u16/u32是沒(méi)問(wèn)題的typedef,不過(guò)它們更符合情況 (4) 而不是這里。再次注意:需要有一個(gè)原因。 如果某個(gè)變量類型是unsigned long,則沒(méi)有必要這樣typedef unsigned long myflags_t; 但是,如果有明確的原因,比如有些情況可能是unsigned int,而在其他情況下可能是unsigned long,那么一定要繼續(xù)使用typedef。當(dāng)你使用 sparse從字面上創(chuàng)建用于類型檢查的新類型時(shí)。陳孝松注:sparse誕生于 2004 年,是由 Linus 開(kāi)發(fā)的,目的就是提供一個(gè)靜態(tài)檢查代碼的工具, 從而減少 Linux 內(nèi)核的隱患。在某些特殊情況下,與標(biāo)準(zhǔn) C99 類型相同的新類型。 盡管眼睛和大腦只需要很短的時(shí)間就習(xí)慣了uint32_t這樣的標(biāo)準(zhǔn)類型,但是仍然有人反對(duì)使用它們。因此,Linux 特有的等同于標(biāo)準(zhǔn)類型的u8/u16/u32/u64類型和它們的有符號(hào)類型是被允許的 -- 盡管它們?cè)谀阕约旱男麓a中不是必需的。編輯已使用了某類型集的現(xiàn)有代碼時(shí),應(yīng)遵循該代碼中的現(xiàn)有選擇。可以在用戶空間中安全使用的類型。 在用戶空間可見(jiàn)的某些結(jié)構(gòu)體中,我們不能要求C99類型而且不能用上面提到的u32類型。 因此,我們?cè)谂c用戶空間共享的所有結(jié)構(gòu)體中使用__u32和類似的類型。
也許還有其他情況,但是基本的規(guī)則應(yīng)該永遠(yuǎn)不要使用typedef,除非你可以明確符合上述規(guī)則中的一個(gè)。
通常,指針或結(jié)構(gòu)體中的元素可以合理被訪問(wèn)到,那么就不應(yīng)該是 typedef。
6) 函數(shù)函數(shù)應(yīng)該簡(jiǎn)短而漂亮,并且只完成一件事。 它們應(yīng)該一屏或兩屏顯示完(眾所周知,ISO/ANSI屏幕大小為80x24),并且可以做一件事并且做好。
函數(shù)的最大長(zhǎng)度與該函數(shù)的復(fù)雜度和縮進(jìn)級(jí)數(shù)成反比。 因此,如果你有一個(gè)理論上很簡(jiǎn)單的函數(shù),只是一個(gè)很長(zhǎng)(但很簡(jiǎn)單)的 case語(yǔ)句,那么需要在每個(gè)case語(yǔ)句做很多小事情,這樣的函數(shù)可以很長(zhǎng)。
但是,如果功能很復(fù)雜,并且懷疑天分不是很高的一年級(jí)高中生甚至可能根本不了解該函數(shù)的功能,則應(yīng)該更加嚴(yán)格地遵守長(zhǎng)度限制。 使用具有描述性名稱的輔助函數(shù)(如果你認(rèn)為它們的性能至關(guān)重要,則可以讓編譯器內(nèi)聯(lián)它們,效果比寫(xiě)一個(gè)復(fù)雜的函數(shù)要好)。
函數(shù)的另一種衡量標(biāo)準(zhǔn)是局部變量的個(gè)數(shù)。 它們不應(yīng)超過(guò) 5-10 個(gè),否則函數(shù)就有問(wèn)題了。 重新考慮一下函數(shù)的實(shí)現(xiàn),并將其拆分為更小的函數(shù)。 人腦通常可以輕松地跟蹤約7種不同的事物,如果更多的話就會(huì)變得混亂。 即使你再聰明,你也可能會(huì)記不清兩個(gè)星期前做過(guò)的事。
在源文件中,用一個(gè)空行分隔函數(shù)。 如果導(dǎo)出了該函數(shù),則該函數(shù)的 EXPORT宏應(yīng)緊跟在結(jié)束大括號(hào)的下一行。 例如:
int system_is_up(void){return system_state == SYSTEM_RUNNING;}EXPORT_SYMBOL(system_is_up);
在函數(shù)原型中,最好包含參數(shù)名稱和其數(shù)據(jù)類型。 盡管 C 語(yǔ)言沒(méi)要求必須這樣做,但在 Linux 中提倡這樣做,因?yàn)檫@樣可以很簡(jiǎn)單的給讀者提供更多的有價(jià)值的信息。
請(qǐng)勿將 extern關(guān)鍵字與函數(shù)原型一起使用,因?yàn)檫@會(huì)使行更長(zhǎng),并且并非絕對(duì)必要。
7) 集中的函數(shù)退出途徑盡管某些人認(rèn)為已過(guò)時(shí),但是 goto語(yǔ)句的等價(jià)物經(jīng)常以無(wú)條件跳轉(zhuǎn)指令的形式被編譯器使用。
陳孝松注:equivalent of the goto statement翻譯為goto語(yǔ)句的等價(jià)物 似乎不大通順,如果你有更好的翻譯請(qǐng)告訴我。
當(dāng)函數(shù)從多個(gè)位置退出并且必須執(zhí)行一些常規(guī)工作(例如清理)時(shí), goto語(yǔ)句會(huì)派上用場(chǎng)。 如果不需要清理,則直接返回。
選擇標(biāo)簽名稱,要能說(shuō)明 goto的功能或goto存在的原因。 如果goto中轉(zhuǎn)到釋放buffer的地方,則標(biāo)簽名字為out_free_buffer:將是一個(gè)很好的例子。 避免使用諸如err1:和err2:之類的 GW-BASIC 名稱,因?yàn)槿绻闾砑踊騽h除出口路徑,則必須重新編號(hào)它們,并且它們無(wú)論如何都會(huì)使正確性難以驗(yàn)證。
陳孝松注:GW-BASIC 是 BASIC 的一個(gè)方言版本,這個(gè)版本的 BASIC 最早是微軟在 1984 年為康柏(2002 年康柏公司被惠普公司收購(gòu))開(kāi)發(fā)的。
使用 goto的基本原理是:
無(wú)條件語(yǔ)句更易于理解和跟蹤嵌套程度減少防止在進(jìn)行修改時(shí)忘記更新某個(gè)單獨(dú)的退出點(diǎn)而導(dǎo)致錯(cuò)誤節(jié)省了編譯器的工作,以優(yōu)化冗余代碼 :wink:int fun(int a){int result = 0;char *buffer;buffer = kmalloc(SIZE, GFP_KERNEL);if (!buffer)return -ENOMEM;if (condition1) {while (loop1) {...}result = 1;goto out_free_buffer;}...out_free_buffer:kfree(buffer);return result;}
要注意的一種常見(jiàn)錯(cuò)誤是 one err bugs,如下所示:
err:kfree(foo->bar);kfree(foo);return ret;
此代碼中的錯(cuò)誤是在某些出口路徑上 foo為NULL。 通常,此問(wèn)題的解決方法是將其分為兩個(gè)錯(cuò)誤標(biāo)簽err_free_bar:和err_free_foo::
err_free_bar:kfree(foo->bar); err_free_foo:kfree(foo);return ret;
理想情況下,你應(yīng)該模擬錯(cuò)誤以測(cè)試所有出口路徑。
8) 注釋注釋是好的,但也有過(guò)度注釋的危險(xiǎn)。 永遠(yuǎn)不要嘗試在注釋中解釋代碼是如何工作的:更好 的做法是讓別人一看代碼就可以明白,解釋寫(xiě)的很差的代碼是浪費(fèi)時(shí)間。
通常,你希望你的注釋告訴別人你的代碼做了什么,而不是怎么做的。 另外,請(qǐng)盡量避免在函數(shù)體內(nèi)添加注釋:如果函數(shù)太復(fù)雜以至于需要單獨(dú)注釋其中的某些部分,則可能應(yīng)該回到第6章看一看。 你可以加一些小注釋,注明或警告某些特別聰明(或糟糕)的做法,但不要加太多。 你應(yīng)該將注釋放在函數(shù)的頭部,告訴人們它做了什么,以及這么做的原因。
在注釋內(nèi)核 API 函數(shù)時(shí),請(qǐng)使用 kernel-doc 格式。詳細(xì)信息請(qǐng)參考:Documentation/doc-guide/ 和scripts/kernel-doc。
長(zhǎng)(多行)注釋的首選風(fēng)格是:
/* * This is the preferred style for multi-line * comments in the Linux kernel source code. * Please use it consistently. * * Description: A column of asterisks on the left side, * with beginning and ending almost-blank lines. *//* * 這是Linux內(nèi)核源代碼中多行注釋的首選風(fēng)格。 * 請(qǐng)始終使用這種風(fēng)格。 * * 說(shuō)明:左側(cè)是星號(hào)列,開(kāi)始和結(jié)束的行幾乎是空白的。 */
對(duì)于 net/和drivers/net/中的文件,長(zhǎng)(多行)注釋的首選風(fēng)格略有不同。
/* The preferred comment style for files in net/ and drivers/net * looks like this. * * It is nearly the same as the generally preferred comment style, * but there is no initial almost-blank line. *//* net/和drivers/net/中的文件的首選注釋風(fēng)格如下所示。 * * 它幾乎與一般的首選注釋風(fēng)格相同,但是開(kāi)始的行不是幾乎空白的。 */
注釋數(shù)據(jù)(無(wú)論是基本類型還是衍生類型)也很重要。 為此,每行僅使用一個(gè)數(shù)據(jù)聲明(不要使用逗號(hào)來(lái)一次聲明多個(gè)數(shù)據(jù))。 這為你留出了對(duì)每個(gè)數(shù)據(jù)寫(xiě)一段小注釋的空間,以解釋其用途。
9) 你已經(jīng)把事情弄糟了這沒(méi)什么,我們都是這樣。 長(zhǎng)期的 Unix 用戶幫手可能已經(jīng)告訴你 GNU emacs會(huì)自動(dòng)為你格式化 C 源代碼,并且你已經(jīng)注意到,確實(shí)可以這樣做,但是它使用的默認(rèn)值并不理想(實(shí)際上 ,它們比隨機(jī)輸入更糟糕-無(wú)限數(shù)量的猴子在 GNUemacs中輸入永遠(yuǎn)不會(huì)成為一個(gè)好的程序)。
因此,你要么放棄 GNU emacs,要么改變它讓它使用更合理的設(shè)定。 為此,你可以將以下內(nèi)容粘貼到.emacs文件中:
(defun c-lineup-arglist-tabs-only (ignored) "Line up argument lists by tabs, not spaces" (let* ((anchor (c-langelem-pos c-syntactic-element)) (column (c-langelem-2nd-pos c-syntactic-element)) (offset (- (1+ column) anchor)) (steps (floor offset c-basic-offset))) (* (max steps 1) c-basic-offset))) (dir-locals-set-class-variables \'linux-kernel \'((c-mode . ( (c-basic-offset . 8) (c-label-minimum-indentation . 0) (c-offsets-alist . ( (arglist-close . c-lineup-arglist-tabs-only) (arglist-cont-nonempty . (c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)) (arglist-intro . +) (brace-list-intro . +) (c . c-lineup-C-comments) (case-label . 0) (comment-intro . c-lineup-comment) (cpp-define-intro . +) (cpp-macro . -1000) (cpp-macro-cont . +) (defun-block-intro . +) (else-clause . 0) (func-decl-cont . +) (inclass . +) (inher-cont . c-lineup-multi-inher) (knr-argdecl-intro . 0) (label . -1000) (statement . 0) (statement-block-intro . +) (statement-case-intro . +) (statement-cont . +) (substatement . +) )) (indent-tabs-mode . t) (show-trailing-whitespace . t) )))) (dir-locals-set-directory-class (expand-file-name "~/src/linux-trees") \'linux-kernel)
這將使 emacs更好地配合~/src/linux-trees下 C 文件的內(nèi)核編碼風(fēng)格。
但是,即使你無(wú)法使 emacs進(jìn)行合理的格式化,也并不意味著你失去了一切:還可以用indent。
現(xiàn)在,再次,GNU indent具有與 GNUemacs有問(wèn)題的設(shè)定,所以你需要給它一些命令選項(xiàng)。 但是,這并不算太糟,因?yàn)榧词?GNUindent的作者也認(rèn)同 K&R 的權(quán)威(GNU 的人并不是壞人,他們?cè)诖藛?wèn)題上受到嚴(yán)重誤導(dǎo)),所以你只需給indent指定選項(xiàng)-kr -i8( 代表 “K&R, 8 character indents”),或使用scripts/Lindent(以最時(shí)髦的方式縮進(jìn))。
indent有很多選項(xiàng),尤其是重新格式化注釋時(shí),你可能需要看一下手冊(cè)頁(yè)。 但是請(qǐng)記?。篿ndent不能修正壞的編程習(xí)慣。
請(qǐng)注意,你還可以使用 clang-format工具來(lái)幫助你遵循這些規(guī)則,快速自動(dòng)地重新格式化部分代碼,并查看完整文件,以發(fā)現(xiàn)編碼風(fēng)格錯(cuò)誤,錯(cuò)別字和可能的改進(jìn)。 它對(duì)于排序#includes,對(duì)齊變量/宏,重排文本和其他類似任務(wù)也很方便。更多詳細(xì)信息,請(qǐng)參見(jiàn)文件Documentation/process/clang-format.rst 。
10) Kconfig 配置文件對(duì)于整個(gè)源代碼樹(shù)中的所有 Kconfig* 配置文件,縮進(jìn)有些不同。 緊挨 在 config定義下面的行縮進(jìn)一個(gè)制表符,幫助信息則再多縮 2 個(gè)空格。 例子:
config AUDITbool "Auditing support"depends on NEThelp Enable auditing infrastructure that can be used with another kernel subsystem, such as SELinux (which requires this for logging of avc messages output). Does not do system-call auditing without CONFIG_AUDITSYSCALL.
嚴(yán)重危險(xiǎn)的功能(例如對(duì)某些文件系統(tǒng)的寫(xiě)支持)應(yīng)在其提示字符串中突出顯示這一點(diǎn):
config ADFS_FS_RWbool "ADFS write support (DANGEROUS)"depends on ADFS_FS...
有關(guān)配置文件的完整文檔,請(qǐng)參閱文件 Documentation/kbuild/kconfig-language.rst。
11) 數(shù)據(jù)結(jié)構(gòu)在創(chuàng)建和銷毀它們的單線程環(huán)境之外具有可見(jiàn)性的數(shù)據(jù)結(jié)構(gòu)應(yīng)始終具有引用計(jì)數(shù)。 在內(nèi)核中,不存在垃圾回收(并且在內(nèi)核之外,垃圾回收是緩慢且效率低下的),這意味著你絕對(duì)必須引用計(jì)數(shù)所有使用情況。
引用計(jì)數(shù)意味著你可以避免上鎖,并允許多個(gè)用戶并行訪問(wèn)數(shù)據(jù)結(jié)構(gòu) - 不必?fù)?dān)心這個(gè)數(shù)據(jù)結(jié)構(gòu)僅僅因?yàn)闀簳r(shí)不被使用就消失了,因?yàn)樾菝咭欢螘r(shí)間或做了其他事情。
請(qǐng)注意,上鎖不能代替引用計(jì)數(shù)。 上鎖用于保持?jǐn)?shù)據(jù)結(jié)構(gòu)的一致性,而引用計(jì)數(shù)是一種內(nèi)存管理技術(shù)。 通常兩者都是必需的,并且不要相互混淆。
當(dāng)存在不同 classes的用戶時(shí),許多數(shù)據(jù)結(jié)構(gòu)的確可以具有兩級(jí)的引用計(jì)數(shù)。 子類計(jì)數(shù)器對(duì)子類用戶的數(shù)量進(jìn)行計(jì)數(shù),并且當(dāng)子類計(jì)數(shù)器變?yōu)榱銜r(shí),全局計(jì)數(shù)器減一。
這種多級(jí)引用計(jì)數(shù)(multi-level-reference-counting)的示例可以在內(nèi)存管理(struct mm_struct:mm_users和mm_count)以及文件系統(tǒng)代碼(struct super_block:s_count和s_active)中找到。
請(qǐng)記住:如果另一個(gè)線程可以找到你的數(shù)據(jù)結(jié)構(gòu),并且你沒(méi)有對(duì)其的引用計(jì)數(shù),則幾乎可以肯定有一個(gè)bug。
12) 宏、枚舉和 RTL定義常量的宏名稱和枚舉中的標(biāo)簽均使用大寫(xiě)字母。
#define CONSTANT 0x12345
定義多個(gè)相關(guān)常量時(shí),最好使用枚舉。
宏的名字請(qǐng)用大寫(xiě)字母,但是類似于函數(shù)的宏可以用小寫(xiě)字母命名。
通常,內(nèi)聯(lián)函數(shù)比類似于函數(shù)的宏更可取。
具有多個(gè)語(yǔ)句的宏應(yīng)包含在 do-while語(yǔ)句塊中:
#define macrofun(a, b, c)\\do {\\if (a == 5)\\do_this(b, c);\\} while (0)
使用宏時(shí)應(yīng)避免的事情:
影響控制流程的宏:#define FOO(x) \\ do { \\ if (blah(x) 非常不好。 它看起來(lái)像一個(gè)函數(shù),但是會(huì)導(dǎo)致調(diào)用它的函數(shù)退出。不要打亂讀者大腦里的語(yǔ)法分析器。依賴于一個(gè)固定名字的本地變量的宏
#define FOO(val) bar(index, val) 也許看起來(lái)不錯(cuò),但是當(dāng)人們閱讀代碼時(shí),它看起來(lái)很混亂,并且很容易因不相關(guān)的更改而被破壞。帶參數(shù)的宏作為左值:FOO(x) = y;,如果有人將FOO變成一個(gè)內(nèi)聯(lián)函數(shù)就會(huì)出錯(cuò)。忘記優(yōu)先級(jí):使用表達(dá)式定義常量的宏必須將表達(dá)式用括號(hào)括起來(lái)。 帶參數(shù)的宏也要注意此類問(wèn)題。
#define CONSTANT 0x4000 #define CONSTEXP (CONSTANT | 3) 在類似于函數(shù)的宏中定義局部變量時(shí),名稱空間沖突:
#define FOO(x) \\ ({ \\ typeof(x) ret; \\ ret = calc_ret(x); \\ (ret); \\ }) ret是局部變量的通用名稱 -__foo_ret與現(xiàn)有變量發(fā)生沖突的可能性較小。cpp 手冊(cè)詳盡地處理了宏。 gcc internals 手冊(cè)還介紹了RTL,內(nèi)核里的匯編語(yǔ)言經(jīng)常用到RTL。陳孝松注: RTL:寄存器傳遞語(yǔ)言register transfer language,又譯為暫存器轉(zhuǎn)換語(yǔ)言、寄存器轉(zhuǎn)換語(yǔ)言,一種中間語(yǔ)言,使用于編譯器中。13) 打印內(nèi)核消息
內(nèi)核開(kāi)發(fā)者應(yīng)該是受過(guò)良好教育的。 請(qǐng)注意內(nèi)核消息的拼寫(xiě),以給人留下深刻的印象。 不要使用不正確的收縮,例如 dont; 而要使用do not或don\'t。 使消息簡(jiǎn)單、明了、無(wú)歧義。
內(nèi)核消息不必以句點(diǎn)(即點(diǎn)號(hào))終止。
括號(hào)中的數(shù)字(%d)沒(méi)有任何價(jià)值,應(yīng)避免使用。
中有許多驅(qū)動(dòng)模型診斷宏driver model diagnostic macros,你應(yīng)使用這些宏來(lái)確保消息與正確的設(shè)備和驅(qū)動(dòng)程序匹配,并以正確的級(jí)別進(jìn)行標(biāo)記: dev_err、dev_warn、dev_info等。 對(duì)于與特定設(shè)備無(wú)關(guān)的消息, 定義了pr_notice、pr_info、pr_warn、pr_err等。
寫(xiě)出好的調(diào)試消息可以是一個(gè)很大的挑戰(zhàn)。 一旦有了它們,它們將為遠(yuǎn)程故障排除提供巨大幫助。 但是,調(diào)試消息的打印方式與打印其他非調(diào)試消息的方式不同。 雖然其他 pr_XXX函數(shù)無(wú)條件打印,但pr_debug不會(huì); 除非定義了DEBUG或設(shè)置了CONFIG_DYNAMIC_DEBUG,否則編譯器會(huì)忽略它。dev_dbg也是如此,并且相關(guān)的約定使用VERBOSE_DEBUG將dev_vdbg消息添加到已由DEBUG啟用的消息中。
許多子系統(tǒng)具有 Kconfig 調(diào)試選項(xiàng),可以在相應(yīng)的 Makefile 中打開(kāi) -DDEBUG。 在其他情況下,特定文件定義了#define DEBUG。 并且當(dāng)應(yīng)無(wú)條件打印調(diào)試消息時(shí)(例如,如果它已經(jīng)在與調(diào)試相關(guān)的#ifdef中),可以使用printk(KERN_DEBUG ...)。
14) 分配內(nèi)存內(nèi)核提供以下一般用途的內(nèi)存分配函數(shù):kmalloc、kzalloc、kmalloc_array、kcalloc、vmalloc、vzalloc。 請(qǐng)參閱 API 文檔以獲取有關(guān)它們的更多信息:Documentation/core-api/memory-allocation.rst 。
傳遞結(jié)構(gòu)體大小的首選形式如下:
p = kmalloc(sizeof(*p), ...);
另外一種傳遞方式中,sizeof的操作數(shù)是結(jié)構(gòu)體的名字,這樣會(huì)降低可讀性,并且可能會(huì)引 入 bug。有可能指針變量類型被改變時(shí),而對(duì)應(yīng)的傳遞給內(nèi)存分配函數(shù)的sizeof的結(jié)果不變。
陳孝松注:有可能出現(xiàn)以下情況:
int *p; /* 最開(kāi)始是 char *p, 后來(lái)修改成 int *p */ p = kmalloc(sizeof(char), ...);
強(qiáng)制轉(zhuǎn)換 void指針的返回值是多余的。 C 語(yǔ)言保證了從void指針到任何其他指針類型的轉(zhuǎn)換是沒(méi)問(wèn)題的。
分配數(shù)組的首選形式如下:
p = kmalloc_array(n, sizeof(...), ...);
分配初始化為零的數(shù)組的首選形式如下:
p = kcalloc(n, sizeof(...), ...);
兩種形式都檢查分配大小 n * sizeof(...)上的溢出,如果發(fā)生,則返回NULL。
這些通用的分配函數(shù)在不帶 __GFP_NOWARN的情況下使用時(shí),都會(huì)在失敗時(shí)發(fā)出堆棧轉(zhuǎn)儲(chǔ),因此在返回NULL時(shí)不會(huì)有其他失敗消息。
15) 內(nèi)聯(lián)弊病有一個(gè)常見(jiàn)的誤解是內(nèi)聯(lián)函數(shù)(inline)是 gcc 提供的可以讓代碼運(yùn)行更快的一個(gè)選項(xiàng)。 雖然可以適當(dāng)使用內(nèi)聯(lián)函數(shù)(例如,作為替換宏的一種方法,請(qǐng)參見(jiàn)第 12 章),不過(guò)很多情況下不是這樣。 大量使用inline關(guān)鍵字會(huì)導(dǎo)致內(nèi)核變大,這會(huì)降低整個(gè)系統(tǒng)的速度,這是因?yàn)?CPU 的 icache 占用量更大,而且會(huì)導(dǎo)致 pagecache 的可用內(nèi)存減少。 考慮一下:pagecache 未命中會(huì)導(dǎo)致磁盤查找,這很容易花費(fèi) 5 毫秒。5 毫秒的時(shí)間內(nèi) CPU 能執(zhí)行很多指令。
一個(gè)基本的原則是不要對(duì)其中包含多于 3 行代碼的函數(shù)進(jìn)行內(nèi)聯(lián)。該規(guī)則的例外情況是參數(shù)已知為編譯時(shí)常量,并且由于該常量,你確定編譯器將能夠在編譯時(shí)優(yōu)化大部分函數(shù)。 一個(gè)很好的示例就是 kmalloc內(nèi)聯(lián)函數(shù)。
人們經(jīng)常主張說(shuō),將 inline添加到static且僅使用一次的函數(shù),不會(huì)有任何損失,因?yàn)闆](méi)有什么好權(quán)衡的。 盡管從技術(shù)上講這是正確的,但是gcc能夠在沒(méi)有幫助的情況下自動(dòng)內(nèi)聯(lián)這個(gè)函數(shù),而且其他用戶可能會(huì)要求移除inline,由此而來(lái)的爭(zhēng)論會(huì)抵消inline自身的潛在價(jià)值,得不償失。
16) 函數(shù)返回值及命名函數(shù)可以返回許多不同類型的值,最常見(jiàn)的值之一是指示函數(shù)成功還是失敗的值。 這樣的值可以表示為錯(cuò)誤代碼整數(shù)(-Exxx = failure,0 = success)或是否成功的布爾值(0 = failure, non-zero = success)。
混合使用這兩種表達(dá)方式是難于發(fā)現(xiàn)的 bug 的來(lái)源。 如果 C 語(yǔ)言能嚴(yán)格區(qū)分整數(shù)和布爾值,則編譯器會(huì)為我們找到這些錯(cuò)誤……但是 C 語(yǔ)言不區(qū)分。 為防止此類錯(cuò)誤,請(qǐng)始終遵循以下約定:
如果函數(shù)的名字是一個(gè)動(dòng)作或者強(qiáng)制性的命令,則該函數(shù)應(yīng)返回錯(cuò)誤代碼整數(shù)。 如果是一個(gè)判斷,則函數(shù)應(yīng)返回表示是否“成功”的布爾值。
例如,add work是一條命令,add_work函數(shù)成功時(shí)返回0,失敗時(shí)返回-EBUSY。 同樣,PCI device present是一個(gè)判斷,如果成功找到匹配的設(shè)備,pci_dev_present函數(shù)將返回1,否則將返回0。
所有 EXPORT函數(shù)必須遵守此約定,所有公共函數(shù)也應(yīng)遵守此約定。 私有(static)函數(shù)不是必需的,但建議這樣做。
返回值是計(jì)算的實(shí)際結(jié)果而不是指示計(jì)算是否成功的函數(shù)不受此規(guī)則的約束。 通常,它們通過(guò)返回超出范圍的結(jié)果來(lái)指示失敗。 典型的例子是返回指針的函數(shù)。 他們使用 NULL或ERR_PTR機(jī)制來(lái)報(bào)告錯(cuò)誤。
17) 使用布爾Linux 內(nèi)核的布爾類型是 C99 _Bool類型的別名。 布爾值只能是0或1,并且隱式或顯式轉(zhuǎn)換為布爾值,會(huì)自動(dòng)將值轉(zhuǎn)換為true或false。 使用布爾型時(shí),!! 不需要結(jié)構(gòu)體,從而消除了一類錯(cuò)誤。
使用布爾值時(shí),應(yīng)使用 true和false定義,而不是1和0。
在適當(dāng)?shù)臅r(shí)候使用可以使用布爾返回類型的函數(shù)和堆棧變量。 鼓勵(lì)使用布爾值來(lái)提高可讀性,并且在存儲(chǔ)布爾值時(shí)通常比使用整型類型更好。
如果緩存行的布局cache line layout或值的大小size of the value很重要,請(qǐng)不要使用布爾,因?yàn)槠浯笮『蛯?duì)齊方式會(huì)根據(jù)編譯的體系結(jié)構(gòu)而變化。 針對(duì)對(duì)齊和大小進(jìn)行了優(yōu)化的結(jié)構(gòu)不應(yīng)使用布爾值。
如果結(jié)構(gòu)體具有許多 true/false,請(qǐng)考慮將它們合并到具有 1 個(gè)位成員的位域中,或使用適當(dāng)?shù)墓潭▽挾阮愋停ɡ鐄8)。
類似地,對(duì)于函數(shù)參數(shù),可以將許多 true/false值合并為單個(gè)按位的flags參數(shù),并且如果調(diào)用位置具有裸露的true/false常量,則flags通常是更具可讀性的替代方法。
否則,在結(jié)構(gòu)體和參數(shù)中限制使用布爾可以提高可讀性。
18) 不要重新發(fā)明內(nèi)核宏頭文件 include/linux/kernel.h包含許多你應(yīng)該使用的宏,而不要自己寫(xiě)一些它們的變種。 例如,如果你需要計(jì)算數(shù)組的長(zhǎng)度,請(qǐng)利用宏:
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
同樣,如果需要計(jì)算某些結(jié)構(gòu)成員的大小,請(qǐng)使用:
#define sizeof_field(t, f) (sizeof(((t*)0)->f))
如果需要,還有 min和max宏會(huì)進(jìn)行嚴(yán)格的類型檢查。 你可以自己看看那個(gè)頭文件里還定義了什么你可以拿來(lái)用的宏,如果有定義的話,你就不應(yīng)在你的代碼里自己重新定義。
19) 編輯器模式行(配置信息)和其他內(nèi)容一些編輯器可以解釋用特殊標(biāo)記表示的嵌入在源文件中的配置信息。 例如,emacs解釋標(biāo)記如下的行:
-*- mode: c -*-
或者這樣的:
/*Local Variables:compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"End:*/
vim解釋如下標(biāo)記:
/* vim:set sw=8 noet */
不要在源文件中包含任何這些。 人們具有自己的個(gè)人編輯器配置,并且你的源文件不應(yīng)覆蓋它們。 這包括用于縮進(jìn)和模式配置的標(biāo)記。 人們可以使用他們自己定制的模式,或者使用其他可以產(chǎn)生正確的縮進(jìn)的巧妙方法。
20) 內(nèi)聯(lián)匯編在特定于體系結(jié)構(gòu)的代碼中,你可能需要使用內(nèi)聯(lián)匯編與 CPU 或平臺(tái)功能交互。 必要時(shí)不要猶豫。 但是,當(dāng) C 可以完成這項(xiàng)工作時(shí),請(qǐng)不要隨意使用內(nèi)聯(lián)匯編。 你可以并且應(yīng)該在可能的情況下用 C 操作硬件。
考慮編寫(xiě)簡(jiǎn)單的輔助函數(shù),這些函數(shù)包裝內(nèi)聯(lián)匯編的常用位,而不是重復(fù)編寫(xiě)稍有變化的函數(shù)。 請(qǐng)記住,內(nèi)聯(lián)匯編可以使用 C 參數(shù)。
大型的,非平凡(大量的)的匯編函數(shù)應(yīng)放在 .S文件中,并在 C 頭文件中定義相應(yīng)的 C 原型。 匯編函數(shù)的 C 原型應(yīng)使用asmlinkage。
你可能需要將 asm語(yǔ)句標(biāo)記為volatile,以防止 GCC 在未發(fā)現(xiàn)任何副作用的情況下將其刪除。 但是,你不一定總是需要這樣做,因?yàn)檫@樣做可能會(huì)限制優(yōu)化。
當(dāng)編寫(xiě)包含多個(gè)指令的單個(gè)內(nèi)聯(lián)匯編語(yǔ)句時(shí),將每條指令放在單獨(dú)的行中,放在單獨(dú)的帶引號(hào)的字符串中,用 \\結(jié)束除最后一個(gè)字符串以外的每個(gè)字符串,以正確縮進(jìn)匯編輸出中的下一條指令:
asm ("magic %reg1, #42\\" "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */);21) 條件編譯
盡可能不要在 .c文件中使用預(yù)處理?xiàng)l件(#if、#ifdef); 這樣做會(huì)使代碼更難閱讀,邏輯也更難遵循。 而是在頭文件中使用此類條件,以定義在那些.c文件中使用的函數(shù),在#else情況下提供 no-op(無(wú)操作) stub 版本,然后從.c文件中無(wú)條件調(diào)用這些函數(shù)。 編譯器將避免為 stub calls 生成任何代碼,從而產(chǎn)生相同的結(jié)果,但是邏輯將易于遵循。
最好編譯出整個(gè)函數(shù),而不是編譯部分函數(shù)或表達(dá)式的一部分。 不要將 ifdef放入表達(dá)式中,而是將部分或全部表達(dá)式封裝成函數(shù),然后調(diào)用該函數(shù)。
如果你具有在特定配置中可能未使用的函數(shù)或變量,并且編譯器會(huì)警告其定義未使用,請(qǐng)將該定義標(biāo)記為 __maybe_unused,而不是將其包裝在預(yù)處理?xiàng)l件中。 (但是,如果函數(shù)或變量始終不使用,請(qǐng)將其刪除。)
在代碼內(nèi),在可能的情況下,使用 IS_ENABLED宏將 Kconfig 符號(hào)轉(zhuǎn)換為 C 布爾表達(dá)式,并在普通的 C 條件中使用它:
if (IS_ENABLED(CONFIG_SOMETHING)) {...}
編譯器將不斷折疊條件,并像 #ifdef一樣包含或排除代碼塊,因此這不會(huì)增加任何運(yùn)行時(shí)開(kāi)銷。 但是,這種方法仍然允許C編譯器查看塊中的代碼,并檢查其是否正確(語(yǔ)法,類型,符號(hào)引用等)。 因此,如果塊內(nèi)的代碼引用了如果不滿足條件將不存在的符號(hào),則仍然必須使用#ifdef。
在任何重要的 #if或#ifdef代碼塊的末尾(多行),在同一行的#endif后面放置注釋,并注明所使用的條件表達(dá)式。 例如:
#ifdef CONFIG_SOMETHING...#endif /* CONFIG_SOMETHING */附錄 I) 參考
The C Programming Language, Second Edition by Brian W. Kernighan and Dennis M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (paperback), 0-13-110370-9 (hardback).
The Practice of Programming by Brian W. Kernighan and Rob Pike. Addison-Wesley, Inc., 1999. ISBN 0-201-61586-X.
GNU manuals - where in compliance with K&R and this text - for cpp, gcc, gcc internals and indent, all available from https://www.gnu.org/manual/
WG14 is the international standardization working group for the programming language C, URL: http://www.open-std.org/JTC1/SC22/WG14/
Kernel :ref:process/coding-style.rst , bygreg@kroah.comat OLS 2002:http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/
以上就是關(guān)于pos機(jī)操作手冊(cè)分解,Linux 內(nèi)核編碼風(fēng)格的知識(shí),后面我們會(huì)繼續(xù)為大家整理關(guān)于pos機(jī)操作手冊(cè)分解的知識(shí),希望能夠幫助到大家!
