~ubuntu-branches/ubuntu/precise/linux-lowlatency/precise

« back to all changes in this revision

Viewing changes to Documentation/zh_CN/CodingStyle

  • Committer: Package Import Robot
  • Author(s): Alessio Igor Bogani
  • Date: 2011-10-26 11:13:05 UTC
  • Revision ID: package-import@ubuntu.com-20111026111305-tz023xykf0i6eosh
Tags: upstream-3.2.0
Import upstream version 3.2.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
Chinese translated version of Documentation/CodingStyle
 
2
 
 
3
If you have any comment or update to the content, please post to LKML directly.
 
4
However, if you have problem communicating in English you can also ask the
 
5
Chinese maintainer for help.  Contact the Chinese maintainer, if this
 
6
translation is outdated or there is problem with translation.
 
7
 
 
8
Chinese maintainer: Zhang Le <r0bertz@gentoo.org>
 
9
---------------------------------------------------------------------
 
10
Documentation/CodingStyle的中文翻译
 
11
 
 
12
如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
 
13
以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
 
14
 
 
15
中文版维护者: 张乐 Zhang Le <r0bertz@gentoo.org>
 
16
中文版翻译者: 张乐 Zhang Le <r0bertz@gentoo.org>
 
17
中文版校译者: 王聪 Wang Cong <xiyou.wangcong@gmail.com>
 
18
               wheelz <kernel.zeng@gmail.com>
 
19
               管旭东 Xudong Guan <xudong.guan@gmail.com>
 
20
               Li Zefan <lizf@cn.fujitsu.com>
 
21
               Wang Chen <wangchen@cn.fujitsu.com>
 
22
以下为正文
 
23
---------------------------------------------------------------------
 
24
 
 
25
                Linux内核代码风格
 
26
 
 
27
这是一个简短的文档,描述了linux内核的首选代码风格。代码风格是因人而异的,而且我
 
28
不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格,
 
29
并且我也希望绝大多数其他代码也能遵守这个风格。请在写代码时至少考虑一下本文所述的
 
30
风格。
 
31
 
 
32
首先,我建议你打印一份GNU代码规范,然后不要读它。烧了它,这是一个具有重大象征性
 
33
意义的动作。
 
34
 
 
35
不管怎样,现在我们开始:
 
36
 
 
37
 
 
38
                第一章:缩进
 
39
 
 
40
制表符是8个字符,所以缩进也是8个字符。有些异端运动试图将缩进变为4(乃至2)个字符
 
41
深,这几乎相当于尝试将圆周率的值定义为3。
 
42
 
 
43
理由:缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的屏幕
 
44
连续看了20小时之后,你将会发现大一点的缩进会使你更容易分辨缩进。
 
45
 
 
46
现在,有些人会抱怨8个字符的缩进会使代码向右边移动的太远,在80个字符的终端屏幕上
 
47
就很难读这样的代码。这个问题的答案是,如果你需要3级以上的缩进,不管用何种方式你
 
48
的代码已经有问题了,应该修正你的程序。
 
49
 
 
50
简而言之,8个字符的缩进可以让代码更容易阅读,还有一个好处是当你的函数嵌套太深的
 
51
时候可以给你警告。留心这个警告。
 
52
 
 
53
在switch语句中消除多级缩进的首选的方式是让“switch”和从属于它的“case”标签对齐于同
 
54
一列,而不要“两次缩进”“case”标签。比如:
 
55
 
 
56
        switch (suffix) {
 
57
        case 'G':
 
58
        case 'g':
 
59
                mem <<= 30;
 
60
                break;
 
61
        case 'M':
 
62
        case 'm':
 
63
                mem <<= 20;
 
64
                break;
 
65
        case 'K':
 
66
        case 'k':
 
67
                mem <<= 10;
 
68
                /* fall through */
 
69
        default:
 
70
                break;
 
71
        }
 
72
 
 
73
 
 
74
不要把多个语句放在一行里,除非你有什么东西要隐藏:
 
75
 
 
76
        if (condition) do_this;
 
77
          do_something_everytime;
 
78
 
 
79
也不要在一行里放多个赋值语句。内核代码风格超级简单。就是避免可能导致别人误读的表
 
80
达式。
 
81
 
 
82
除了注释、文档和Kconfig之外,不要使用空格来缩进,前面的例子是例外,是有意为之。
 
83
 
 
84
选用一个好的编辑器,不要在行尾留空格。
 
