Linux內核編碼規範

這是一個簡短的文檔,描述了linux內核的首選代碼風格。代碼風格是因人而異的,並且我不肯意把個人觀點強加給任何人,不過這裏所講述的是我必需要維護的代碼所遵照的風格,而且我也但願絕大多數其餘代碼也能遵照這個風格。請在寫代碼時至少考慮一下本文所述的風格。linux

首先,我建議你打印一份GNU代碼規範,而後不要讀它。燒了它,這是一個具備重大象徵性意義的動做。git

無論怎樣,如今咱們開始:程序員


第一章:縮進編程

製表符是8個字符,因此縮進也是8個字符。有些異端運動試圖將縮進變爲4(乃至2)個字符深,這幾乎至關於嘗試將圓周率的值定義爲3。vim

理由:縮進的所有意義就在於清楚的定義一個控制塊起止於何處。尤爲是當你盯着你的屏幕連續看了20小時以後,你將會發現大一點的縮進會使你更容易分辨縮進。數組

如今,有些人會抱怨8個字符的縮進會使代碼向右邊移動的太遠,在80個字符的終端屏幕上就很難讀這樣的代碼。這個問題的答案是,若是你須要3級以上的縮進,無論用何種方式你的代碼已經有問題了,應該修正你的程序。緩存

簡而言之,8個字符的縮進可讓代碼更容易閱讀,還有一個好處是當你的函數嵌套太深的時候能夠給你警告。留心這個警告。安全

在switch語句中消除多級縮進的首選的方式是讓「switch」和從屬於它的「case」標籤對齊於同一列,而不要「兩次縮進」「case」標籤。好比:數據結構

    switch (suffix) {編輯器

    case 'G':

    case 'g':

        mem <<= 30;

        break;

    case 'M':

    case 'm':

        mem <<= 20;

        break;

    case 'K':

    case 'k':

        mem <<= 10;

        /* fall through */

    default:

        break;

    }


不要把多個語句放在一行裏,除非你有什麼東西要隱藏:

    if (condition) do_this;

      do_something_everytime;

也不要在一行裏放多個賦值語句。內核代碼風格超級簡單。就是避免可能致使別人誤讀的表達式。

除了註釋、文檔和Kconfig以外,不要使用空格來縮進,前面的例子是例外,是有意爲之。

選用一個好的編輯器,不要在行尾留空格。


第二章:把長的行和字符串打散

代碼風格的意義就在於使用日常使用的工具來維持代碼的可讀性和可維護性。

每一行的長度的限制是80列,咱們強烈建議您遵照這個慣例。

長於80列的語句要打散成有意義的片斷。每一個片斷要明顯短於原來的語句,並且放置的位置也明顯的靠右。一樣的規則也適用於有很長參數列表的函數頭。長字符串也要打散成較短的字符串。惟一的例外是超過80列能夠大幅度提升可讀性而且不會隱藏信息的狀況。

void fun(int a, int b, int c)

{

    if (condition)

        printk(KERN_WARNING "Warning this is a long printk with "

                        "3 parameters a: %u b: %u "

                        "c: %u \n", a, b, c);

    else

        next_statement;

}

第三章:大括號和空格的放置

C語言風格中另一個常見問題是大括號的放置。和縮進大小不一樣,選擇或棄用某種放置策略並無多少技術上的緣由,不過首選的方式,就像Kernighan和Ritchie展現給咱們的,是把起始大括號放在行尾,而把結束大括號放在行首,因此:

    if (x is true) {

        we do 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;

    }

不過,有一個例外,那就是函數:函數的起始大括號放置於下一行的開頭,因此:

    int function(int x)

    {

        body of function

    }

全世界的異端可能會抱怨這個不一致性是……呃……不一致的,不過全部思惟健全的人都知道(a)K&R是_正確的_,而且(b)K&R是正確的。此外,無論怎樣函數都是特殊的(在C語言中,函數是不能嵌套的)。

注意結束大括號獨自佔據一行,除非它後面跟着同一個語句的剩餘部分,也就是do語句中的「while」或者if語句中的「else」,像這樣:

    do {

        body of do-loop

    } while (condition);

    if (x == y) {

        ..

    } else if (x > y) {

        ...

    } else {

        ....

    }

理由:K&R。

也請注意這種大括號的放置方式也能使空(或者差很少空的)行的數量最小化,同時不失可讀性。所以,因爲你的屏幕上的新行是不可再生資源(想一想25行的終端屏幕),你將會有更多的空行來放置註釋。

