source: https://www.kernel.org/doc/html/latest/process/coding-style.html
translated by trav, travmymail@gmail.comhtml
這是一篇闡述Linux內核編程代碼風格的文檔,譯者以學習爲目的進行翻譯。linux
Tab的寬度是八個字符,所以縮進的寬度也是八個字符。有些異教徒想讓縮進變成四個字符,甚至是兩個字符的寬度,這些人和那些把 PI 定義爲 3 的人是一個路子的。程序員
注意:縮進的所有意義在於清晰地定義語句塊的開始與結束,特別是當你盯着屏幕20個小時以後,你會發現長的縮進寬度的做用。編程
如今有些人說八個字符的寬度太寬了,這會讓代碼往右移很遠,在一塊八十字符寬的屏幕上,這樣的代碼會很難閱讀。對此的回答是,若是你寫的代碼須要超過三層的縮進,那麼你把一切都搞砸了,你應該修復你的程序。vim
簡而言之,八個字符寬度的縮進讓代碼更容易閱讀,而且額外的好處就是提醒你,不要在一個函數裏寫太多層的嵌套邏輯。請記住這個警示。數組
switch語句的縮進方式是讓case與switch對齊:緩存
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中,永遠不要使用空格做爲縮進,上面的例子是故意犯的錯誤。app
找一個像樣的編輯器,不要在行末留有空格。
規範代碼風格的目的是提升代碼的可讀性和維護性。
單行的寬度限制爲八十列,這是強烈推薦的設置。
任何一行超過八十列寬度的語句都應該拆分紅多個行,除非超過八十列的部分能夠提升可讀性且不會隱藏信息。拆分出來的子句長度老是應該比其主句要短,而且應該儘可能靠右。這條法則一樣適用於一個有很長的參數列表的函數頭。然而,千萬不要把用戶可見的字符串,好比 printk 的信息,拆分紅多行,由於這樣會致使使用 grep 的時候找不到這些信息。
另外一個關於 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 }
異教徒們會認爲這樣的風格是不一致的,可是全部有腦子的人都知道盡管是 K&R 也是不一致的(譯者注:K&R這本書的初版和第二版有不一致的地方)。除此以外,咱們知道函數是很特殊的,在 C 語言中,你不能有嵌套函數。
注意到,右括號通常是單獨成一行的,除非右括號以後緊隨着緊密結合的語句,例如 do-while 語句和 if 語句:
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(); else do_that();
這一點不適用於只有一個 case 有單行,其餘 case 有多行的狀況:
if (condition) { do_this(); do_that(); } else { otherwise(); }
在一個循環中超過一個語句的狀況也一樣須要使用括號:
while (condition) { if (test) do_something(); }
3.1 空格
Linux 內核風格的空格主要用在一些關鍵字上,即在關鍵字以後添一個空格。值得關注的例外是一些長得像函數的關鍵字,好比:sizeof, typeof, alignof, attribute,在 Linux 中,這些關鍵字的使用都會帶上一對括號,儘管在 C 語言的使用上並不須要帶上括號。
因此在下面這些關鍵字以後添加一個空格:
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 一般會警告你,讓你除去這些多餘的空格,而且能夠幫你刪掉這些東西。可是,若是你讓 Git 一直幫你這樣修補你的代碼,這極可能致使代碼行的上下錯亂,以後的自動修補的失敗。
C 是一種簡潔粗曠的語言,所以,你的命名也應該是簡潔的。C 程序員不會像 Modula-2 和 Pascal 程序員那樣使用 ThisVariableIsATemporaryCounter 這種「可愛」的名字,一個 C 程序員會把這種變量命名爲 tmp ,如此簡潔易寫。
儘管看到一個混合大小寫的名字讓人皺眉,不過對於全局變量來講,一個具備描述性的名字仍是頗有必要的。去調用一個名爲 foo 的全局函數一樣讓人難以接受。
全局變量(只有當你真正須要的時候才用它)和全局函數須要使用描述性的名字。若是你有一個計算活躍用戶數量的函數,你應該起這樣一個名字 count_active_users()
或者相似的,而不是這樣一個名字 cntusr()
。
起一個包含函數類型的名字(匈牙利命名法)是摧殘大腦的行爲,編譯器知道函數的類型而且會檢查類型,這樣的名字不會起到任何幫助,它僅僅會迷惑程序員。因此,也難怪微軟作出了那麼多充滿了 bug 的程序。
局部變量名應該簡短,若是你須要寫一個循環,定義一個計數器,在不產生歧義的狀況下,你大可命名爲 i ,命名爲 loop_counter 是生產力很低的行爲。一樣地,tmp 能夠是任何類型的臨時變量。
若是你擔憂會弄混變量名,那麼你遇到了另外一個問題,你患上了函數增加荷爾蒙失調綜合症。
請不要使用 vps_t 這種東西,這是 typedef 的錯誤用法,當你看到
vps_t a;
這種寫法時,它到底是個什麼東西?相反,若是是這樣的寫法
struct virtual_container *a;
你就很容易知道 a 表明着什麼。
不少人認爲 typedef 是用來幫助提升可讀性的,可是事實每每不是這樣的。typedef 僅僅有以下用處:
a. 封裝對象(typedef 能夠方便的隱藏對象)
例如,pte_t 會把對象封裝起來,你僅僅只能經過合適的「訪問函數」(成員函數)來訪問這個對象。
注意:封裝和「訪問函數」(成員函數)自己就不是好東西,咱們使用 pte_t 這種東西的理由就是,它指向的對象自己絕對沒有東西能夠訪問(咱們壓根兒不使用封裝和成員函數那一套)。
b. 指明整數類型,這種抽象能夠幫助咱們避免一些使用 int 和 long 的疑慮
u8/u16/u32 是完美的使用 typedef 的例子。
注意:你必需要有明確的理由來使用這些用法,若是一些地方使用的自己就是 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個這個範圍,不然你就犯了一些錯誤。從新思考這個函數,把它拆分紅更小的幾段。人類的大腦通常只能同時關注七件不一樣的事,更多須要關注的事情意味着更多的困擾。儘管你認爲你是個天才,可是你也但願理解一段你兩週以前寫的代碼。
在源文件中,用一個空行分割不一樣的函數,若是函數須要導出到外部使用,那麼它對應的 EXPORT 宏應當緊隨在函數以後,例如:
int system_is_up(void) { return system_state == SYSTEM_RUNNING; } EXPORT_SYMBOL(system_is_up);
函數原型中,參數名應該與參數類型引發寫出來,儘管 C 語言容許只寫上參數類型,可是咱們更推薦參數名,由於這是一種爲讀者提供有價值信息的簡單方式。
不要在函數原型以前使用extern
關鍵字,由於這是沒必要要且多餘的。
儘管許多人反對,可是 goto 語句頻繁地以無條件跳轉的形式被編譯器使用。
當函數有多個出口,而且返回以前須要作不少類似的工做時,好比清理空間,這時候 goto 語句是十分方便的。固然了,若是沒有相似的清理工做要在返回以前作,那麼直接返回便可。
根據 goto 的做用來決定一個 label 的名字,若是 goto 語言要去釋放緩存,那麼out_free_buffer:
會是一個好名字。避免使用 GW-BASIC 的命名方式,好比 err1:
err2:
,由於當你須要新加或者刪除某些函數出口時,你就須要從新排列標籤數字,這會讓代碼的正確性難以獲得保證。
使用 goto 的理由以下:
無條件跳轉易於理解和閱讀
能夠減小嵌套
能夠減小修改個別函數出口代碼所形成的錯誤
算是幫助編譯器作了一些優化的工做
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; }
一個常見的 bug 被稱做 one err bug,它長得像這樣:
err: kfree(foo->bar); kfree(foo); return ret;
bug 在於某些 goto 語句跳轉到此時,foo 仍然是 NULL,修復此 bug 的簡單方式就是將一個 label 拆分紅兩個,err_free_bar:
和 err_free_foo:
:
err_free_bar: kfree(foo->bar); err_free_foo: kfree(foo); return ret;
事實上,你應該進行測試,模擬錯誤狀況的發生,測試全部的出口代碼。
註釋是好的,可是要避免過度註釋。永遠不要去嘗試解釋你的代碼如何工做,而是花時間在寫出好的代碼來,解釋一段爛代碼是浪費時間。
通常來講,你應該去說明你的代碼作了什麼,而不是怎麼作。一樣地,儘可能避免在函數體內寫註釋,若是你的函數如此複雜,以至於你須要在函數體內分幾段註釋來解釋,那麼你應該回到第六節去看看。你能夠寫一小段的註釋來標記或者提醒你們哪些地方寫得真聰明(或者真爛),可是不要作得太過度。除此以外,你應該把註釋寫在函數開頭,告訴人們這個函數幹了什麼,爲何要這樣幹。
當你給 kernel API 進行註釋的時候,請你使用 kernel-doc 的格式。具體參見 https://www.kernel.org/doc/html/latest/doc-guide/index.html#doc-guide
多行註釋推薦的格式以下:
/* * 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. */
對於在 net/ 和 drivers/net/ 中的文件,推薦的多行註釋格式以下:
/* 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. */
對一些數據和變量進行註釋也是必要的,不管他們是基本類型的仍是派生類型的。爲了進行註釋,你應該在一行內只聲明一個變量,不要使用逗號進行多個聲明,這讓你有地方對每個變量進行註釋。
不要緊,咱們都犯過錯。你的那些 Unix 的老手朋友們可能會告訴你,GNU emacs 能幫你自動地對 C 代碼進行排版,你也注意到它確實能夠。可是它默認的排版方式真的很糟糕,事實上,即使是在鍵盤上亂敲也比它來的好看。相信我,無數的猴子在 GNU emacs 上亂敲是不會作出好的程序的。
所以,你能夠選擇直接把 GNU emacs 給刪了,或者修一修它,讓它恢復正常。若是你選擇了後者,那麼請把下面的東西拷貝到你的 .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))) (add-hook 'c-mode-common-hook (lambda () ;; Add kernel style (c-add-style "linux-tabs-only" '("linux" (c-offsets-alist (arglist-cont-nonempty c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only)))))) (add-hook 'c-mode-hook (lambda () (let ((filename (buffer-file-name))) ;; Enable kernel mode for the appropriate files (when (and filename (string-match (expand-file-name "~/src/linux-trees") filename)) (setq indent-tabs-mode t) (setq show-trailing-whitespace t) (c-set-style "linux-tabs-only")))))
這會讓你的 emacs 更好地知足內核的代碼風格。
可是即便你不能讓你的 emacs 恢復正常,也有解救方法:使用 indent 。
一樣的問題出現了,GNU indent 和 GNU emacs 有一樣的問題,所以你須要一些命令行選項來進行配置。可是事情也沒那麼糟,由於 GNU indent 的製造者認可 K&R 的權威性,因此你只須要添加命令行參數 -kr -i8 (表示 K&R,8個字符寬的縮進),或者使用 scripts/Lindent 也能夠。
indent 有不少命令行選項,特別是註釋的格式化方面,你能夠經過 man 幫助頁面來查看,不過請記住:indent 不是用來修復爛程序的。
注意:你也可使用 clang-format 來完成這些格式化的工做,具體參見 https://www.kernel.org/doc/html/latest/process/clang-format.html#clangformat
對於 Linux 中的 Kconfig 配置文件,他們的縮進是有所不一樣的。在 config 定義下的縮進是一個 tab,而裏面的 help 文本是兩個空格,例如:
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.
而對於有可能致使危險的動做(好比特定文件系統的寫支持),你應該在提示文本中直接指出:
config ADFS_FS_RW bool "ADFS write support (DANGEROUS)" depends on ADFS_FS ...
具體細節參見 Documentation/kbuild/kconfig-language.txt
對於單線程環境裏建立和銷燬的一些數據結構,若是他們對於線程外是可見的,那麼老是應該有引用計數。在內核裏,垃圾收集器(GC)是不存在的,這意味着你必須對你使用過的數據進行引用計數。
進行引用計數意味着你能夠避免死鎖,容許多個用戶並行訪問數據,而且不用擔憂數據由於睡眠或者其餘緣由而找不到。
注意,鎖不是引用計數的替代品。鎖是爲了保持數據的一致性,而引用計數是一種內存管理計數。一般這兩種技術都是須要的,咱們不要把他們搞混。
當有多個不一樣類的使用者時,不少數據結構會使用二級引用計數。第二級的引用計數會統計第二級使用者的數量,只有當第二級引用計數遞減至零時,全局的第一級引用計數纔會減一。
這種多級引用計數在內存管理(struct mm_struct: mm_users and mm_count)和文件系統(struct super_block: s_count and s_active)中都有使用。
記住,若是其餘線程能夠發現並使用你的數據結構,而你卻沒有引用計數,那麼這基本就是一個 bug。
常量宏和枚舉的命名都是大寫的。
#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)
用宏來定義常量的時候,必需要括上括號,帶有參數的宏也要注意。
5) 在定義宏函數時發生命名衝突
#define FOO(x) \ ({ \ typeof(x) ret; \ ret = calc_ret(x); \ (ret); \ })
ret 是一個很容易和局部變量發生衝突的名字,而 __foo_ret 這樣的名字則不多會發生衝突。
C++ 手冊全面地闡述了宏定義的細節,gcc 手冊一樣也闡述了彙編語言使用的 RTL 規則,具體請自行查看。
內核開發者喜歡被視爲有素養的,好的英文拼寫和準確的內核信息能給人留下好的印象,所以,不要使用一些單詞的縮寫,好比 dont,而是 do not 或者 don't。把提示信息寫得儘量準確、清晰、無二義。
內核信息不須要在末尾加上句號
在圓括號中打印數字(%d)沒有任何意義,應該避免這樣幹。
在<linux/device.h>中有許多驅動模型的診斷宏,你應該使用這些宏來確保消息匹配正確的設備和驅動,並正確的標記它們的級別:dev_err(), dev_warn(), dev_info(), and so forth。對於沒有關聯特定設備的消息,<linux/printk.h>中定義了 pr_notice(), pr_info(), pr_warn(), pr_err(), etc。
編寫好的調試信息是一項巨大的挑戰,一旦你完成了,這些信息會對遠程調試產生巨大幫助。調試信息與普通訊息不一樣,pr_XXX() 函數在任何條件下都會進行打印,而 pr_debug() 卻不是,這些與調試有關的函數默認都不會被編譯,除非你定義了一個 DEBUG 宏或者 CONFIG_DYNAMIC_DEBUG 宏來顯式地讓編譯器編譯他們。還有一個慣例就是使用 VERBOSE_DEBUG 爲那些已經開啓 DEBUG 的用戶添加 dev_vdbg() 消息。
不少子系統在對應的 makefile 裏都有 Kconfig 調試選項來打開 -DDEBUG,或者是在文件裏定義宏 #define DEBUG。當調試信息能夠被無條件打印,或者說已經編譯了和調試有關的 #ifdef 段,那麼 printk(KERN_DEBUG ...) 就能夠用來打印調試信息。
內核提供了下面這些通用的內存分配器:kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc(), and vzalloc()。具體細節參見 API 文檔。
爲一個結構體分配內存的形式最好是這樣的:
p = kmalloc(sizeof(*p), ...);
另外一種寫出結構體名字的方式(sizeof(struct name))會破壞可讀性而且給 bug 製造了機會:修改結構體名字卻忘了修改對於的 sizeof 語句。
另外,在 malloc 以前添加上一個強制的類型轉換,把空類型的指針轉換爲特定類型的指針,這些是畫蛇添足的操做,他們應當交給編譯器來幹,而不是你。
分配一個數組的形式最好是這樣的:
p = kmalloc_array(n, sizeof(...), ...);
分配一個零數組的形式最好是這樣的:
p = kcalloc(n, sizeof(...), ...);
兩種形式都會檢查溢出,而且溢出發生時返回一個空指針 NULL。
一個很常見的誤解就是,人們認爲 gcc 有一種讓他們的程序跑得更快的魔法,就是內聯。然而,內聯每每也有不合適的用法(例如第十二節提到的替換宏)。inline 關鍵字的泛濫,會使內核變大,從而使整個系統運行速度變慢,由於大內核會佔用更多的CPU高速緩存,同時會致使可用內存頁緩存減小。想象一下,一次頁緩存未命中就會致使一次磁盤尋址,這至少耗費5毫秒。5毫秒足夠CPU運行不少不少的指令。
一個基本的原則就是,若是一個函數有3行以上的代碼,就不要把它變成內聯函數。有一個例外,若某個參數是一個編譯時常數,且你肯定由於這個常量,編譯器在編譯時能優化掉函數的大部分代碼,那麼加上 inline 關鍵字。kmalloc() 就是個很好的例子。
人們常常主張能夠給只用一次的靜態函數加上 inline 關鍵字,這樣不會有任何損失。雖然從技術上來講這樣沒錯,可是實際上 gcc 會自動內聯這些函數。
函數能夠返回不一樣種類的值,可是最廣泛的就是表示運行成功或失敗的值。這樣的值能夠用預先定義好的錯誤碼錶示(-Exxx = failure, 0 = success),或者一個布爾值(0 = failure, non-zero = success)
混合兩種方式會使代碼變得複雜,而且很難找到 bug。若是C語言能明確區分整型和布爾型,那麼編譯器會替咱們發現這個問題……可是它不會那麼作。爲了不這種問題,必定要謹記以下約定:
若是函數名是一個短語,表示的是一個動做,或者一個命令,那麼返回值應該使用錯誤碼的方式。 若是函數名是一句話,表示的是一個斷言,那麼應該使用布爾值的方式。
例如,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 */
不要在源代碼中包含任何相似的內容。每一個人都有本身的編輯器配置,你的源文件不該該影響他們。
在寫一些與體系結構有關的代碼中,你可能須要使用一些內聯彙編調用CPU相關的接口或者和平臺有關的功能,若是有這種需求,你大可以使用匯編。可是若是C語言能夠乾的事,不要使用匯編。你應該儘量地使用C語言來控制硬件。
儘量寫一些輔助函數來實現相同的功能,而不是重複地寫一些相同的代碼,同時記住,內聯彙編也可使用C函數的參數。
大的、重要的彙編函數應該獨自寫在一個 .S 文件中,而且編寫對應的C頭文件和函數原型,相應的函數原型應該添加 asmlinkage 關鍵字。
你也許須要標記某些彙編代碼爲 volatile,避免 gcc 誤把一些彙編移除掉。通常狀況下,你不須要這樣幹,不必的標記會影響優化。
當一條彙編語句裏包含多個指令時,每一個指令分行寫,而且除了最後一行外,在其餘行的行末添加 \n\t 進行縮進和對齊:
asm ("magic %reg1, #42\n\t" "more_magic %reg2, %reg3" : /* outputs */ : /* inputs */ : /* clobbers */);
不管在哪,不要在 .c 文件中使用條件編譯命令(#if, #ifdef),這樣幹會致使代碼可讀性下降而且代碼邏輯混亂。取而代之,應該在 .c 文件對應的頭文件中使用這些條件編譯,而且在每一個 #else 分支註明對應的版本信息。
把同一個版本的全部函數都寫在一個 #ifdef 中,不要在其中寫一部分,而又在外部寫一部分。
在 #endif 以後寫上一個註釋,註明這個 #ifdef 塊對應的內容:
#ifdef CONFIG_SOMETHING ... #endif /* CONFIG_SOMETHING */
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 http://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 process/coding-style.rst, by greg@kroah.com at OLS 2002: http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/