85
 
 
86
 
 
87
                第二章:把长的行和字符串打散
 
88
 
 
89
代码风格的意义就在于使用平常使用的工具来维持代码的可读性和可维护性。
 
90
 
 
91
每一行的长度的限制是80列,我们强烈建议您遵守这个惯例。
 
92
 
 
93
长于80列的语句要打散成有意义的片段。每个片段要明显短于原来的语句,而且放置的位置
 
94
也明显的靠右。同样的规则也适用于有很长参数列表的函数头。长字符串也要打散成较短的
 
95
字符串。唯一的例外是超过80列可以大幅度提高可读性并且不会隐藏信息的情况。
 
96
 
 
97
void fun(int a, int b, int c)
 
98
{
 
99
        if (condition)
 
100
                printk(KERN_WARNING "Warning this is a long printk with "
 
101
                                                "3 parameters a: %u b: %u "
 
102
                                                "c: %u \n", a, b, c);
 
103
        else
 
104
                next_statement;
 
105
}
 
106
 
 
107
                第三章:大括号和空格的放置
 
108
 
 
109
C语言风格中另外一个常见问题是大括号的放置。和缩进大小不同,选择或弃用某种放置策
 
110
略并没有多少技术上的原因,不过首选的方式,就像Kernighan和Ritchie展示给我们的,是
 
111
把起始大括号放在行尾,而把结束大括号放在行首,所以:
 
112
 
 
113
        if (x is true) {
 
114
                we do y
 
115
        }
 
116
 
 
117
这适用于所有的非函数语句块(if、switch、for、while、do)。比如:
 
118
 
 
119
        switch (action) {
 
120
        case KOBJ_ADD:
 
121
                return "add";
 
122
        case KOBJ_REMOVE:
 
123
                return "remove";
 
124
        case KOBJ_CHANGE:
 
125
                return "change";
 
126
        default:
 
127
                return NULL;
 
128
        }
 
129
 
 
130
不过,有一个例外,那就是函数:函数的起始大括号放置于下一行的开头,所以:
 
131
 
 
132
        int function(int x)
 
133
        {
 
134
                body of function
 
135
        }
 
136
 
 
137
全世界的异端可能会抱怨这个不一致性是……呃……不一致的,不过所有思维健全的人都知道(
 
138
a)K&R是_正确的_,并且(b)K&R是正确的。此外,不管怎样函数都是特殊的(在C语言中
 
139
,函数是不能嵌套的)。
 
140
 
 
141
注意结束大括号独自占据一行,除非它后面跟着同一个语句的剩余部分,也就是do语句中的
 
142
“while”或者if语句中的“else”,像这样:
 
143
 
 
144
        do {
 
145
                body of do-loop
 
146
        } while (condition);
 
147
 
 
148
 
149
 
 
150
        if (x == y) {
 
151
                ..
 
152
        } else if (x > y) {
 
153
                ...
 
154
        } else {
 
155
                ....
 
156
        }
 
157
 
 
158
理由:K&R。
 
159
 
 
160
也请注意这种大括号的放置方式也能使空(或者差不多空的)行的数量最小化,同时不失可
 
161
读性。因此,由于你的屏幕上的新行是不可再生资源(想想25行的终端屏幕),你将会有更
 
162
多的空行来放置注释。
 
163
 
 
164
当只有一个单独的语句的时候,不用加不必要的大括号。
 
165
 
 
166
if (condition)
 
167
        action();
 
168
 
 
169
这点不适用于本身为某个条件语句的一个分支的单独语句。这时需要在两个分支里都使用大
 
170
括号。
 
171
 
 
172
if (condition) {
 
173
        do_this();
 
174
        do_that();
 
175
} else {
 
176
        otherwise();
 
177
}
 
178
 
 
179
                3.1:空格
 
180
 
 
181
Linux内核的空格使用方式(主要)取决于它是用于函数还是关键字。(大多数)关键字后
 
182
要加一个空格。值得注意的例外是sizeof、typeof、alignof和__attribute__,这些关键字
 
183
某些程度上看起来更像函数(它们在Linux里也常常伴随小括号而使用,尽管在C语言里这样
 
184
的小括号不是必需的,就像“struct fileinfo info”声明过后的“sizeof info”)。
 
185
 
 
186
所以在这些关键字之后放一个空格:
 
187
        if, switch, case, for, do, while
 
