Linux 內核Coding Style整理


總結linux內核開發的coding style, 便於以后寫代碼時參考.

下面只是羅列一些規則, 具體說明可以參考: 內核源碼(Documentation/CodingStyle)

 

01 - 縮進

縮進用 Tab, 並且Tab的寬度為8個字符

swich 和 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;  /* bad example */

 

不要用空格來縮進 (除了注釋或文檔)

 

02 - 代碼行長度控制在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;
}

 

03 - 括號和空格的位置

函數的大括號另起一行

int function(int x)
{   /* 這個大括號 { 另起了一行 */
        body of function
}

 

非函數的語句塊(if, switch, for, while, do)不用另起一行

if (x is true) {  /* 這個大括號 { 不用另起一行 */
        we do y
}

 

只有一行的語句塊不用大括號

if (condition)
        action();

 

如果if用了大括號, 那么else即使只有一行也要用大括號

if (condition) {
        do_this();
        do_that();
} else {
        otherwise();
}

 

下列 keywords 后面追加一個空格

if, switch, case, for, do, while

 

下列 keywords 后面 *不要* 追加一個空格

sizeof, typeof, alignof, __attribute

s = sizeof(struct file);    /* good */
s = sizeof( struct file );  /* bad  */

 

定義指針時, * 緊靠函數名或者變量名

char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);

 

下面的二元和三元操作符左右要留一個空格

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

 

下面的一元操作符后面 *不要* 留空格

&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined

 

后綴操作符(++ --)前面不要留空格

 

前綴操作符(++ --)后面不要留空格

 

結構體成員操作符(. ->)前后都不要留空格

 

每行代碼之后不要有多余的空格

 

04 - 命名

全局變量或函數(在確實需要時才使用)要有個描述性的名稱

count_active_users()  /* good */
cntusr()              /* cnt  */

 

局部變量名稱要簡潔(這個規則比較抽象, 只能多看看內核代碼中其他人的命名方式)

 

05 – Typedefs

盡量不要使用 typedef, 使用typedef主要為了下面的用途:

1. 完全不透明的類型(訪問這些類型也需要對應的訪問函數)

    ex. pid_t, uid_t, pte_t ... 等等

2. 避免整型數據的困擾

    比如int, long類型的長度在不同體系結構中不一致等等, 使用 u8/u16/u32 來代替整型定義

3. 當使用kernel的sparse工具做變量類型檢查時, 可以typedef一個類型.

4. 定義C99標准中的新類型

5. 為了用戶空間的類型安全

    內核空間的結構體映射到用戶空間時使用typedef, 這樣即使內核空間的數據結構有變化, 用戶空間也能正常運行

 

06 - 函數

函數要簡短,一個函數只做一件事情

 

函數長度一般不超過2屏(1屏的大小是 80x24), 也就是48行

 

如果函數中的 switch 有很多簡單的 case語句, 那么超出2屏也可以

 

函數中局部變量不能超過 5~10 個

 

函數與函數之間空一行, 但是和EXPORT* 之間不用空

int one_func(void)
{
        return 0;
}

int system_is_up(void)
{
        return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);

 

07 - 函數退出

將函數的退出集中在一起, 特別有需要清理內存的時候.(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;
}

 

08 - 注釋

注釋code做了什么, 而不是如何做的

 

使用C89的注釋風格(/* ... */), 不要用C99的注釋風格(// ...)

 

注釋定義的數據, 不管是基本類型還是衍生的類型

 

09 - 控制縮進的方法

用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)
                (c-set-style "linux-tabs-only")))))

 

使用 indent 腳本來保證縮進. (腳本位置: scripts/Lindent)

 

10 - Kconfig配置文件

"config" 下一行要縮進一個Tab, "help" 下則縮進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)"
        ...

 

危險的特性要加上 *DANGEROUS*

config ADFS_FS_RW
        bool "ADFS write support (DANGEROUS)"
        depends on ADFS_FS
        ...

 

Kconfig的說明可以參見: Documentation/kbuild/kconfig-language.txt

 

11 - 數據結構

結構體要包含一個引用計數字段 (內核中沒有自動垃圾收集, 需要手動釋放內存)

 

需要保證結構體數據一致性時要加鎖

 

結構體中有時甚至會需要多層的引用計數

ex. struc mm_struct, struct super_block

 

12 - 宏, 枚舉類型和RTL

宏定義常量后者標簽時使用大寫字母

#define CONSTANT 0x12345

 

宏定義多行語句時要放入 do - while 中, 此時宏的名稱用小寫

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

 

宏定義表達式時要放在 () 中

#define CONSTANT 0x4000
#define CONSTEXP (CONSTANT | 3)

 

枚舉類型 用來定義多個有聯系的常量

 

13 - 打印內核消息

保持打印信息的簡明清晰

比如, 不要用 "dont", 而是使用 "do not" 或者 "don't"

 

內核信息不需要使用 "." 結尾

 

打印 "(%d)" 之類的沒有任何意義, 應該避免

 

選擇合適的打印級別(調試,還是警告,錯誤等等)

 

14 - 分配內存

分配內存時sizeof(指針) 而不是 sizeof(結構體)

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

這樣的話, 當p指向其他結構體時, 上面的代碼仍然可用

 

分配內存后返回值轉為 void指針是多余的, C語言本身會完成這個步驟

 

15 - 內聯的弊端

如果一個函數有3行以上, 不要使用 inline 來標記它

 

16 - 函數返回值和名稱

如果函數功能是一個動作或者一個命令時, 返回 int型的 error-code

比如, add_work() 函數執行成功時返回 0, 失敗時返回對應的error-code(ex. -EBUSY)

 

如果函數功能是一個判斷時, 返回 "0" 表示失敗, "1" 表示成功

 

所有Exported函數, 公用的函數都要上述2條要求

 

所有私有(static)函數, 不強制要求, 但最好能滿足上面2條要求

 

如果函數返回真實計算結果, 而不是是否成功時, 通過返回計算結果范圍外的值來表示失敗

比如一個返回指針的函數, 通過返回 NULL 來表示失敗

 

17 - 不要重復發明內核宏

內核定義的宏在頭文件 include/linux/kernel.h 中, 想定義新的宏時, 首先看看其中是否已有類似的宏可用

 

18 - 編輯器模式行和其他

不要在代碼中加入特定編輯器的內容或者其他工具的配置,

比如 emacs 的配置

-*- mode: c -*-
or
/*
Local Variables:
compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
End:
*/

 

vim 的配置

/* vim:set sw=8 noet */

 

每個人使用的開發工具可能都不一樣, 這樣的配置對有些人來說就是垃圾

 

內核代碼格式化工具介紹

checkpatch.pl - 檢查source或者patch是否符合規范

Lindent       - 格式化代碼的縮進

 

這2個工具都在內核代碼樹中的 scripts 文件夾中。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM