ImageVerifierCode 换一换
格式:DOC , 页数:17 ,大小:244.50KB ,
资源ID:7231499      下载积分:10 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.docduoduo.com/d-7231499.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录   微博登录 

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Linux内核编码风格.doc)为本站会员(hwpkd79526)主动上传,道客多多仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知道客多多(发送邮件至docduoduo@163.com或直接QQ联系客服),我们立即给予删除!

Linux内核编码风格.doc

1、 Linux 内核编码风格From: iyu.is- linux 内核首选编码风格的文档。编码风格是很个人化的东西,而且我也不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风格,并且我也希望绝大多数其他代码也能遵守这个风格。所以请至少考虑一下本文所述的观点。第一章: 缩进制表符是8个字符,所以缩进也是 8个字符。有些异端运动试图将缩进变为 4(乃至2)个字符深,这跟尝试着将圆周率 PI 的值定义为3没什么两样。理由:缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的屏幕连续看了20小时之后,你将会发现大一点的缩进将会使你更容易分辨缩进现在,有些人会

2、抱怨8个字符的缩进会使代码向右边移动的太远,在80个字符的终端屏幕上就很难读这样的代码。这个问题的答案是,如果你需要3级以上的缩进,不管缩进深度如何你的代码已经有问题了,应该修正你的程序。简而言之,8 个字符的缩进可以让代码更容易阅读,还有一个好处是当你的函数嵌套太深的时候可以向你提出告警。请留意这个警告。在 switch 语句中消除多级缩进的首选的方式是让 “switch”和从属于它的“case ”标签对齐于同一列,而不要“两次缩进” “case”标签。比如:?12345678910111213141516switch (suffix) case G:case g:mem y) 4567.

3、else 理由:K和?1234if (condition)do_this();elsedo_that();这点不适用于本身为某个条件语句的一个分支的单独语句。这时应该两个分支里都使用大括号。?123456if (condition) do_this();do_that(); else otherwise();3.1:空格Linux 内核的空格使用方格(主要)取决于它是用于函数还是关键字。 (大多数)关键字后要加一个空格。值得注意的例外是sizeof、typeof、alignof 和_attribute_,这些关键字在一定程度上看起来更像函数(它们在 Linux 里也常常伴随小括号使用,尽管在

4、C 语言里这样的小括号不是必需的,就像“struct fileinfo info”声明过后的“sizeof info”)所以在这些关键字之后放一个空格:if, switch, case, for, do, while但是不在 sizeof、typeof 、alignof 或者_attribute_ 这些关键字之后放空格。例如,?1 s = sizeof(struct file);不要在小括号里的表达式两侧加空格。这是一个反例:?1 s = sizeof( struct file );当声明指针类型或者返回指针类型的函数时, “*”的首选使用方式是使之靠近变量名或者函数名,而不是靠近类型名。例子

5、:?123char *linux_banner;unsigned long long memparse(char *ptr, char *retptr);char *match_strdup(substring_t *s);在大多数二元和三元操作符两侧使用一个空格,例如下面所有这些操作符:?1 = + - * / % | 这代表什么意思呢?相反,如果是这样?1 struct virtual_container *a;你就知道“a”是什么了。很多人认为 typedef“能提高可读性” 。实际不是这样的。它们只在下列情况下有用:(a) 完全不透明的对象(这种情况下要主动使用 typedef 来隐藏

6、这个对象实际上是什么)例如:“pte_t ”等不透明对象,你只能用合适的访问函数来访问它们。注意!不透明性和“访问函数本身”是不好的。我们使用 pte_t 等类型的原因在于 真的是完全没有任何共用的可访问信息。(b) 清楚的整数类型,这样抽象层就可以帮助我们消除到底是“int“ 还是“long“ 的混淆。u8/u16/u32是完全没有问题的 typedef,不过它们更符合(d)中所言,而不是这里。再次注意!要这样做,必须事出有因。如果某个变量是“unsigned long“,那么没有必要 ?1 typedef unsigned long myflags_t;不过如果有一个明确的原因,比如它在某