188
但是不要在sizeof、typeof、alignof或者__attribute__这些关键字之后放空格。例如,
 
189
        s = sizeof(struct file);
 
190
 
 
191
不要在小括号里的表达式两侧加空格。这是一个反例:
 
192
 
 
193
        s = sizeof( struct file );
 
194
 
 
195
当声明指针类型或者返回指针类型的函数时,“*”的首选使用方式是使之靠近变量名或者函
 
196
数名,而不是靠近类型名。例子:
 
197
 
 
198
        char *linux_banner;
 
199
        unsigned long long memparse(char *ptr, char **retptr);
 
200
        char *match_strdup(substring_t *s);
 
201
 
 
202
在大多数二元和三元操作符两侧使用一个空格,例如下面所有这些操作符:
 
203
 
 
204
        =  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :
 
205
 
 
206
但是一元操作符后不要加空格:
 
207
        &  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined
 
208
 
 
209
后缀自加和自减一元操作符前不加空格:
 
210
        ++  --
 
211
 
 
212
前缀自加和自减一元操作符后不加空格:
 
213
        ++  --
 
214
 
 
215
“.”和“->”结构体成员操作符前后不加空格。
 
216
 
 
217
不要在行尾留空白。有些可以自动缩进的编辑器会在新行的行首加入适量的空白,然后你
 
218
就可以直接在那一行输入代码。不过假如你最后没有在那一行输入代码,有些编辑器就不
 
219
会移除已经加入的空白,就像你故意留下一个只有空白的行。包含行尾空白的行就这样产
 
220
生了。
 
221
 
 
222
当git发现补丁包含了行尾空白的时候会警告你,并且可以应你的要求去掉行尾空白;不过
 
223
如果你是正在打一系列补丁,这样做会导致后面的补丁失败,因为你改变了补丁的上下文。
 
224
 
 
225
 
 
226
                第四章:命名
 
227
 
 
228
C是一个简朴的语言,你的命名也应该这样。和Modula-2和Pascal程序员不同,C程序员不使
 
229
用类似ThisVariableIsATemporaryCounter这样华丽的名字。C程序员会称那个变量为“tmp”
 
230
,这样写起来会更容易,而且至少不会令其难于理解。
 
231
 
 
232
不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的名字
 
233
。称一个全局函数为“foo”是一个难以饶恕的错误。
 
234
 
 
235
全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字,就像全局函
 
236
数。如果你有一个可以计算活动用户数量的函数,你应该叫它“count_active_users()”或者
 
237
类似的名字,你不应该叫它“cntuser()”。
 
238
 
 
239
在函数名中包含函数类型(所谓的匈牙利命名法)是脑子出了问题——编译器知道那些类型而
 
240
且能够检查那些类型,这样做只能把程序员弄糊涂了。难怪微软总是制造出有问题的程序。
 
241
 
 
242
本地变量名应该简短,而且能够表达相关的含义。如果你有一些随机的整数型的循环计数器
 
243
,它应该被称为“i”。叫它“loop_counter”并无益处,如果它没有被误解的可能的话。类似
 
244
的,“tmp”可以用来称呼任意类型的临时变量。
 
245
 
 
246
如果你怕混淆了你的本地变量名,你就遇到另一个问题了,叫做函数增长荷尔蒙失衡综合症
 
247
。请看第六章(函数)。
 
248
 
 
249
 
 
250
                第五章:Typedef
 
251
 
 
252
不要使用类似“vps_t”之类的东西。
 
253
 
 
254
对结构体和指针使用typedef是一个错误。当你在代码里看到:
 
255
 
 
256
        vps_t a;
 
257
 
 
258
这代表什么意思呢?
 
259
 
 
260
相反,如果是这样
 
261
 
 
262
        struct virtual_container *a;
 
263
 
 
264
你就知道“a”是什么了。
 
265
 
 
266
很多人认为typedef“能提高可读性”。实际不是这样的。它们只在下列情况下有用:
 
267
 
 
268
 (a) 完全不透明的对象(这种情况下要主动使用typedef来隐藏这个对象实际上是什么)。
 
269
 
 
270
     例如:“pte_t”等不透明对象,你只能用合适的访问函数来访问它们。
 
271
 
 
272
     注意!不透明性和“访问函数”本身是不好的。我们使用pte_t等类型的原因在于真的是
 
273
     完全没有任何共用的可访问信息。
 
