2011年7月26日 星期二

Linux kernel coding style (上)

小弟最近為了這個新硬體,加了一個linux kernel module並改寫了arch/powerpc下的一些code,嘗試要把修改的code送回公司的repo裡。結果寫得太差,光是coding stlye的因素就被maintainer修改了快超過500行,沒有被maintainer直接踢回來是他心地善良。於是我花了點時間看 linux coding style 的文件,並把重點摘要於此,希望能讓初次踏入linux kernel裡的人很快了解這個最最最基本的東西。



以下內容及範例皆取自Linus所寫的 Linux kernel coding style。另外,Linus文筆雄『賤』有力,如果我的摘要有那麼一點點不討喜,那一定跟原文比較有關,跟翻譯無關。

1. 縮排


所有的縮排皆用8個字元tab。

原因:你每天連續看二十個小時code的話,你就會知道為何要用大尺寸縮排了!有人會抱怨大尺寸縮排會讓code一直偏向右移,在80字元寬的terminal上不好讀。Linus曰:如果你的程式用到三層以上的縮排,你的程式大概也好不到哪去,快點修修吧!

別把兩個statement放同一行。
別在同一行用 multiple assignment,kernel code就是要簡單,別搞花俏。
用個好一點editor,別在檔案結尾加任何空行。

switch 和 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;
        }


2. 分割過長的一行code及字串

每行限制80個字元。超過的話請自行分割,分割後的部份儘量往右縮排,這樣能一眼辨別這是一行code分割的結果。function header亦同。
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;
}

3. 括號和空格

用K&R style的括號– 左括號放在行尾,又括號放在另一行,如下
if (x is true) {
                we do y
        }

        switch (action) {
        case KOBJ_ADD:
                return "add";
        case KOBJ_REMOVE:
                return "remove";
        case KOBJ_CHANGE:
                return "change";
        default:
                return NULL;
        }
例外是function,左右兩個括號都放在各自一行的行首:
int function(int x)
{
        body of function
}
do...while和多個if..else...也是例外:
do {
        body of do-loop
} while (condition);
if (x == y) {
 ..
} else if (x > y) {
 ...
} else {
 ....
}
if...else...後的 statement只有一個,不需加括號:
if (condition)
        action();

if (condition)
        do_this();
else
        do_that();

但是if...else...只要有任何分支用了括號,兩個分支都要用括號:
if (condition) {
        do_this();
        do_that();
} else {
        otherwise();
}

3.1: 空白字元

基本原則是,(大部分的)keyword後都要加上空白字元,例如:if, switch, case, for, do, while。但是『長得像function』的keyword後面不需加空白,例如:sizeof, typeof, alignof, __attribute_。
括號內不需要空白: s = sizeof( struct file ); /* BAD!!! */
宣告pointer時,空白放在*之前: char *linux;
binary/ternary operator前後都加空白:
=  +  -  <  >  *  /  %  |  &  ^  
<=  >=  ==  !=  ?  :

unary operator 前後則免:
&  *  +  -  ~  !  sizeof  typeof  alignof  
__attribute__  defined

這些也不用:
a++;
--a;
my_struct.b;
my_ptr->size;

行末不要有多餘空白。有些編輯器有smart indent功能,只要一按enter就自動預先填好indent tab,這種空白可以接受。但smart indent若是在檔案最末端則不OK。

4. 命名

C是種鐵血斯巴達式的語言,不像Modula-2或是pascal會用到ThisVariableIsATemporaryCounter這種裝可愛的命名法。直接用tmp就可以了,因為它好寫又易懂。
Global vairable的命名必須能精確敘述他的用途。例如:
count_active_user(); /* good */
cntusr();            /* BAD */
把data type放進命名規則中(匈牙利命名法)的人一定是腦袋有問題,compiler本來就知道各個變數的data type了,何必在放在變數名中讓事情弄得更複雜。難怪Microsoft的程式很多bug。
Local variable的命名則要短又有力。用 i 當作loop counter就是一個好例子,用 loop_counter這個名字就遜了。

5. Typedefs

不要刻意把某個struct或是pointer用typedef的方式另外命名。
是這行比較易懂?
vps_t a;
還是這行?
struct virtual_container* a;

typedef只適用於:
  1. 你需要把所有內部細節隱藏在 opaque object 中,外人只能經由 access functions 操作。
  2. 分辨不同長度的integer type,例如:u8/u16/u32
  3. 用到 sparse 來作 type-checking
  4. (跟2雷同,譯者略)
  5. 某些結構會和 user space 共用,但既不能直接用C99的data type,也不能用kernel自行定義的 u32,所以只好訂個 __u32。(譯者自己不懂這段的意思,有人可以解釋嗎?)

總而言之,struct 內部若可直接被外界存取,就不要再去typedef。沒有適當理由,不要隨便亂用typedef。

6. Functions

function要短要甜而且只做一件事,內容要可以在一兩頁內顯示完畢。80x24是一頁,你知道的。
function可接受的最大長度是和複雜度成反比的,所以一個function中只有一個超大switch但每個case都超簡單,那沒問題,全部放在一個function。反之,如果你的function有點複雜度,沒天份的高一學生可能很難了解其中的奧妙,那請嚴格遵守最大長度限制。
一個不錯的指標是在單一function中不應該會使用超過5~10個 local variable。而且人腦一次只能追蹤7件不同的東西,太多東西搞在一起一定會搞砸。
程式碼中function和function必須間隔一行。如果用到 EXPORT_* macro,請緊跟在function後:
int system_is_up(void)
{
        return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

8 則留言:

  1. Hi, 剛好路過,發現照片有點像小學同學耶! 你不會恰好是永和國小畢吧!!

    回覆刪除
    回覆
    1. 我就是永和國小畢業的耶, 我是梁書豪, 敢問你是? 寄信給我吧!

      刪除
    2. 哈..還真是有緣ㄚ,我是王鵬超,mail : tomy.wang.1@gmail.com

      刪除
  2. 哈, 真巧. 我是賣菜的小菜ㄈ, 國小ㄅ葉就很少遇到同學. 書豪是國立大學ㄅ葉, 真利害. 超王他家來買菜時, 聽他家人說, 也是國立大學ㄅ葉, 在竹科大公司上班, 股票2, 3百, 真是一個比一個利害.

    回覆刪除
  3. switch 的 csae 好像多了一層縮排耶

    回覆刪除
  4. 作者已經移除這則留言。

    回覆刪除
  5. 受益良多 謝謝!!

    回覆刪除