7、种情况下可能会是一个“unsigned int”而在其他情况下可能为“unsigned long”,那么就不要犹豫,请务必使用typedef。(c) 当你使用 sparse 按字面的创建一个新类型来做类型检查的时候。(d) 和标准 C99类型相同的类型,在某些例外的情况下。虽然让眼睛和脑筋来适应新的标准类型比如“uint32_t”不需要花很多时间,可以 有些人仍然拒绝使用它们。因此,Linux 特有的等同于标准类型的 “u8/u16/u32/u64”类型和它们的有符号类型是被允许的 尽管在你自己的新代码中,它们不是强制要求要使用的。当编辑已经使用了某个类型集的已有代码时,你应该遵循那些代码中已

8、经做出的选 择。(e) 可以在用户空间安全使用的类型。在某些用户空间可见的结构体里,我们不能要求 C99类型而且不能用上面提到的“u32”类型。因此,我们在与用户空间共享的所有结构体中使用_u32和类似的类型。可能还有 其他的情况,不过基本的规则是永远不要使用 typedef,除非你可以明确的应用上述某个规则中的一个。总的来说,如果一个指针或者一个结构体里的元素可以合理的被直接访问到,那么它们就不应该是一个 typedef第六章:函数函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完(我们都知道 ISO/ANSI 屏幕大小是 80x24) ,只做一件事情,而且把它做好。一个

9、函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上很简单的只有一个很长(但是简单)的 case 语句的函数,而且你需要在每个 case里做很多很小的事情,这样的函数尽管很长,但也是可以的。不过,如果你有一个复杂的函数,而且你怀疑一个天分不是很高的高中一年级学生可能甚至搞不清楚这个函数的目的,你应该更严格的遵守最大限制。使用辅助函数,并为之取个具描述性的名字(如果你觉得其对性能要求严格的话,你可以要求编译器将它们内联展开,它往往会比你更好的完成任务。 )函数的另外一个衡量标准是本地变量的数量。此数量不应超过510个,否则你的函数就有问题了。重新考虑一下你的函数,把它分拆

10、成更小的函数。人的大脑一般可以轻松的同时跟踪7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你2 个星期前做过的事情。在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的 EXPORT*宏应该紧贴在它的结束大括号之下。比如:?12345int system_is_up(void)return system_state = SYSTEM_RUNNING;EXPORT_SYMBOL(system_is_up);在函数原型中,包含函数名和它们的数据类型。虽然 C 语言里没有这样的要求,在Linux 里这是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信

11、息。第七章:集中的函数退出途径虽然被某些人声称已经过时,但是 goto 语句的等价物还是经常被编译器所使用,具体形式是无条件跳转指令。当一个函数从多个位置退出并且需要做一些通用的清洁工作的时候,goto 的好处就显现出来了 。 理由是:- 无条件语句容易理解和跟踪- 嵌套程度减小- 可以避免由于修改时忘记更新某个单独的退出点而导致的错误- 减轻了编译器的工作,无需删除冗余代码;)?1234567891011121314151617181920int fun(int a)int result = 0;char *buffer = kmalloc(SIZE);if (buffer = NULL)r

12、eturn -ENOMEM;if (condition1) while (loop1) .result = 1;goto out;.out:kfree(buffer);return result;第八章:注释注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的:更好的做法是让别人一看你的代码就可以明白,解释写的很差的代码是浪费时间。一般的,你想要你的注释告诉别人你的代码做了什么,而不是怎么做的。也请你不要把注释放在一个函数体内部:如果函数复杂到你需要独立的注释其中的一部分,你很可能需要回到第六章看一看。你可以做一些小注释来注明或警告某些很聪明(或者槽糕)的做法,但不要加太