274
 
 
275
 (b) 清楚的整数类型,如此,这层抽象就可以帮助消除到底是“int”还是“long”的混淆。
 
276
 
 
277
     u8/u16/u32是完全没有问题的typedef,不过它们更符合类别(d)而不是这里。
 
278
 
 
279
     再次注意!要这样做,必须事出有因。如果某个变量是“unsigned long“,那么没有必要
 
280
 
 
281
        typedef unsigned long myflags_t;
 
282
 
 
283
     不过如果有一个明确的原因,比如它在某种情况下可能会是一个“unsigned int”而在
 
284
     其他情况下可能为“unsigned long”,那么就不要犹豫,请务必使用typedef。
 
285
 
 
286
 (c) 当你使用sparse按字面的创建一个新类型来做类型检查的时候。
 
287
 
 
288
 (d) 和标准C99类型相同的类型,在某些例外的情况下。
 
289
 
 
290
     虽然让眼睛和脑筋来适应新的标准类型比如“uint32_t”不需要花很多时间,可是有些
 
291
     人仍然拒绝使用它们。
 
292
 
 
293
     因此,Linux特有的等同于标准类型的“u8/u16/u32/u64”类型和它们的有符号类型是被
 
294
     允许的——尽管在你自己的新代码中,它们不是强制要求要使用的。
 
295
 
 
296
     当编辑已经使用了某个类型集的已有代码时,你应该遵循那些代码中已经做出的选择。
 
297
 
 
298
 (e) 可以在用户空间安全使用的类型。
 
299
 
 
300
     在某些用户空间可见的结构体里,我们不能要求C99类型而且不能用上面提到的“u32”
 
301
     类型。因此,我们在与用户空间共享的所有结构体中使用__u32和类似的类型。
 
302
 
 
303
可能还有其他的情况,不过基本的规则是永远不要使用typedef,除非你可以明确的应用上
 
304
述某个规则中的一个。
 
305
 
 
306
总的来说,如果一个指针或者一个结构体里的元素可以合理的被直接访问到,那么它们就不
 
307
应该是一个typedef。
 
308
 
 
309
 
 
310
                第六章:函数
 
311
 
 
312
函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完(我们都知
 
313
道ISO/ANSI屏幕大小是80x24),只做一件事情,而且把它做好。
 
314
 
 
315
一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上
 
316
很简单的只有一个很长(但是简单)的case语句的函数,而且你需要在每个case里做很多很
 
317
小的事情,这样的函数尽管很长,但也是可以的。
 
318
 
 
319
不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能甚至
 
320
搞不清楚这个函数的目的,你应该严格的遵守前面提到的长度限制。使用辅助函数,并为之
 
321
取个具描述性的名字(如果你觉得它们的性能很重要的话,可以让编译器内联它们,这样的
 
322
效果往往会比你写一个复杂函数的效果要好。)
 
323
 
 
324
函数的另外一个衡量标准是本地变量的数量。此数量不应超过5-10个,否则你的函数就有
 
325
问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟
 
326
踪7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你2
 
327
个星期前做过的事情。
 
328
 
 
329
在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的EXPORT*宏应该紧贴
 
330
在它的结束大括号之下。比如:
 
331
 
 
332
int system_is_up(void)
 
333
{
 
334
        return system_state == SYSTEM_RUNNING;
 
335
}
 
336
EXPORT_SYMBOL(system_is_up);
 
337
 
 
338
在函数原型中,包含函数名和它们的数据类型。虽然C语言里没有这样的要求,在Linux里这
 
339
是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信息。
 
340
 
 
341
 
 
342
                第七章:集中的函数退出途径
 
343
 
 
344
虽然被某些人声称已经过时,但是goto语句的等价物还是经常被编译器所使用,具体形式是
 
345
无条件跳转指令。
 
346
 
 
347
当一个函数从多个位置退出并且需要做一些通用的清理工作的时候,goto的好处就显现出来
 
348
了。
 
349
 
 
350
理由是:
 
351
 
 
352
- 无条件语句容易理解和跟踪
 
353
- 嵌套程度减小
 
354
- 可以避免由于修改时忘记更新某个单独的退出点而导致的错误
 
355
- 减轻了编译器的工作,无需删除冗余代码;)
 
356
 
 
357
int fun(int a)
 