當只有一個單獨的語句的時候,不用加沒必要要的大括號。

if (condition)

    action();

這點不適用於自己爲某個條件語句的一個分支的單獨語句。這時須要在兩個分支裏都使用大括號。

if (condition) {

    do_this();

    do_that();

} else {

    otherwise();

}

3.1:空格

Linux內核的空格使用方式(主要)取決於它是用於函數仍是關鍵字。(大多數)關鍵字後要加一個空格。值得注意的例外是sizeof、typeof、alignof和__attribute__,這些關鍵字某些程度上看起來更像函數(它們在Linux裏也經常伴隨小括號而使用,儘管在C語言裏這樣的小括號不是必需的,就像「struct fileinfo info」聲明事後的「sizeof info」)。

因此在這些關鍵字以後放一個空格:

    if, switch, case, for, do, while

可是不要在sizeof、typeof、alignof或者__attribute__這些關鍵字以後放空格。例如,

    s = sizeof(struct file);

不要在小括號裏的表達式兩側加空格。這是一個反例:

    s = sizeof( struct file );

當聲明指針類型或者返回指針類型的函數時,「*」的首選使用方式是使之靠近變量名或者函數名,而不是靠近類型名。例子:

    char *linux_banner;

    unsigned long long memparse(char *ptr, char **retptr);

    char *match_strdup(substring_t *s);

在大多數二元和三元操做符兩側使用一個空格,例以下面全部這些操做符:

    = + - < > * / % | & ^ <= >= == != ? :

可是一元操做符後不要加空格:

    & * + - ~ ! sizeof typeof alignof __attribute__ defined

後綴自加和自減一元操做符前不加空格:

    ++ --

前綴自加和自減一元操做符後不加空格:

    ++ --

「.」和「->」結構體成員操做符先後不加空格。

不要在行尾留空白。有些能夠自動縮進的編輯器會在新行的行首加入適量的空白,而後你就能夠直接在那一行輸入代碼。不過假如你最後沒有在那一行輸入代碼,有些編輯器就不會移除已經加入的空白,就像你故意留下一個只有空白的行。包含行尾空白的行就這樣產生了。

當git發現補丁包含了行尾空白的時候會警告你,而且能夠應你的要求去掉行尾空白;不過若是你是正在打一系列補丁,這樣作會致使後面的補丁失敗,由於你改變了補丁的上下文。


第四章:命名

C是一個簡樸的語言,你的命名也應該這樣。和Modula-2和Pascal程序員不一樣,C程序員不使用相似ThisVariableIsATemporaryCounter這樣華麗的名字。C程序員會稱那個變量爲「tmp」,這樣寫起來會更容易,並且至少不會令其難於理解。

不過,雖然混用大小寫的名字是不提倡使用的,可是全局變量仍是須要一個具描述性的名字。稱一個全局函數爲「foo」是一個難以饒恕的錯誤。

全局變量(只有當你真正須要它們的時候再用它)須要有一個具描述性的名字,就像全局函數。若是你有一個能夠計算活動用戶數量的函數,你應該叫它「count_active_users()」或者相似的名字,你不該該叫它「cntuser()」。

在函數名中包含函數類型(所謂的匈牙利命名法)是腦子出了問題——編譯器知道那些類型並且可以檢查那些類型,這樣作只能把程序員弄糊塗了。難怪微軟老是製造出有問題的程序。

本地變量名應該簡短,並且可以表達相關的含義。若是你有一些隨機的整數型的循環計數器,它應該被稱爲「i」。叫它「loop_counter」並沒有益處,若是它沒有被誤解的可能的話。相似的,「tmp」能夠用來稱呼任意類型的臨時變量。

若是你怕混淆了你的本地變量名,你就遇到另外一個問題了,叫作函數增加荷爾蒙失衡綜合症。請看第六章(函數)。


第五章:Typedef

不要使用相似「vps_t」之類的東西。

對結構體和指針使用typedef是一個錯誤。當你在代碼裏看到:

    vps_t a;

這表明什麼意思呢?

相反,若是是這樣

    struct virtual_container *a;

你就知道「a」是什麼了。

不少人認爲typedef「能提升可讀性」。實際不是這樣的。它們只在下列狀況下有用:

