编码风格——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 文件夹中。开发工具

相关文章
相关标签/搜索