最近在向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,但在提交內核驅動這件事上,最好還是以大局為重。對於這麼一個龐大的集市式的開發來說,隨意書寫代碼必將帶來嚴重的可維護性的災難。
(題圖來自:mota.ru)
當代碼量達到一定程度時,手動去檢查和修改coding style是非常繁瑣的工作,幸好,我們還有一些工具可以使用。
這是一個檢查代碼是否符合內核編碼規范的的腳本。顧名思義,checkpatch是用來檢查patch的,默認的調用也確實如此。如果用來檢查原文件,需要加上“-f”的選項。
我們來看一段無聊的代碼(文件名為print_msg.c):
void print_msg(int a)
{
switch(a){
case1:
printf("a == 1\n");
break;
case2:
printf("a == 2\n");
break;
}
}
這段代碼的coding style是否有問題呢?用checkpatch.pl來檢查一下:
scripts/checkpatch.pl -f print_msg.c
檢查的結果是:
ERROR:switchandcase should be at the same indent
#3: FILE: switch.c:3:
+switch(a){
+case1:
[...]
+case2:
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可以用來自動修改縮進問題。提醒一下,使用Lindent要求系統安裝indent這個工具。
對於上面這個例子,執行Lindent命令:
scripts/Lindent print_msg.c
得到的新代碼是:
void print_msg(int a)
{
switch(a){
case1:
printf("a == 1\n");
break;
case2:
printf("a == 2\n");
break;
}
}
sed是一個流編輯器,其強大的功能可以幫助我們處理很多重復性的工作。比如,Linux內核的coding style要求,行尾不能有空格(包括Tab),去除這些空格就可以借助sed。
我自己的習慣很差,經常在代碼的行尾留下一些空格。比如一行代碼過長需要換行時,總是下意識的在換行的地方敲一個空格。另外,我常用的編輯器之一的Kate,為了對齊的需要,經常在空行的前面留上幾個縮進的Tab(如下圖)。
手動去除這些行尾的空格是一件頭大的事情,但對於sed來說不過是舉手之勞。命令格式如下:
sed 's/[ \t]*$//g' your_code.c
1、除了注釋、文檔和Kconfig之外,使用Tab縮進,而不是空格,並且Tab的寬度為8個字符;
2、switch ... case ...語句中,switch和case具有相同的縮進(參考上文);
3、花括號的使用參考K&R風格。
如果是函數,左花括號另起一行:
intfunction(int x)
{
body of function
}
否則,花括號緊接在語句的最後:
if(x istrue){
we do y
}
如果只有一行語句,則不需要用花括號:
if(condition)
action();
但是,對於條件語句來說,如果一個分支是一行語句,另一個分支是多行,則需要保持一致,使用花括號:
if(condition){
do_this();
do_that();
}else{
otherwise();
}
4、在關鍵字“if, switch, case, for, do, while”之後需要加上空格,如:
if(something)
5、在關鍵字“sizeof, typeof, alignof, or __attribute__”之後不要加空格,如:
sizeof(struct file)
6、在括號裡的表達式兩邊不要加空格,比如,下面是一個反面的例子:
sizeof(struct file )
7、大多說的二元和三元運算符兩邊需要空格,如“= + - < > * / % | & ^ <= >= == != ? :”;
8、一元運算符後面不要空格,如“& * + - ~ ! sizeof typeof alignof __attribute__ defined”;
9、在前綴自增自減運算符之後和後綴自增自減運算符之前不需要空格(“++”和“--”);
10、結構成員運算符(“.”和“->”)的兩邊不需要空格;
11、行尾不需要空格;
12、使用C89的“/* ... */”風格而不是C99的“// ...”風格;
13、對於多行注釋,可以參考下例:
/*
* 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.
*/
14、“config”定義下面的語句用Tab縮進,help下面的語句再額外縮進兩個空格,如:
config AUDIT
bool"Auditing support"
depends on NET
help
Enable auditing infrastructure that can be used with another
kernel subsystem, such asSELinux(which requires thisfor
logging of avc messages output).Doesnotdo system-call
auditing without CONFIG_AUDITSYSCALL.
15、多行的宏定義需要用“do .. while”封裝,如:
#define macrofun(a, b, c) \
do{ \
if(a ==5) \
do_this(b, c); \
}while(0)
16、函數返回值的定義最好也要遵循一定的章法。
如果函數的名稱是一種動作或者命令式的語句,應該以錯誤代碼的形式返回(通常是0表示成功,-Exxx這種形式的負數表示錯誤),如:
do_something()
如果函數的名稱是判斷語句,則返回值應該類似與布爾值(通常1表示成功,0表示錯誤),如:
something_is_present()