Linux内核编码风格(三)

Chapter 11: Data structures第11章 数据结构Data structures that have visibility outside the single-threaded environment they are created and destroyed in should always have reference counts. In the kernel, garbage collection doesn’t exist (and outside the kernel garbage collection is slow and inefficient), which means that you absolutely _have_ to reference count all your uses.如果一个数据结构在创建和销毁它的单线程环境之外可见,那么它必须要有一个引用计数器。内核里没有垃圾回收机制(并且内核之外的垃圾回收缓慢且效率低下),这意味着你绝对需要记录你对这种数据结构的使用情况。Reference counting means that you can avoid locking, and allows multiple users to have access to the data structure in parallel – and not having to worry about the structure suddenly going away from under them just because they slept or did something else for a while.引用计数意味着你能够避免上锁,并且允许多个用户并行访问这个数据结构——而不需要担心这个数据结构仅仅因为暂时不被使用就消失了,那些用户可能不过是睡眠了一阵或者做了一些其他事情而已。Note that locking is _not_ a replacement for reference counting. Locking is used to keep data structures coherent, while reference counting is a memory management technique. Usually both are needed, and they are not to be confused with each other.注意上锁不能取代引用计数。上锁是为了保持数据结构的一致性,而引用计数是一个内存管理技巧。通常二者都需要,不要把两个搞混了。Many data structures can indeed have two levels of reference counting, when there are users of different "classes". The subclass count counts the number of subclass users, and decrements the global count just once when the subclass count goes to zero.很多数据结构实际上有2级引用计数,它们通常有不同“类”的用户。子类计数器统计子类用户的数量,每当子类计数器减至零时,全局计数器减一。Examples of this kind of "multi-level-reference-counting" can be found in memory management ("struct mm_struct": mm_users and mm_count), and in filesystem code ("struct super_block": s_count and s_active).这种“多级引用计数”的例子可以在内存管理(“struct mm_struct”:mm_users和mm_count)和文件系统(“struct super_block”:s_count和s_active)的代码中找到。Remember: if another thread can find your data structure, and you don’t have a reference count on it, you almost certainly have a bug.记住:如果另一个执行线程可以找到你的数据结构,而且你没有在这个数据结构中使用引用计数,那么这里几乎肯定是一个bug。Chapter 12: Macros, Enums and RTL第12章 宏、枚举和RTLNames of macros defining constants and labels in enums are capitalized.用于定义常量的宏的名字及枚举里的标签需要大写。#define CONSTANT 0x12345Enums are preferred when defining several related constants.在定义几个相关的常量时,最好用枚举。CAPITALIZED macro names are appreciated but macros resembling functions may be named in lower case.宏的名字请用大写字母,不过形如函数的宏的名字可以用小写字母。Generally, inline functions are preferable to macros resembling functions.一般的,如果能写成内联函数就不要写成像函数的宏。Macros with multiple statements should be enclosed in a do – while block:含有多个语句的宏应该被包含在一个do-while代码块里:#define macrofun(a, b, c) \do { \if (a == 5) \do_this(b, c);\} while (0)Things to avoid when using macros:使用宏的时候应避免的事情:1) macros that affect control flow:(1)影响控制流程的宏#define FOO(x)\do { \if (blah(x) < 0)\return -EBUGGERED;\} while(0)is a _very_ bad idea. It looks like a function call but exits the "calling" function; don’t break the internal parsers of those who will read the code.非常不好。它看起来像一个函数,不过却能导致“调用”它的函数退出;不要打乱读者大脑里的语法分析器。2) macros that depend on having a local variable with a magic name:(2)依赖于一个莫名其妙的名字的局部变量的宏#define FOO(val) bar(index, val)might look like a good thing, but it’s confusing as hell when one reads the code and it’s prone to breakage from seemingly innocent changes.可能看起来像是个不错的东西,不过它非常容易把读代码的人搞糊涂,而且容易由看起来不相关的改动带来错误。3) macros with arguments that are used as l-values: FOO(x) = y; will bite you if somebody e.g. turns FOO into an inline function.(3)作为左值的带参数的宏: FOO(x) = y;如果有人把FOO变成一个内联函数的话,这种用法就会出错了。4) forgetting about precedence: macros defining constants using expressions must enclose the expression in parentheses. Beware of similar issues with macros using parameters.忘记了优先级:使用表达式定义常量的宏必须将表达式置于一对小括号之内。带参数的宏也要注意此类问题。#define CONSTANT 0x4000#define CONSTEXP (CONSTANT | 3)The cpp manual deals with macros exhaustively. The gcc internals manual also covers RTL which is used frequently with assembly language in the kernel.cpp手册对宏的讲解很详细。Gcc internals手册也详细讲解了RTL,内核里的汇编语言经常用到它。

Chapter 13: Printing kernel messages