358
{
 
359
        int result = 0;
 
360
        char *buffer = kmalloc(SIZE);
 
361
 
 
362
        if (buffer == NULL)
 
363
                return -ENOMEM;
 
364
 
 
365
        if (condition1) {
 
366
                while (loop1) {
 
367
                        ...
 
368
                }
 
369
                result = 1;
 
370
                goto out;
 
371
        }
 
372
        ...
 
373
out:
 
374
        kfree(buffer);
 
375
        return result;
 
376
}
 
377
 
 
378
                第八章:注释
 
379
 
 
380
注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的:更好
 
381
的做法是让别人一看你的代码就可以明白,解释写的很差的代码是浪费时间。
 
382
 
 
383
一般的,你想要你的注释告诉别人你的代码做了什么,而不是怎么做的。也请你不要把注释
 
384
放在一个函数体内部:如果函数复杂到你需要独立的注释其中的一部分,你很可能需要回到
 
385
第六章看一看。你可以做一些小注释来注明或警告某些很聪明(或者槽糕)的做法,但不要
 
386
加太多。你应该做的,是把注释放在函数的头部,告诉人们它做了什么,也可以加上它做这
 
387
些事情的原因。
 
388
 
 
389
当注释内核API函数时,请使用kernel-doc格式。请看
 
390
Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc以获得详细信息。
 
391
 
 
392
Linux的注释风格是C89“/* ... */”风格。不要使用C99风格“// ...”注释。
 
393
 
 
394
长(多行)的首选注释风格是:
 
395
 
 
396
        /*
 
397
         * This is the preferred style for multi-line
 
398
         * comments in the Linux kernel source code.
 
399
         * Please use it consistently.
 
400
         *
 
401
         * Description:  A column of asterisks on the left side,
 
402
         * with beginning and ending almost-blank lines.
 
403
         */
 
404
 
 
405
注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行应只
 
406
声明一个数据(不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据写一段
 
407
小注释来解释它们的用途了。
 
408
 
 
409
 
 
410
                第九章:你已经把事情弄糟了
 
411
 
 
412
这没什么,我们都是这样。可能你的使用了很长时间Unix的朋友已经告诉你“GNU emacs”能
 
413
自动帮你格式化C源代码,而且你也注意到了,确实是这样,不过它所使用的默认值和我们
 
414
想要的相去甚远(实际上,甚至比随机打的还要差——无数个猴子在GNU emacs里打字永远不
 
415
会创造出一个好程序)(译注:请参考Infinite Monkey Theorem)
 
416
 
 
417
所以你要么放弃GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案,你可
 
418
以把下面这段粘贴到你的.emacs文件里。
 
419
 
 
420
(defun linux-c-mode ()
 
421
  "C mode with adjusted defaults for use with the Linux kernel."
 
422
  (interactive)
 
423
  (c-mode)
 
424
  (c-set-style "K&R")
 
425
  (setq tab-width 8)
 
426
  (setq indent-tabs-mode t)
 
427
  (setq c-basic-offset 8))
 
428
 
 
429
这样就定义了M-x linux-c-mode命令。当你hack一个模块的时候,如果你把字符串
 
430
-*- linux-c -*-放在头两行的某个位置,这个模式将会被自动调用。如果你希望在你修改
 
431
/usr/src/linux里的文件时魔术般自动打开linux-c-mode的话,你也可能需要添加
 
432
 
 
433
(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
 
434
                        auto-mode-alist))
 
435
 
 
436
到你的.emacs文件里。
 
437
 
 
438
不过就算你尝试让emacs正确的格式化代码失败了,也并不意味着你失去了一切:还可以用“
 
439
indent”。
 
440
 
 
441
不过,GNU indent也有和GNU emacs一样有问题的设定,所以你需要给它一些命令选项。不
 
442
过,这还不算太糟糕,因为就算是GNU indent的作者也认同K&R的权威性(GNU的人并不是坏
 
443
人,他们只是在这个问题上被严重的误导了),所以你只要给indent指定选项“-kr -i8”
 
444
(代表“K&R,8个字符缩进”),或者使用“scripts/Lindent”,这样就可以以最时髦的方式
 
445
缩进源代码。
 
446
 
 
447
“indent”有很多选项,特别是重新格式化注释的时候,你可能需要看一下它的手册页。不过
 
448
记住:“indent”不能修正坏的编程习惯。
 
449
 
 
450
 
 
451
                第十章:Kconfig配置文件
 
452
 
 
453
对于遍布源码树的所有Kconfig*配置文件来说,它们缩进方式与C代码相比有所不同。紧挨
 
454
在“config”定义下面的行缩进一个制表符,帮助信息则再多缩进2个空格。比如:
 
455
 
 
456
config AUDIT
 
457
        bool "Auditing support"
 
458
        depends on NET
 
459
        help
 
460
          Enable auditing infrastructure that can be used with another
 
461
          kernel subsystem, such as SELinux (which requires this for
 
462
          logging of avc messages output).  Does not do system-call
 
463
          auditing without CONFIG_AUDITSYSCALL.
 
464
 
 
465
仍然被认为不够稳定的功能应该被定义为依赖于“EXPERIMENTAL”:
 
466
 
 
467
config SLUB
 
468
        depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT
 
469
        bool "SLUB (Unqueued Allocator)"
 
470
        ...
 
471
 
 
472
而那些危险的功能(比如某些文件系统的写支持)应该在它们的提示字符串里显著的声明这
 
473
一点:
 
474
 
 
475
config ADFS_FS_RW
 
476
        bool "ADFS write support (DANGEROUS)"
 
477
        depends on ADFS_FS
 
478
        ...
 
479
 
 
480
要查看配置文件的完整文档,请看Documentation/kbuild/kconfig-language.txt。
 
481
 
 
482
 
 
483
                第十一章:数据结构
 
484
 
 
485
如果一个数据结构,在创建和销毁它的单线执行环境之外可见,那么它必须要有一个引用计
 
486
数器。内核里没有垃圾收集(并且内核之外的垃圾收集慢且效率低下),这意味着你绝对需
 
487
要记录你对这种数据结构的使用情况。
 
488
 
 
489
引用计数意味着你能够避免上锁,并且允许多个用户并行访问这个数据结构——而不需要担心
 
490
这个数据结构仅仅因为暂时不被使用就消失了,那些用户可能不过是沉睡了一阵或者做了一
 
491
些其他事情而已。
 
492
 
 
493
注意上锁不能取代引用计数。上锁是为了保持数据结构的一致性,而引用计数是一个内存管
 
494
理技巧。通常二者都需要,不要把两个搞混了。
 
495
 
 
496
很多数据结构实际上有2级引用计数,它们通常有不同“类”的用户。子类计数器统计子类用
 
497
户的数量,每当子类计数器减至零时,全局计数器减一。
 
498
 
 
499
这种“多级引用计数”的例子可以在内存管理(“struct mm_struct”:mm_users和mm_count)
 
500
和文件系统(“struct super_block”:s_count和s_active)中找到。
 
501
 
 
502
记住:如果另一个执行线索可以找到你的数据结构,但是这个数据结构没有引用计数器,这
 
503
里几乎肯定是一个bug。
 
504
 
 
505
 
 
506
                第十二章:宏,枚举和RTL
 
507
 
 
508
用于定义常量的宏的名字及枚举里的标签需要大写。
 
509
 
 
510
#define CONSTANT 0x12345
 
511
 
 
512
在定义几个相关的常量时,最好用枚举。
 
513
 
 
514
宏的名字请用大写字母,不过形如函数的宏的名字可以用小写字母。
 
515
 
 
516
一般的,如果能写成内联函数就不要写成像函数的宏。
 
517
 
 
518
含有多个语句的宏应该被包含在一个do-while代码块里:
 
519
 
 
520
#define macrofun(a, b, c)                       \
 
521
        do {                                    \
 
522
                if (a == 5)                     \
 
523
                        do_this(b, c);          \
 
524
        } while (0)
 
525
 
 
526
使用宏的时候应避免的事情:
 
527
 
 
528
1) 影响控制流程的宏:
 
529
 
 
530
#define FOO(x)                                  \
 
531
        do {                                    \
 
532
                if (blah(x) < 0)                \
 
533
                        return -EBUGGERED;      \
 
534
        } while(0)
 
535
 
 
536
非常不好。它看起来像一个函数,不过却能导致“调用”它的函数退出;不要打乱读者大脑里
 
537
的语法分析器。
 
538
 
 
539
2) 依赖于一个固定名字的本地变量的宏:
 
540
 
 
541
#define FOO(val) bar(index, val)
 
542
 
 
543
可能看起来像是个不错的东西,不过它非常容易把读代码的人搞糊涂,而且容易导致看起来
 
544
不相关的改动带来错误。
 
545
 
 
546
3) 作为左值的带参数的宏: FOO(x) = y;如果有人把FOO变成一个内联函数的话,这种用
 
547
法就会出错了。
 
548
 
 
549
4) 忘记了优先级:使用表达式定义常量的宏必须将表达式置于一对小括号之内。带参数的
 
550
宏也要注意此类问题。
 
551
 
 
552
#define CONSTANT 0x4000
 
553
#define CONSTEXP (CONSTANT | 3)
 
554
 
 
555
cpp手册对宏的讲解很详细。Gcc internals手册也详细讲解了RTL(译注:register
 
556
transfer language),内核里的汇编语言经常用到它。
 
557
 
 
558
 
 
559
                第十三章:打印内核消息
 
560
 
 
561
内核开发者应该是受过良好教育的。请一定注意内核信息的拼写,以给人以好的印象。不要
 
562
用不规范的单词比如“dont”,而要用“do not”或者“don't”。保证这些信息简单、明了、无
 
563
歧义。
 
564
 
 
565
内核信息不必以句号(译注:英文句号,即点)结束。
 
566
 
 
567
在小括号里打印数字(%d)没有任何价值,应该避免这样做。
 
568
 
 
569
<linux/device.h>里有一些驱动模型诊断宏,你应该使用它们,以确保信息对应于正确的
 
570
设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err(), dev_warn(),
 
571
dev_info()等等。对于那些不和某个特定设备相关连的信息,<linux/kernel.h>定义了
 
572
pr_debug()和pr_info()。
 
573
 
 
574
写出好的调试信息可以是一个很大的挑战;当你写出来之后,这些信息在远程除错的时候
 
575
就会成为极大的帮助。当DEBUG符号没有被定义的时候,这些信息不应该被编译进内核里
 
576
(也就是说,默认地,它们不应该被包含在内)。如果你使用dev_dbg()或者pr_debug(),
 
577
就能自动达到这个效果。很多子系统拥有Kconfig选项来启用-DDEBUG。还有一个相关的惯例
 
578
是使用VERBOSE_DEBUG来添加dev_vdbg()消息到那些已经由DEBUG启用的消息之上。
 
579
 
 
580
 
 
581
                第十四章:分配内存
 
582
 
 
583
内核提供了下面的一般用途的内存分配函数:kmalloc(),kzalloc(),kcalloc()和
 
584
vmalloc()。请参考API文档以获取有关它们的详细信息。
 
585
 
 
586
传递结构体大小的首选形式是这样的:
 
587
 
 
588
        p = kmalloc(sizeof(*p), ...);
 
589
 
 
590
另外一种传递方式中,sizeof的操作数是结构体的名字,这样会降低可读性,并且可能会引
 
591
入bug。有可能指针变量类型被改变时,而对应的传递给内存分配函数的sizeof的结果不变。
 
592
 
 
593
强制转换一个void指针返回值是多余的。C语言本身保证了从void指针到其他任何指针类型
 
594
的转换是没有问题的。
 
595
 
 
596
 
 
597
                第十五章:内联弊病
 
598
 
 
599
有一个常见的误解是内联函数是gcc提供的可以让代码运行更快的一个选项。虽然使用内联
 
600
函数有时候是恰当的(比如作为一种替代宏的方式,请看第十二章),不过很多情况下不是
 
601
这样。inline关键字的过度使用会使内核变大,从而使整个系统运行速度变慢。因为大内核
 
602
会占用更多的指令高速缓存(译注:一级缓存通常是指令缓存和数据缓存分开的)而且会导
 
603
致pagecache的可用内存减少。想象一下,一次pagecache未命中就会导致一次磁盘寻址,将
 
604
耗时5毫秒。5毫秒的时间内CPU能执行很多很多指令。
 
605
 
 
606
一个基本的原则是如果一个函数有3行以上,就不要把它变成内联函数。这个原则的一个例
 
607
外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在编译时能
 
608
优化掉你的函数的大部分代码,那仍然可以给它加上inline关键字。kmalloc()内联函数就
 
609
是一个很好的例子。
 
610
 
 
611
人们经常主张给static的而且只用了一次的函数加上inline,如此不会有任何损失,因为没
 
612
有什么好权衡的。虽然从技术上说这是正确的,但是实际上这种情况下即使不加inline gcc
 
613
也可以自动使其内联。而且其他用户可能会要求移除inline,由此而来的争论会抵消inline
 
614
自身的潜在价值,得不偿失。
 
615
 
 
616
 
 
617
                第十六章:函数返回值及命名
 
618
 
 
619
函数可以返回很多种不同类型的值,最常见的一种是表明函数执行成功或者失败的值。这样
 
620
的一个值可以表示为一个错误代码整数(-Exxx=失败,0=成功)或者一个“成功”布尔值(
 
621
0=失败,非0=成功)。
 
622
 
 
623
混合使用这两种表达方式是难于发现的bug的来源。如果C语言本身严格区分整形和布尔型变
 
624
量,那么编译器就能够帮我们发现这些错误……不过C语言不区分。为了避免产生这种bug,请
 
625
遵循下面的惯例:
 
626
 
 
627
        如果函数的名字是一个动作或者强制性的命令,那么这个函数应该返回错误代码整
 
628
        数。如果是一个判断,那么函数应该返回一个“成功”布尔值。
 
629
 
 
630
比如,“add work”是一个命令,所以add_work()函数在成功时返回0,在失败时返回-EBUSY。
 
631
类似的,因为“PCI device present”是一个判断,所以pci_dev_present()函数在成功找到
 
632
一个匹配的设备时应该返回1,如果找不到时应该返回0。
 
633
 
 
634
所有导出(译注:EXPORT)的函数都必须遵守这个惯例,所有的公共函数也都应该如此。私
 
635
有(static)函数不需要如此,但是我们也推荐这样做。
 
636
 
 
637
返回值是实际计算结果而不是计算是否成功的标志的函数不受此惯例的限制。一般的,他们
 
638
通过返回一些正常值范围之外的结果来表示出错。典型的例子是返回指针的函数,他们使用
 
639
NULL或者ERR_PTR机制来报告错误。
 
640
 
 
641
 
 
642
                第十七章:不要重新发明内核宏
 
643
 
 
644
头文件include/linux/kernel.h包含了一些宏,你应该使用它们,而不要自己写一些它们的
 
645
变种。比如,如果你需要计算一个数组的长度,使用这个宏
 
646
 
 
647
  #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
 
648
 
 
649
类似的,如果你要计算某结构体成员的大小,使用
 
650
 
 
651
  #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))
 
652
 
 
653
还有可以做严格的类型检查的min()和max()宏,如果你需要可以使用它们。你可以自己看看
 
654
那个头文件里还定义了什么你可以拿来用的东西,如果有定义的话,你就不应在你的代码里
 
655
自己重新定义。
 
656
 
 
657
 
 
658
                第十八章:编辑器模式行和其他需要罗嗦的事情
 
659
 
 
660
有一些编辑器可以解释嵌入在源文件里的由一些特殊标记标明的配置信息。比如,emacs
 
661
能够解释被标记成这样的行:
 
662
 
 
663
-*- mode: c -*-
 
664
 
 
665
或者这样的:
 
666
 
 
667
/*
 
668
Local Variables:
 
669
compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
 
670
End:
 
671
*/
 
672
 
 
673
Vim能够解释这样的标记:
 
674
 
 
675
/* vim:set sw=8 noet */
 
676
 
 
677
不要在源代码中包含任何这样的内容。每个人都有他自己的编辑器配置,你的源文件不应
 
678
该覆盖别人的配置。这包括有关缩进和模式配置的标记。人们可以使用他们自己定制的模
 
679
式,或者使用其他可以产生正确的缩进的巧妙方法。
 
680
 
 
681
 
 
682
 
 
683
                附录 I:参考
 
684
 
 
685
The C Programming Language, 第二版, 作者Brian W. Kernighan和Denni
 
686
M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (软皮),
 
687
0-13-110370-9 (硬皮). URL: http://cm.bell-labs.com/cm/cs/cbook/
 
688
 
 
689
The Practice of Programming 作者Brian W. Kernighan和Rob Pike.  Addison-Wesley,
 
690
Inc., 1999.  ISBN 0-201-61586-X.  URL: http://cm.bell-labs.com/cm/cs/tpop/
 
691
 
 
692
cpp,gcc,gcc internals和indent的GNU手册——和K&R及本文相符合的部分,全部可以在
 
693
http://www.gnu.org/manual/找到
 
694
 
 
695
WG14是C语言的国际标准化工作组,URL: http://www.open-std.org/JTC1/SC22/WG14/
 
696
 
 
697
Kernel CodingStyle,作者greg@kroah.com发表于OLS 2002:
 
698
http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/
 
699
 
 
700
--
 
701
最后更新于2007年7月13日。