13、多。你应该做的,是把注释放在函数的头部,告诉人们它做了什么,也可以加上它做这些事情的原因。当注释内核 API 函数时,请使用 kernel-doc 格式。请看Documentation/kernel-doc-nano-HOWTO.txt 和 scripts/kernel-doc 以获得详细信息。Linux 的注释风格是 C89“/* . */”风格。不要使用 C99风格“/ .”注释。长(多行)的首选注释风格是:?12345678/* This is the preferred style for multi-line* comments in the Linux kernel source

14、code.* Please use it consistently.* Description: A column of asterisks on the left side,* with beginning and ending almost-blank lines.*/注释数据也是很重要的,不管是基本类型还是衍生类型。为了方便实现这一点,每一行应只声明一个数据(不要使用逗号来一次声明多个数据) 。这样你就有空间来为每个数据写一段小注释来解释它们的用途了 第九章:你已经把事情弄糟了这没什么,我们都是这样。可能你的使用了很长时间 Unix 的朋友已经告诉你“GNU emacs”能自动帮你格式化

15、 C 源代码,而且你也注意到了,确实是这样,不过它所使用的默认值和我们想要的相去甚远(实际上,甚至比随机打的还要差 无数个猴子在 GNU emacs 里打字永远不会创造出一个好程序)(译注:请参考 Infinite Monkey Theorem)所以你要么放弃 GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案,你可以把下面这段粘贴到你的.emacs 文件里。1234567891011121314151617(defun c-lineup-arglist-tabs-only (ignored)“Line up argument lists by tabs, not space

16、s“(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-offset

17、s-alist(arglist-cont-nonemptyc-lineup-gcc-asm-reg1819202122232425262728c-lineup-arglist-tabs-only)(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-

18、tabs-mode t)(c-set-style “linux-tabs-only“)这样就定义了 M-x linux-c-mode 命令。当你 hack 一个模块的时候,如果你把字符串-*- linux-c -*-放在头两行的某个位置,这个模式将会被自动调用。如果你希望在你修改/usr/src/linux 里的文件时魔术般自动打开 linux-c-mode 的话,你也可能需要添加。 不过,GNU indent 也有和 GNU emacs 一样有问题的设定,所以你需要给它一些命令选项。不过,这还不算太糟糕,因为就算是 GNU indent 的作者也认同 K while (0)使用宏的时候应避免

19、的事情:1) 影响控制流程的宏:?12345#define FOO(x) do if (blah(x) 里有一些驱动模型诊断宏,你应该使用它们,以确保信息对应于正确的设备和驱动,并且被标记了正确的消息级别。这些宏有:dev_err(), dev_warn(),dev_info()等等。对于那些不和某个特定设备相关连的信息,定义了 pr_debug()和pr_info()。写出好的调试信息可以是一个很大的挑战;当你写出来之后,这些信息在远程除错的时候就会成为极大的帮助。当 DEBUG 符号没有被定义的时候,这些信息不应该被编译进内核里(也就是说,默认地,它们不应该被包含在内) 。如果你使用 de

20、v_dbg()或者pr_debug(),就能自动达到这个效果。很多子系统拥有 Kconfig 选项来启用-DDEBUG。还有一个相关的惯例是使用 VERBOSE_DEBUG 来添加 dev_vdbg()消息到那些已经由 DEBUG 启用的消息之上。第十四章:分配内存内核提供了下面的一般用途的内存分配函数:kmalloc(),kzalloc(),kcalloc()和vmalloc()。请参考 API 文档以获取有关它们的详细信息。传递结构体大小的首选形式是这样的:?1 p = kmalloc(sizeof(*p), .);另外一种传递方式中,sizeof 的操作数是结构体的名字,这样会降低可读性

21、,并且可能会引入 bug。有可能指针变量类型被改变时,而对应的传递给内存分配函数的 sizeof 的结果不变。强制转换一个 void 指针返回值是多余的。 C 语言本身保证了从 void 指针到其他任何指针类型的转换是没有问题的。第十五章:内联弊病有一个常见的误解是内联函数是 gcc 提供的可以让代码运行更快的一个选项。虽然使用内联函数有时候是恰当的(比如作为一种替代宏的方式,请看第十二章),不过很多情况下不是这样。inline 关键字的过度使用会使内核变大,从而使整个系统运行速度变慢。因为大内核会占用更多的指令高速缓存(译注:一级缓存通常是指令缓存和数据缓存分开的)而且会导致 pagecac

22、he 的可用内存减少。想象一下,一次 pagecache 未命中就会导致一次磁盘寻址,将耗时5毫秒。5 毫秒的时间内 CPU 能执行很多很多指令。一个基本的原则是如果一个函数有3行以上,就不要把它变成内联函数。这个原则的一个例外是,如果你知道某个参数是一个编译时常量,而且因为这个常量你确定编译器在编译时能优化掉你的函数的大部分代码,那仍然可以给它加上 inline 关键字。kmalloc() 内联函数就是一个很好的例子。人们经常主张给 static 的而且只用了一次的函数加上 inline,不会有任何损失,因为这种情况下没有什么好权衡的。虽然从技术上说,这是正确的,不过 gcc 可以在没有提示

23、的情况下自动使其内联,而且其他用户可能会要求移除 inline,此种维护上的争论会抵消可以告诉 gcc 来做某些事情的提示带来的潜在价值。不管有没有 inline,这种函数都会被内联。第16 章:函数返回值及命名函数可以返回很多种不同类型的值,最常见的一种是表明函数执行成功或者失败的值。这样的一个值可以表示为一个错误代码整数(-E*失败,0成功)或者一个“成功”布尔值(0失败,非0成功) 。混合使用这两种表达方式是难于发现的 bug 的来源。如果 C 语言本身严格区分整形和布尔型变量,那么编译器就能够帮我们发现这些错误不过 C 语言不区分。为了避免产生这种 bug,请遵循下面的惯例:如果函数的

24、名字是一个动作或者强制性的命令,那么这个函数应该返回错误代码整数。如果是一个判断,那么函数应该返回一个“成功”布尔值 。比如, “add work”是一个命令,所以 add_work()函数在成功时返回 0,在失败时返回-EBUSY。类似的,因为“ PCI device present”是一个判断,所 pci_dev_present()函数在成功找到一个匹配的设备时应该返回1,如果找不到时应该返回0。 所有导出(译注:EXPORT)的函数都必须遵守这个惯例,所有的公共函数也都应该如此。私有(static)函数不需要如此,但是我们也推荐这样做。返回值是实际计算结果而不是计算是否成功的标志的函数不

25、受此惯例的限制。一般的,他们通过返回一些正常值范围之外的结果来表示出错。典型的例子是返回指针的函数,他们使用 NULL 或者 ERR_PTR 机制来报告错误。第17章 :不要重新发明内核宏头文件 include/linux/kernel.h 包含了一些宏,你应该使用它们,而不要自己写一些它们的变种。比如,如果你需要计算一个数组的长度,使用这个宏?1 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x)0)类似的,如果你要计算某结构体成员的大小,使用?1 #define FIELD_SIZEOF(t, f) (sizeof(t*)0)-f)还有可以做严格的类型

26、检查的 min()和 max()宏,如果你需要可以使用它们。你可以自己看看那个头文件里还定义了什么你可以拿来用的东西,如果有定义的话,你就不应在你的代码里自己重新定义。第18 章:编辑器模式行和其他需要罗嗦的事情有一些编辑器可以解释嵌入在源文件里的由一些特殊标记标明的配置信息。比如,emacs 能够解释被标记成这样的行:?1 -*- mode: c -*-或者这样的:?12345/*Local Variables:compile-command: “gcc -DMAGIC_DEBUG_FLAG foo.c“End:*/Vim 能够解释这样的标记:?1 /* vim:set sw=8 noet */不要在源代码中包含任何这样的内容。每个人都有他自己的编辑器配置,你的源文件不应该覆盖别人的配置。这包括有关缩进和模式配置的标记。人们可以使用他们自己定制的模式,或者使用其他可以产生正确的缩进的巧妙方法。

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报