(a) 徹底不透明的對象(這種狀況下要主動使用typedef來隱藏這個對象其實是什麼)。

     例如:「pte_t」等不透明對象,你只能用合適的訪問函數來訪問它們。

     注意!不透明性和「訪問函數」自己是很差的。咱們使用pte_t等類型的緣由在於真的是徹底沒有任何共用的可訪問信息。

(b) 清楚的整數類型,如此,這層抽象就能夠幫助消除究竟是「int」仍是「long」的混淆。

     u8/u16/u32是徹底沒有問題的typedef,不過它們更符合類別(d)而不是這裏。

     再次注意!要這樣作,必須事出有因。若是某個變量是「unsigned long「,那麼沒有必要

    typedef unsigned long myflags_t;

     不過若是有一個明確的緣由,好比它在某種狀況下可能會是一個「unsigned int」而在其餘狀況下可能爲「unsigned long」,那麼就不要猶豫,請務必使用typedef。

(c) 當你使用sparse按字面的建立一個新類型來作類型檢查的時候。

(d) 和標準C99類型相同的類型,在某些例外的狀況下。

     雖然讓眼睛和腦筋來適應新的標準類型好比「uint32_t」不須要花不少時間,但是有些人仍然拒絕使用它們。

     所以,Linux特有的等同於標準類型的「u8/u16/u32/u64」類型和它們的有符號類型是被容許的——儘管在你本身的新代碼中,它們不是強制要求要使用的。

     當編輯已經使用了某個類型集的已有代碼時,你應該遵循那些代碼中已經作出的選擇。

(e) 能夠在用戶空間安全使用的類型。

     在某些用戶空間可見的結構體裏,咱們不能要求C99類型並且不能用上面提到的「u32」類型。所以,咱們在與用戶空間共享的全部結構體中使用__u32和相似的類型。

可能還有其餘的狀況,不過基本的規則是永遠不要使用typedef,除非你能夠明確的應用上述某個規則中的一個。

總的來講,若是一個指針或者一個結構體裏的元素能夠合理的被直接訪問到,那麼它們就不該該是一個typedef。


第六章:函數

函數應該簡短而漂亮,而且只完成一件事情。函數應該能夠一屏或者兩屏顯示完(咱們都知道ISO/ANSI屏幕大小是80x24),只作一件事情,並且把它作好。

一個函數的最大長度是和該函數的複雜度和縮進級數成反比的。因此,若是你有一個理論上很簡單的只有一個很長(可是簡單)的case語句的函數,並且你須要在每一個case裏作不少很小的事情,這樣的函數儘管很長,但也是能夠的。

不過,若是你有一個複雜的函數,並且你懷疑一個天分不是很高的高中一年級學生可能甚至搞不清楚這個函數的目的,你應該嚴格的遵照前面提到的長度限制。使用輔助函數,併爲之取個具描述性的名字(若是你以爲它們的性能很重要的話,可讓編譯器內聯它們,這樣的效果每每會比你寫一個複雜函數的效果要好。)

函數的另一個衡量標準是本地變量的數量。此數量不該超過5-10個,不然你的函數就有問題了。從新考慮一下你的函數,把它分拆成更小的函數。人的大腦通常能夠輕鬆的同時跟蹤7個不一樣的事物,若是再增多的話,就會糊塗了。即使你聰穎過人,你也可能會記不清你2個星期前作過的事情。

在源文件裏,使用空行隔開不一樣的函數。若是該函數須要被導出,它的EXPORT*宏應該緊貼在它的結束大括號之下。好比:

int system_is_up(void)

{

    return system_state == SYSTEM_RUNNING;

}

EXPORT_SYMBOL(system_is_up);

在函數原型中,包含函數名和它們的數據類型。雖然C語言裏沒有這樣的要求,在Linux裏這是提倡的作法,由於這樣能夠很簡單的給讀者提供更多的有價值的信息。


第七章:集中的函數退出途徑

雖然被某些人聲稱已通過時,可是goto語句的等價物仍是常常被編譯器所使用,具體形式是無條件跳轉指令。

當一個函數從多個位置退出而且須要作一些通用的清理工做的時候,goto的好處就顯現出來了。

理由是:

- 無條件語句容易理解和跟蹤

- 嵌套程度減少

- 能夠避免因爲修改時忘記更新某個單獨的退出點而致使的錯誤

- 減輕了編譯器的工做,無需刪除冗餘代碼;)

int fun(int a)

{

    int result = 0;

    char *buffer = kmalloc(SIZE);

    if (buffer == NULL)

        return -ENOMEM;

    if (condition1) {

        while (loop1) {

            ...

        }

        result = 1;

        goto out;

    }

    ...

out:

    kfree(buffer);

    return result;

}

