淺析linux內核驅動的代碼風格

源出處:http://top.akaedu.org/index.php/index/bookdetail/id/2890


最近在向Linux內核提交一些驅動程序,在提交的過程中,發現自己的代碼離Linux內核的coding style要求還是差很多。當初自己對內核文檔裏的CodingStyle一文只是粗略的瀏覽,真正寫代碼的時候在很多細節上會照顧不周。不過, 在不遵守規則的程序員隊伍裏,我並不是孤獨的。如果去看drivers/staging下的代碼,就會發現很多驅動程序都沒有嚴格遵守內核的coding style,而且在很多驅動程序的TODO文件裏,都會把"checkpatch.pl fixes"作爲自己的目標之一(checkpatch.pl是用來檢查代碼是否符合coding style的腳本)。

   不可否認,coding style是仁者見仁、智者見智的事情。比如Microsoft所推崇的匈牙利命名法,在Linus看來就是及其腦殘(brain damaged)的做法。也許您並不贊成Linus制定的coding style,但在提交內核驅動這件事上,最好還是以大局爲重。對於這麼一個龐大的集市式的開發來說,隨意書寫代碼必將帶來嚴重的可維護性的災難。


一些輔助工具

   當代碼量達到一定程度時,手動去檢查和修改coding style是非常繁瑣的工作,幸好,我們還有一些工具可以使用。

scripts/checkpatch.pl

   這是一個檢查代碼是否符合內核編碼規範的的腳本。顧名思義,checkpatch是用來檢查patch的,默認的調用也確實如此。如果用來檢查原文件,需要加上“-f”的選項。

我們來看一段無聊的代碼(文件名爲print_msg.c):

 
01 voidprint_msg(inta)
02 {
03     switch(a) {
04         case1:
05             printf("a == 1\n");
06             break;
07   
08         case2:
09             printf("a == 2\n");
10             break;
11     }
12 }

這段代碼的coding style是否有問題呢?用checkpatch.pl來檢查一下:

scripts/checkpatch.pl -f  print_msg.c

檢查的結果是:

 
ERROR: switch and case should be at the same indent
#3: FILE: switch.c:3:
+       switch (a) {
+               case 1:
[...]
+               case 2:
  
total: 1 errors, 0 warnings, 12 lines checked
  
switch.c has style problems, please review.  If any of these errors
are false positives report them to the maintainer, see
CHECKPATCH in MAINTAINERS.

   在Linux內核的coding style裏,switch和case要求有相同的縮進。本例的代碼很少,錯誤也只有這一個,手動修改很方便。如果類似的縮緊錯誤很多怎麼辦?

scripts/Lindent

   scripts目錄下的工具Lindent可以用來自動修改縮進問題。提醒一下,使用Lindent要求系統安裝indent這個工具。

對於上面這個例子,執行Lindent命令:

scripts/Lindent print_msg.c

得到的新代碼是:

 
01 voidprint_msg(inta)
02 {
03     switch(a) {
04     case1:
05         printf("a == 1\n");
06         break;
07   
08     case2:
09         printf("a == 2\n");
10         break;
11     }
12 }

sed

    sed是一個流編輯器,其強大的功能可以幫助我們處理很多重複性的工作。比如,Linux內核的coding style要求,行尾不能有空格(包括Tab),去除這些空格就可以藉助sed。

我自己的習慣很差,經常在代碼的行尾留下一些空格。比如一行代碼過長需要換行時,總是下意識的在換行的地方敲一個空格。另外,我常用的編輯器之一的Kate,爲了對齊的需要,經常在空行的前面留上幾個縮進的Tab(如下圖)。

 

手動去除這些行尾的空格是一件頭大的事情,但對於sed來說不過是舉手之勞。命令格式如下:

sed   's/[ \t]*$//g'   your_code.c


一些需要注意的Coding Style

縮進

1、除了註釋、文檔和Kconfig之外,使用Tab縮進,而不是空格,並且Tab的寬度爲8個字符;

2、switch ... case ...語句中,switch和case具有相同的縮進(參考上文);

花括號

3、花括號的使用參考K&R風格。

如果是函數,左花括號另起一行:

 
1 intfunction(int x)
2 {
3         body of function
4 }

否則,花括號緊接在語句的最後:

 
1 if(x is true) {
2         wedo y
3 }

如果只有一行語句,則不需要用花括號:

 
1 if(condition)
2         action();

但是,對於條件語句來說,如果一個分支是一行語句,另一個分支是多行,則需要保持一致,使用花括號:

 
1 if(condition) {
2         do_this();
3         do_that();
4 }else {
5         otherwise();
6 }

空格

4、在關鍵字“if, switch, case, for, do, while”之後需要加上空格,如:

 
1 if(something)

5、在關鍵字“sizeof, typeof, alignof, or __attribute__”之後不要加空格,如:

 
1 sizeof(structfile)

6、在括號裏的表達式兩邊不要加空格,比如,下面是一個反面的例子

 
1 sizeof(struct file )

7、大多說的二元和三元運算符兩邊需要空格,如“= + - < > * / % | & ^ <= >= == != ? :”;

8、一元運算符後面不要空格,如“& * + - ~ ! sizeof typeof alignof __attribute__ defined”;

9、在前綴自增自減運算符之後和後綴自增自減運算符之前不需要空格(“++”和“--”);

10、結構成員運算符(“.”和“->”)的兩邊不需要空格;

11、行尾不需要空格;

註釋

12、使用C89的“/* ... */”風格而不是C99的“// ...”風格;

13、對於多行註釋,可以參考下例:

 
1 /*
2 * This is the preferred style for multi-line
3 * comments in the Linux kernel source code.
4 * Please use it consistently.
5 *
6 * Description: A column of asterisks on the left side,
7 * with beginning and ending almost-blank lines.
8 */

Kconfig

14、“config”定義下面的語句用Tab縮進,help下面的語句再額外縮進兩個空格,如:

 
1 config AUDIT
2         bool "Auditing support"
3         depends on NET
4         help
5           Enable auditing infrastructure that can be used with another
6           kernel subsystem, such as SELinux (which requires this for
7           logging of avc messages output). Does not do system-call
8           auditing without CONFIG_AUDITSYSCALL.

15、多行的宏定義需要用“do .. while”封裝,如:

 
1 #define macrofun(a, b, c)               \
2 do{                                    \
3         if(a == 5)                     \
4                 do_this(b, c);          \
5 }while (0)

函數返回值

16、函數返回值的定義最好也要遵循一定的章法。

如果函數的名稱是一種動作或者命令式的語句,應該以錯誤代碼的形式返回(通常是0表示成功,-Exxx這種形式的負數表示錯誤),如:

1 do_something()

如果函數的名稱是判斷語句,則返回值應該類似與布爾值(通常1表示成功,0表示錯誤),如:

1 something_is_present()
發佈了37 篇原創文章 · 獲贊 3 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章