第13章 打印内核消息Kernel developers like to be seen as literate. Do mind the spelling of kernel messages to make a good impression. Do not use crippled words like "dont"; use "do not" or "don’t" instead. Make the messages concise, clear, and unambiguous.内核开发者应该是受过良好教育的。请一定注意内核信息的拼写,以给人以好的印象。不要用不规范的单词比如“dont”,而要用“do not”或者“don’t”。保证这些信息简单、明了、无歧义。Kernel messages do not have to be terminated with a period.内核信息不必以句号(译注:英文句号,即点)结束。Printing numbers in parentheses (%d) adds no value and should be avoided.在小括号里打印数字(%d)没有任何价值,应该避免这样做。There are a number of driver model diagnostic macros in <linux/device.h> which you should use to make sure messages are matched to the right device and driver, and are tagged with the right level: dev_err(), dev_warn(), dev_info(), and so forth. For messages that aren’t associated with a particular device, <linux/printk.h> defines pr_debug() and pr_info().<linux/device.h>里有一些驱动模型诊断宏,你应该使用它们,以确保信息对应于正确的设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err(), dev_warn(), dev_info()等等。对于那些不和某个特定设备相关连的信息,<linux/kernel.h>定义了pr_debug()和pr_info()。Coming up with good debugging messages can be quite a challenge; and once you have them, they can be a huge help for remote troubleshooting. Such messages should be compiled out when the DEBUG symbol is not defined (that is, by default they are not included). When you use dev_dbg() or pr_debug(), that’s automatic. Many subsystems have Kconfig options to turn on -DDEBUG. A related convention uses VERBOSE_DEBUG to add dev_vdbg() messages to the ones already enabled by DEBUG.写出好的调试信息可以是一个很大的挑战;当你写出来之后,这些信息在远程除错的时候就会成为极大的帮助。当DEBUG符号没有被定义的时候,这些信息不应该被编译进内核里(也就是说,默认地,它们不应该被包含在内)。如果你使用dev_dbg()或者pr_debug(),就能自动达到这个效果。很多子系统拥有Kconfig选项来启用-DDEBUG。还有一个相关的惯例是使用VERBOSE_DEBUG来将dev_vdbg()消息添加到那些已经由DEBUG启用的消息之上。

Chapter 14: Allocating memory

第14章 分配内存The kernel provides the following general purpose memory allocators: kmalloc(), kzalloc(), kcalloc(), vmalloc(), and vzalloc(). Please refer to the API documentation for further information about them.内核提供了下面的一般用途的内存分配函数:kmalloc(),kzalloc(),kcalloc()和vmalloc()。请参考API文档以获取有关它们的详细信息。The preferred form for passing a size of a struct is the following:传递结构体大小的首选形式是这样的:p = kmalloc(sizeof(*p), …);The alternative form where struct name is spelled out hurts readability and introduces an opportunity for a bug when the pointer variable type is changed but the corresponding sizeof that is passed to a memory allocator is not.另外一种传递方式中,sizeof的操作数是结构体的名字,这样会降低可读性,并且可能会引入bug。有可能指针变量类型被改变时,而对应的传递给内存分配函数的sizeof的结果不变。Casting the return value which is a void pointer is redundant. The conversion from void pointer to any other pointer type is guaranteed by the C programming language.强制转换一个void指针返回值是多余的。C语言本身保证了从void指针到其他任何指针类型的转换是没有问题的。

Chapter 15: The inline disease

第15章 内联弊病There appears to be a common misperception that gcc has a magic "make me faster" speedup option called "inline". While the use of inlines can be appropriate (for example as a means of replacing macros, see Chapter 12), it very often is not. Abundant use of the inline keyword leads to a much bigger kernel, which in turn slows the system as a whole down, due to a bigger icache footprint for the CPU and simply because there is less memory available for the pagecache. Just think about it; a pagecache miss causes a disk seek, which easily takes 5 milliseconds. There are a LOT of cpu cycles that can go into these 5 milliseconds.有一个常见的误解是内联函数是gcc提供的可以让代码运行更快的一个选项。虽然使用内联函数有时候是恰当的(比如作为一种替代宏的方式,请看第12章),不过很多情况下不是这样。inline关键字的过度使用会使内核变大,从而使整个系统运行速度变慢。因为大内核会占用更多的指令cache而且会导致pagecache的可用内存减少。想象一下,一次pagecache未命中就会导致一次磁盘寻址,将耗时5毫秒。5毫秒的时间内CPU能执行很多很多指令。A reasonable rule of thumb is to not put inline at functions that have more than 3 lines of code in them. An exception to this rule are the cases where a parameter is known to be a compiletime constant, and as a result of this constantness you *know* the compiler will be able to optimize most of your function away at compile time. For a good example of this later case, seethe kmalloc() inline function.一个基本的原则是如果一个函数有3行以上,就不要把它变成内联函数。这个原则的一个例外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在编译时能优化掉你的函数的大部分代码,那仍然可以给它加上inline关键字。kmalloc()内联函数就是一个很好的例子。Often people argue that adding inline to functions that are static and used only once is always a win since there is no space tradeoff. While this is technically correct, gcc is capable of inlining these automatically without help, and the maintenance issue of removing the inline when a second user appears outweighs the potential value of the hint that tells gcc to do something it would have done anyway.人们经常主张给只用了一次的static函数加上inline,如此不会有任何损失,因为没有什么好权衡的。虽然从技术上说这是正确的,但是实际上这种情况下即使不加inline gcc也可以自动使其内联。而且其他用户可能会要求移除inline,由此而来的争论会抵消inline自身的潜在价值,得不偿失。如果可以,我真想和你一直旅行。或许是某个未开发的荒凉小岛,

Linux内核编码风格(三)

相关文章:

你感兴趣的文章:

标签云: