Linux内核编程规范与代码风格 (5)

内核开发者喜欢被视为有素养的,好的英文拼写和准确的内核信息能给人留下好的印象,因此,不要使用一些单词的缩写,比如 dont,而是 do not 或者 don't。把提示信息写得尽可能准确、清晰、无二义。

内核信息不需要在末尾加上句号

在圆括号中打印数字(%d)没有任何意义,应该避免这样干。

在<linux/device.h>中有许多驱动模型的诊断宏,你应该使用这些宏来确保消息匹配正确的设备和驱动,并正确的标记它们的级别:dev_err(), dev_warn(), dev_info(), and so forth。对于没有关联特定设备的消息,<linux/printk.h>中定义了 pr_notice(), pr_info(), pr_warn(), pr_err(), etc。

编写好的调试信息是一项巨大的挑战,一旦你完成了,这些信息会对远程调试产生巨大帮助。调试信息与普通信息不同,pr_XXX() 函数在任何条件下都会进行打印,而 pr_debug() 却不是,这些与调试有关的函数默认都不会被编译,除非你定义了一个 DEBUG 宏或者 CONFIG_DYNAMIC_DEBUG 宏来显式地让编译器编译他们。还有一个惯例就是使用 VERBOSE_DEBUG 为那些已经开启 DEBUG 的用户添加 dev_vdbg() 消息。

很多子系统在对应的 makefile 里都有 Kconfig 调试选项来打开 -DDEBUG,或者是在文件里定义宏 #define DEBUG。当调试信息可以被无条件打印,或者说已经编译了和调试有关的 #ifdef 段,那么 printk(KERN_DEBUG ...) 就可以用来打印调试信息。

14 分配内存

内核提供了下面这些通用的内存分配器:kmalloc(), kzalloc(), kmalloc_array(), kcalloc(), vmalloc(), and vzalloc()。具体细节参见 API 文档。

为一个结构体分配内存的形式最好是这样的:

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

另一种写出结构体名字的方式(sizeof(struct name))会破坏可读性并且给 bug 制造了机会:修改结构体名字却忘了修改对于的 sizeof 语句。

另外,在 malloc 之前添加上一个强制的类型转换,把空类型的指针转换为特定类型的指针,这些是多此一举的操作,他们应当交给编译器来干,而不是你。

分配一个数组的形式最好是这样的:

p = kmalloc_array(n, sizeof(...), ...);

分配一个零数组的形式最好是这样的:

p = kcalloc(n, sizeof(...), ...);

两种形式都会检查溢出,并且溢出发生时返回一个空指针 NULL。

15 内联之灾

一个很常见的误解就是,人们认为 gcc 有一种让他们的程序跑得更快的魔法,就是内联。然而,内联往往也有不合适的用法(例如第十二节提到的替换宏)。inline 关键字的泛滥,会使内核变大,从而使整个系统运行速度变慢,因为大内核会占用更多的CPU高速缓存,同时会导致可用内存页缓存减少。想象一下,一次页缓存未命中就会导致一次磁盘寻址,这至少耗费5毫秒。5毫秒足够CPU运行很多很多的指令。

一个基本的原则就是,如果一个函数有3行以上的代码,就不要把它变成内联函数。有一个例外,若某个参数是一个编译时常数,且你确定因为这个常量,编译器在编译时能优化掉函数的大部分代码,那么加上 inline 关键字。kmalloc() 就是个很好的例子。

人们经常主张可以给只用一次的静态函数加上 inline 关键字,这样不会有任何损失。虽然从技术上来说这样没错,但是实际上 gcc 会自动内联这些函数。

16 函数返回值与名称

函数可以返回不同种类的值,但是最普遍的就是表示运行成功或失败的值。这样的值可以用预先定义好的错误码表示(-Exxx = failure, 0 = success),或者一个布尔值(0 = failure, non-zero = success)

混合两种方式会使代码变得复杂,并且很难找到 bug。如果C语言能明确区分整型和布尔型,那么编译器会替我们发现这个问题……但是它不会那么做。为了避免这种问题,一定要谨记如下约定:

如果函数名是一个短语,表示的是一个动作,或者一个命令,那么返回值应该使用错误码的方式。 如果函数名是一句话,表示的是一个断言,那么应该使用布尔值的方式。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsfpsf.html