編碼風格——linux內核開發的coding style


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


下面只是羅列一些規則, 具體說明能夠參考: 內核源碼(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;
}vim

一行只有一個表達式
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;
}app

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))))))oop

(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 文件夾中。開發工具

相關文章
相關標籤/搜索