第八章:註釋

註釋是好的,不過有過分註釋的危險。永遠不要在註釋裏解釋你的代碼是如何運做的:更好的作法是讓別人一看你的代碼就能夠明白,解釋寫的不好的代碼是浪費時間。

通常的,你想要你的註釋告訴別人你的代碼作了什麼,而不是怎麼作的。也請你不要把註釋放在一個函數體內部:若是函數複雜到你須要獨立的註釋其中的一部分,你極可能須要回到第六章看一看。你能夠作一些小注釋來註明或警告某些很聰明(或者槽糕)的作法,但不要加太多。你應該作的,是把註釋放在函數的頭部,告訴人們它作了什麼,也能夠加上它作這些事情的緣由。

當註釋內核API函數時,請使用kernel-doc格式。請看

Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc以得到詳細信息。

Linux的註釋風格是C89「/* ... */」風格。不要使用C99風格「// ...」註釋。

長(多行)的首選註釋風格是:

    /*

     * 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.

     */

註釋數據也是很重要的,無論是基本類型仍是衍生類型。爲了方便實現這一點,每一行應只聲明一個數據(不要使用逗號來一次聲明多個數據)。這樣你就有空間來爲每一個數據寫一段小注釋來解釋它們的用途了。


第九章:你已經把事情弄糟了

這沒什麼,咱們都是這樣。可能你的使用了很長時間Unix的朋友已經告訴你「GNU emacs」能自動幫你格式化C源代碼,並且你也注意到了,確實是這樣,不過它所使用的默認值和咱們想要的相去甚遠(實際上,甚至比隨機打的還要差——無數個猴子在GNU emacs裏打字永遠不會創造出一個好程序)(譯註:請參考Infinite Monkey Theorem)

因此你要麼放棄GNU emacs,要麼改變它讓它使用更合理的設定。要採用後一個方案,你能夠把下面這段粘貼到你的.emacs文件裏。

(defun linux-c-mode ()

"C mode with adjusted defaults for use with the Linux kernel."

(interactive)

(c-mode)

(c-set-style "K&R")

(setq tab-width 8)

(setq indent-tabs-mode t)

(setq c-basic-offset 8))

這樣就定義了M-x linux-c-mode命令。當你hack一個模塊的時候,若是你把字符串

-*- linux-c -*-放在頭兩行的某個位置,這個模式將會被自動調用。若是你但願在你修改

/usr/src/linux裏的文件時魔術般自動打開linux-c-mode的話,你也可能須要添加

(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)

            auto-mode-alist))

到你的.emacs文件裏。

不過就算你嘗試讓emacs正確的格式化代碼失敗了,也並不意味着你失去了一切:還能夠用「indent」。

不過,GNU indent也有和GNU emacs同樣有問題的設定,因此你須要給它一些命令選項。不過,這還不算太糟糕,由於就算是GNU indent的做者也認同K&R的權威性(GNU的人並非壞人,他們只是在這個問題上被嚴重的誤導了),因此你只要給indent指定選項「-kr -i8」(表明「K&R,8個字符縮進」),或者使用「scripts/Lindent」,這樣就能夠以最時髦的方式縮進源代碼。

「indent」有不少選項,特別是從新格式化註釋的時候,你可能須要看一下它的手冊頁。不過記住:「indent」不能修正壞的編程習慣。


第十章:Kconfig配置文件

對於遍及源碼樹的全部Kconfig*配置文件來講,它們縮進方式與C代碼相比有所不一樣。緊挨在「config」定義下面的行縮進一個製表符,幫助信息則再多縮進2個空格。好比:

config AUDIT

    bool "Auditing support"

    depends on NET

    help

      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.

仍然被認爲不夠穩定的功能應該被定義爲依賴於「EXPERIMENTAL」:

config SLUB

    depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT

    bool "SLUB (Unqueued Allocator)"

    ...

而那些危險的功能(好比某些文件系統的寫支持)應該在它們的提示字符串裏顯著的聲明這一點:

config ADFS_FS_RW

    bool "ADFS write support (DANGEROUS)"

    depends on ADFS_FS

    ...

要查看配置文件的完整文檔,請看Documentation/kbuild/kconfig-language.txt。


第十一章:數據結構

若是一個數據結構,在建立和銷燬它的單線執行環境以外可見,那麼它必需要有一個引用計數器。內核裏沒有垃圾收集(而且內核以外的垃圾收集慢且效率低下),這意味着你絕對須要記錄你對這種數據結構的使用狀況。

引用計數意味着你可以避免上鎖,而且容許多個用戶並行訪問這個數據結構——而不須要擔憂這個數據結構僅僅由於暫時不被使用就消失了,那些用戶可能不過是沉睡了一陣或者作了一些其餘事情而已。

注意上鎖不能取代引用計數。上鎖是爲了保持數據結構的一致性,而引用計數是一個內存管理技巧。一般兩者都須要,不要把兩個搞混了。

不少數據結構實際上有2級引用計數,它們一般有不一樣「類」的用戶。子類計數器統計子類用戶的數量,每當子類計數器減至零時,全局計數器減一。

這種「多級引用計數」的例子能夠在內存管理(「struct mm_struct」:mm_users和mm_count)和文件系統(「struct super_block」:s_count和s_active)中找到。

記住:若是另外一個執行線索能夠找到你的數據結構,可是這個數據結構沒有引用計數器,這裏幾乎確定是一個bug。


第十二章:宏,枚舉和RTL

用於定義常量的宏的名字及枚舉裏的標籤須要大寫。

#define CONSTANT 0x12345

在定義幾個相關的常量時,最好用枚舉。

宏的名字請用大寫字母,不過形如函數的宏的名字能夠用小寫字母。

通常的,若是能寫成內聯函數就不要寫成像函數的宏。

含有多個語句的宏應該被包含在一個do-while代碼塊裏:

#define macrofun(a, b, c)           \

    do {                    \

        if (a == 5)         \

            do_this(b, c);      \

    } while (0)

使用宏的時候應避免的事情:

1) 影響控制流程的宏:

#define FOO(x)                  \

    do {                    \

        if (blah(x) < 0)        \

            return -EBUGGERED; \

    } while(0)

很是很差。它看起來像一個函數,不過卻能致使「調用」它的函數退出;不要打亂讀者大腦裏的語法分析器。

2) 依賴於一個固定名字的本地變量的宏:

#define FOO(val) bar(index, val)

可能看起來像是個不錯的東西,不過它很是容易把讀代碼的人搞糊塗,並且容易致使看起來不相關的改動帶來錯誤。

3) 做爲左值的帶參數的宏: FOO(x) = y;若是有人把FOO變成一個內聯函數的話,這種用法就會出錯了。

4) 忘記了優先級:使用表達式定義常量的宏必須將表達式置於一對小括號以內。帶參數的宏也要注意此類問題。

#define CONSTANT 0x4000

#define CONSTEXP (CONSTANT | 3)

cpp手冊對宏的講解很詳細。Gcc internals手冊也詳細講解了RTL(譯註:register

transfer language),內核裏的彙編語言常常用到它。


第十三章:打印內核消息

內核開發者應該是受過良好教育的。請必定注意內核信息的拼寫,以給人以好的印象。不要用不規範的單詞好比「dont」,而要用「do not」或者「don't」。保證這些信息簡單、明瞭、無歧義。

內核信息沒必要以句號(譯註:英文句號,即點)結束。

在小括號裏打印數字(%d)沒有任何價值,應該避免這樣作。

裏有一些驅動模型診斷宏,你應該使用它們,以確保信息對應於正確的

設備和驅動,而且被標記了正確的消息級別。這些宏有:dev_err(), dev_warn(),

dev_info()等等。對於那些不和某個特定設備相關連的信息,定義了

pr_debug()和pr_info()。

寫出好的調試信息能夠是一個很大的挑戰;當你寫出來以後,這些信息在遠程除錯的時候就會成爲極大的幫助。當DEBUG符號沒有被定義的時候,這些信息不該該被編譯進內核裏(也就是說,默認地,它們不該該被包含在內)。若是你使用dev_dbg()或者pr_debug(),就能自動達到這個效果。不少子系統擁有Kconfig選項來啓用-DDEBUG。還有一個相關的慣例是使用VERBOSE_DEBUG來添加dev_vdbg()消息到那些已經由DEBUG啓用的消息之上。


第十四章:分配內存

內核提供了下面的通常用途的內存分配函數:kmalloc(),kzalloc(),kcalloc()和vmalloc()。請參考API文檔以獲取有關它們的詳細信息。

傳遞結構體大小的首選形式是這樣的:

    p = kmalloc(sizeof(*p), ...);

另一種傳遞方式中,sizeof的操做數是結構體的名字,這樣會下降可讀性,而且可能會引入bug。有可能指針變量類型被改變時,而對應的傳遞給內存分配函數的sizeof的結果不變。

強制轉換一個void指針返回值是多餘的。C語言自己保證了從void指針到其餘任何指針類型的轉換是沒有問題的。


第十五章:內聯弊病

有一個常見的誤解是內聯函數是gcc提供的可讓代碼運行更快的一個選項。雖然使用內聯函數有時候是恰當的(好比做爲一種替代宏的方式,請看第十二章),不過不少狀況下不是這樣。inline關鍵字的過分使用會使內核變大,從而使整個系統運行速度變慢。由於大內核會佔用更多的指令高速緩存(譯註:一級緩存一般是指令緩存和數據緩存分開的)並且會致使pagecache的可用內存減小。想象一下,一次pagecache未命中就會致使一次磁盤尋址,將耗時5毫秒。5毫秒的時間內CPU能執行不少不少指令。

一個基本的原則是若是一個函數有3行以上,就不要把它變成內聯函數。這個原則的一個例外是,若是你知道某個參數是一個編譯時常量,並且由於這個常量你肯定編譯器在編譯時能優化掉你的函數的大部分代碼,那仍然能夠給它加上inline關鍵字。kmalloc()內聯函數就是一個很好的例子。

人們常常主張給static的並且只用了一次的函數加上inline,如此不會有任何損失,由於沒有什麼好權衡的。雖然從技術上說這是正確的,可是實際上這種狀況下即便不加inline gcc也能夠自動使其內聯。並且其餘用戶可能會要求移除inline,由此而來的爭論會抵消inline自身的潛在價值,得不償失。


第十六章:函數返回值及命名

函數能夠返回不少種不一樣類型的值,最多見的一種是代表函數執行成功或者失敗的值。這樣的一個值能夠表示爲一個錯誤代碼整數(-Exxx=失敗,0=成功)或者一個「成功」布爾值(0=失敗,非0=成功)。

混合使用這兩種表達方式是難於發現的bug的來源。若是C語言自己嚴格區分整形和布爾型變量,那麼編譯器就可以幫咱們發現這些錯誤……不過C語言不區分。爲了不產生這種bug,請遵循下面的慣例:

    若是函數的名字是一個動做或者強制性的命令,那麼這個函數應該返回錯誤代碼整

    數。若是是一個判斷,那麼函數應該返回一個「成功」布爾值。

好比,「add work」是一個命令,因此add_work()函數在成功時返回0,在失敗時返回-EBUSY。

相似的,由於「PCI device present」是一個判斷,因此pci_dev_present()函數在成功找到一個匹配的設備時應該返回1,若是找不到時應該返回0。

全部導出(譯註:EXPORT)的函數都必須遵照這個慣例,全部的公共函數也都應該如此。私有(static)函數不須要如此,可是咱們也推薦這樣作。

返回值是實際計算結果而不是計算是否成功的標誌的函數不受此慣例的限制。通常的,他們經過返回一些正常值範圍以外的結果來表示出錯。典型的例子是返回指針的函數,他們使用NULL或者ERR_PTR機制來報告錯誤。


第十七章:不要從新發明內核宏

頭文件include/linux/kernel.h包含了一些宏,你應該使用它們,而不要本身寫一些它們的變種。好比,若是你須要計算一個數組的長度,使用這個宏

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

相似的,若是你要計算某結構體成員的大小,使用

#define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))

還有能夠作嚴格的類型檢查的min()和max()宏,若是你須要可使用它們。你能夠本身看看那個頭文件裏還定義了什麼你能夠拿來用的東西,若是有定義的話,你就不該在你的代碼裏本身從新定義。


第十八章:編輯器模式行和其餘須要羅嗦的事情

有一些編輯器能夠解釋嵌入在源文件裏的由一些特殊標記標明的配置信息。好比,emacs可以解釋被標記成這樣的行:

-*- mode: c -*-

或者這樣的:

/*

Local Variables:

compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"

End:

*/

Vim可以解釋這樣的標記:

/* vim:set sw=8 noet */

不要在源代碼中包含任何這樣的內容。每一個人都有他本身的編輯器配置,你的源文件不該該覆蓋別人的配置。這包括有關縮進和模式配置的標記。人們可使用他們本身定製的模式,或者使用其餘能夠產生正確的縮進的巧妙方法。

相關文章
相關標籤/搜索