Linu内核编码风格

上传人:z****2 文档编号:169137303 上传时间:2022-11-14 格式:DOCX 页数:15 大小:34.75KB
收藏 版权申诉 举报 下载
Linu内核编码风格_第1页
第1页 / 共15页
Linu内核编码风格_第2页
第2页 / 共15页
Linu内核编码风格_第3页
第3页 / 共15页
资源描述:

《Linu内核编码风格》由会员分享,可在线阅读,更多相关《Linu内核编码风格(15页珍藏版)》请在装配图网上搜索。

1、这是翻译版本,英文原版是 linux 源码 Documentation 文件夹下的 CodingStyle一个良好风格的程序看起来直观、美观,便于阅读,还能有助于对程序的理解,特别在代码 量比较大情况下更显现编码素质的重要性。相反没有良好的风格的代码读起来难看、晦涩, 甚至有时候一个括号没对齐就能造成对程序的曲解或者不理解。我曾经就遇见过这样的情 况,花费了很多不必要的时间在程序的上下文对照上,还debug 了半天没理解的程序。后来 直接用indent -kr -i8给他转换格式来看了。特此转过来一个关于代码风格的帖子分享一下Linux 内核编码风格这是一份简短的,描述linux内核首选编码风

2、格的文档。编码风格是很个人化的东西,而且 我也不愿意把我的观点强加给任何人,不过这里所讲述的是我必须要维护的代码所遵守的风 格,并且我也希望绝大多数其他代码也能遵守这个风格。所以请至少考虑一下本文所述的观首先,我建议你打印一份GNU的编码规范,然后不要读它。烧掉它,这是一个很高调的具 有象征意义的姿态。Anyway, here goes:第一章:缩进制表符是8个字符,所以缩进也是8个字符。有些异端运动试图将缩进变为4(乃至2)个 字符深,这跟尝试着将圆周率PI的值定义为3没什么两样。理由:缩进的全部意义就在于清楚的定义一个控制块起止于何处。尤其是当你盯着你的屏幕 连续看了 20 小时之后,你将

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

4、break;case M:case m: mem = 20; break;case K:case k: mem y) else 理由:K&R。也请注意这种大括号的放置方式还能使空(或者差不多空的)行的数量最小 化,同时不失可读性。因此,由于你的屏幕上的新行的供应不是可回收的资源(想想 25 行 的终端屏幕),你将会有更多的空行来放置注释。仅有一个单独的语句时,不用加不必要的大括号。if (condition)action();这点不适用于本身为某个条件语句的一个分支的单独语句。这时应该两个分支里都使用 大括号。if (condition) do_this(); do_that(); else

5、otherwise();3.1:空格Linux 内核的空格使用方格(主要)取决于它是用于函数还是关键字。(大多数)关键字后 要加一个空格。值得注意的例外是sizeof、typeof、alignof和_attribute_,这些关键字在一 定程度上看起来更像函数(它们在Linux里也常常伴随小括号使用,尽管在C语言里这样 的小括号不是必需的,就像“struct fileinfo info”声明过后的“sizeof info”)所以在这些关键字之后放一个空格:if, switch, case, for, do, while但是不在sizeof、typeof、alignof或者_attribute_

6、这些关键字之后放空格。例如,s = sizeof(struct file);不要在小括号里的表达式两侧加空格。这是一个反例:s = sizeof( struct file );当声明指针类型或者返回指针类型的函数时, “*”的首选使用方式是使之靠近变量名或者函数名,而不是靠近类型名。例子:char *linux_banner;unsigned long long memparse(char *ptr, char *retptr); char *match_strdup(substring_t *s);在大多数二元和三元操作符两侧使用一个空格,例如下面所有这些操作符:=+-*/% I & 人 =

7、!=?:但是一元操作符后不要加空格:& * + - ! sizeof typeof alignof _attribute_ defined后缀自增和自减一元操作符前不加空格:+ -前缀自增和自减一元操作符后不加空格:+ -“.”和“-”结构体成员操作符前后不加空格。不要在行尾留空白。有些可以自动缩进的编辑器会在新行的行首加入适量的空白,然后你就 可以直接在那一行输入代码。不过假如你最后没有在那一行输入代码,有些编辑器就不会移 除已经加入的空白,就像你故意留下一个只有空白的行。包含行尾空白的行就这样产生了 当 Git 发现补丁包含了行尾空白的时候会警告你,并且可以应你的要求去掉行尾空白;不过 如

8、果你是正在打一系列补丁,这样做会导致后面的补丁失败,因为你改变了补丁的上下文。第四章:命名C是一个简朴的语言,你的命名也应该这样。和Modula-2和Pascal程序员不同,C程序员 不使用类似ThisVariablelsATemporaryCountei这样华丽的名字。C程序员会称那个变量为 “tmp”,这样写起来会更容易,而且至少不会令其难于理解。不过,虽然混用大小写的名字是不提倡使用的,但是全局变量还是需要一个具描述性的名字 称一个全局函数为“foo”是一个难以饶恕的错误。全局变量(只有当你真正需要它们的时候再用它)需要有一个具描述性的名字,就像全局函数。如果你有一个可以计算活动用户数量

9、的函数,你应该叫它,count_active_users()” 或者类似的名字,你不应该叫它cntuser()”。在函数名中包含函数类型(所谓的匈牙利命名法)是脑子出了问题编译器知道那些类型 而且能够检查那些类型,这样做只能把程序员弄糊涂了。难怪微软总是制造出有问题的程序本地变量名应该简短,而且能够表达相关的含义。如果你有一些随机的整数型的循环计数器 它应该被称为“i”。叫它“loop_counter”并无益处,如果它没有可能被误解的话。类似的,tmp” 可以用来称呼任意类型的临时变量。如果你怕混淆了你的本地变量名,你就遇到另一个问题了,叫做函数增长荷尔蒙失衡综合症。请看第六章(函数)。第五章

10、:Typedef不要使用类似“vps_t”之类的东西。对结构体和指针使用typedef是一个错误。当你在代码里看到:vps_t a;这代表什么意思呢?相反,如果是这样struct virtual_container *a;你就知道“a”是什么了。很多人认为typedef“能提高可读性”。实际不是这样的。它们只在下列情况下有用:(a) 完全不透明的对象(这种情况下要主动使用typedef来隐藏这个对象实际上是什么)。例如:“pte_t”等不透明对象,你只能用合适的访问函数来访问它们。注意!不透明性和“访问函数本身”是不好的。我们使用pte_t等类型的原因在于真的 是完全没有任何共用的可访问信息。

11、(b) 清楚的整数类型,这样抽象层就可以帮助我们消除到底是int还是long的混淆。u8/ul6/u32是完全没有问题的typedef,不过它们更符合(d)中所言,而不是这里。再次注 意!要这样做,必须事出有因。如果某个变量是“unsigned long,那么没有必要typedef unsigned long myflags_t;不过如果有一个明确的原因,比如它在某种情况下可能会是一 个unsigned int”而 在其他情况下可能为“unsigned long”,那么就不要犹豫,请务必使用typedef。(c) 当你使用 sparse 按字面的创建一个新类型来做类型检查的时候。(d) 和标准

12、C99类型相同的类型,在某些例外的情况下。虽然让眼睛和脑筋来适应新的标准类型比如,uint32_t”不需要花很多时间,可以有 些人仍然拒绝使用它们。因此,Linux特有的等同于标准类型的“u8/ul6/u32/u64,类型和它们的有符号类型是被允 许的尽管在你自己的新代码中,它们不是强制要求要使用的。当编辑已经使用了某个类型集的已有代码时,你应该遵循那些代码中已经做出的选择。(e)可以在用户空间安全使用的类型。在某些用户空间可见的结构体里,我们不能要求C99类型而且不能用上面提到的“u32” 类型。因此,我们在与用户空间共享的所有结构体中使用_u32 和类似的类型。可能还有其他的情况,不过基本

13、的规则是永远不要使用typedef,除非你可以明确的应用上 述某个规则中的一个。总的来说,如果一个指针或者一个结构体里的元素可以合理的被直接访问到,那么它们就不 应该是一个 typedef。第六章:函数函数应该简短而漂亮,并且只完成一件事情。函数应该可以一屏或者两屏显示完(我们都知 道ISO/ANSI屏幕大小是80x24),只做一件事情,而且把它做好。一个函数的最大长度是和该函数的复杂度和缩进级数成反比的。所以,如果你有一个理论上 很简单的只有一个很长(但是简单)的case语句的函数,而且你需要在每个case里做很多 很小的事情,这样的函数尽管很长,但也是可以的。不过,如果你有一个复杂的函数,

14、而且你怀疑一个天分不是很高的高中一年级学生可能甚至 搞不清楚这个函数的目的,你应该更严格的遵守最大限制。使用辅助函数,并为之取个具描 述性的名字(如果你觉得其对性能要求严格的话,你可以要求编译器将它们内联展开,它往 往会比你更好的完成任务。)函数的另外一个衡量标准是本地变量的数量。此数量不应超过510个,否则你的函数就有 问题了。重新考虑一下你的函数,把它分拆成更小的函数。人的大脑一般可以轻松的同时跟 踪7个不同的事物,如果再增多的话,就会糊涂了。即便你聪颖过人,你也可能会记不清你 2 个星期前做过的事情。在源文件里,使用空行隔开不同的函数。如果该函数需要被导出,它的EXPORT*宏应该紧 贴

15、在它的结束大括号之下。比如:int system_is_up(void)return system_state = SYSTEM_RUNNING;EXPORT_SYMBOL(system_is_up);在函数原型中,包含函数名和它们的数据类型。虽然 C 语言里没有这样的要求,在 Linux 里这是提倡的做法,因为这样可以很简单的给读者提供更多的有价值的信息。第七章:集中的函数退出途径虽然被某些人声称已经过时,但是 goto 语句的等价物还是经常被编译器所使用,具体形式 是无条件跳转指令。当一个函数从多个位置退出并且需要做一些通用的清洁工作的时候, goto 的好处就显现出来了。理由是:- 无条

16、件语句容易理解和跟踪- 嵌套程度减小- 可以避免由于修改时忘记更新某个单独的退出点而导致的错误- 减轻了编译器的工作,无需删除冗余代码;)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;第八章:注释注释是好的,不过有过度注释的危险。永远不要在注释里解释你的代码是如何运作的:更好 的做法是让别人一看你

17、的代码就可以明白,解释写的很差的代码是浪费时间。一般的,你想要你的注释告诉别人你的代码做了什么,而不是怎么做的。也请你不要把注释 放在一个函数体内部:如果函数复杂到你需要独立的注释其中的一部分,你很可能需要回到 第六章看一看。你可以做一些小注释来注明或警告某些很聪明(或者槽糕)的做法,但不要 加太多。你应该做的,是把注释放在函数的头部,告诉人们它做了什么,也可以加上它做这 些事情的原因。当注释内核 API 函数时,请使用 kernel-doc 格式。请看Documentation/kernel-doc-nano-HOWTO.txt和 scripts/kernel-doc 以获得详细信息。Lin

18、ux的注释风格是C89“/* . */”风格。不要使用C99风格“.”注释。长(多行)的首选注释风格是:/* This is the preferred style for multi-line* comments in the Linux kernel source code.* Please use it consistently.* Description: A column of asterisks on the left side,* with beginning and ending almost-blank lines.*/注释数据也是很重要的,不管是基本类型还是衍生类型。为了方

19、便实现这一点,每一行应只 声明一个数据(不要使用逗号来一次声明多个数据)。这样你就有空间来为每个数据写一段 小注释来解释它们的用途了。第九章:你已经把事情弄糟了这没什么,我们都是这样。可能你的使用了很长时间Unix的朋友已经告诉你GNU emacs” 能自动帮你格式化 C 源代码,而且你也注意到了,确实是这样,不过它所使用的默认值和 我们想要的相去甚远(实际上,甚至比随机打的还要差一无数个猴子在GNUemacs里打 字永远不会创造出一个好程序)(译注:请参考 Infinite Monkey Theorem)所以你要么放弃GNU emacs,要么改变它让它使用更合理的设定。要采用后一个方案,你

20、可以把下面这段粘贴到你的.emacs文件里。(defun linux-c-mode ()C mode with adjusted defaults for use with the Linux kernel.(interactive)(c-mode)(c-set-style K&R)(setq tab-width 8)(setq indent-tabs-mode t)(setq c-basic-offset 8)这样就定义了 M-x linux-c-mode 命令。当你 hack 一个模块的时候,如果你把字符串-*- linux-c -*-放在头两行的某个位置,这个模式将会被自动调用。如果你希

21、望在你修改 /usr/src/linux 里的文件时魔术般自动打开 linux-c-mode 的话,你也可能需要添加(setq auto-mode-alist (cons (/usr/src/linux.*/.*.ch$ . linux-c-mode)auto-mode-alist)到你的.emacs文件里。不过就算你尝试让emacs正确的格式化代码失败了,也并不意味着你失去了一切:还可以用 “indent”。不过,GNU indent也有和GNU emacs 一样有问题的设定,所以你需要给它一些命令选项。 不过,这还不算太糟糕,因为就算是GNU indent的作者也认同K&R的权威性(GNU

22、的人 并不是坏人,他们只是在这个问题上被严重的误导了),所以你只要给indent指定选项“-kr -i8”(代表“K&R, 8个字符缩进”),或者使用“scripts/Lindent”,这样就可以以最时髦的方式 缩进源代码。“indent”有很多选项,特别是重新格式化注释的时候,你可能需要看一下它的 手册页。不过记住:“indent”不能修正坏的编程习惯。第十章: Kconfig 配置文件对于遍布源码树的所有Kconfig*配置文件来说,它们缩进方式与C代码相比有所不同。紧 挨在“config”定义下面的行缩进一个制表符,帮助信息则再多缩进2个空格。比如:config AUDITbool Au

23、diting support depends on NEThelpEnable 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 SLUBdepend

24、s on EXPERIMENTAL & !ARCH_USES_SLAB_PAGE_STRUCT bool SLUB (Unqueued Allocator)而那些危险的功能(比如某些文件系统的写支持)应该在它们的提示字符串里显著的声明这 一点:八、config ADFS_FS_RWbool ADFS write support (DANGEROUS) depends on ADFS_FS要查看配置文件的完整文档,请看 Documentation/kbuild/kconfig-language.txt。第十一章:数据结构如果一个数据结构,在创建和销毁它的单线执行环境之外可见,那么它必须要有一个引

25、用计 数器。内核里没有垃圾收集(并且内核之外的垃圾收集慢且效率低下),这意味着你绝对需 要记录你对这种数据结构的使用情况。引用计数意味着你能够避免上锁,并且允许多个用户并行访问这个数据结构而不需要担 心这个数据结构仅仅因为暂时不被使用就消失了,那些用户可能不过是沉睡了一阵或者做了 一些其他事情而已。注意上锁不能取代引用计数。上锁是为了保持数据结构的一致性,而引用计数是一个内存管 理技巧。通常二者都需要,不要把两个搞混了。很多数据结构实际上有2 级引用计数,它们通常有不同“类”的用户。子类计数器统计子类用 户的数量,每当子类计数器减至零时,全局计数器减一。这种“多级引用计数的例子可以在内存管理(

26、“struct mm_struct: mm_users和mm_count)和文件系统(“struct super_block: s_count 和 s_active)中找到。记住:如果另一个执行线索可以找到你的数据结构,但是这个数据结构没有引用计数器,这里几乎肯定是一个bug。第十二章:宏,列举(enum)和RTL定义常量和列举里的标签的宏的名字需要大写。#define CONSTANT 0x12345在定义几个相关的常量时,最好用列举。宏的名字请用大写字母,不过形如函数的宏的名字可以用小写字母 一般的,如果能写成内联函数就不要写成像函数的宏。含有多个语句的宏应该被包含在一个do-while代

27、码块里:#define macrofun(a, b, c)do if (a = 5)do_this(b, c); while (0)使用宏的时候应避免的事情:1) 影响控制流程的宏:#define FOO(x) do if (blah(x) f) 还有可以做严格的类型检查的min()和max()宏,如果你需要可以使用它们。你可以自己看 看那个头文件里还定义了什么你可以拿来用的东西,如果有定义的话,你就不应在你的代码 里自己重新定义。第十八章:编辑器模式行和其他需要罗嗦的事情emacs 能有一些编辑器可以解释嵌入在源文件里的由一些特殊标记标明的配置信息。比如 够解释被标记成这样的行:-*- mode: c -*-或者这样的:/*Local Variables:compile-command: gcc -DMAGIC_DEBUG_FLAG foo.cEnd:*/Vim 能够解释这样的标记:/* vim:set sw=8 noet */不要在源代码中包含任何这样的内容。每个人都有他自己的编辑器配置,你的源文件不应该 覆盖别人的配置。这包括有关缩进和模式配置的标记。人们可以使用他们自己定制的模式 或者使用其他可以产生正确的缩进的巧妙方法。

展开阅读全文
温馨提示:
1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
2: 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
3.本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
关于我们 - 网站声明 - 网站地图 - 资源地图 - 友情链接 - 网站客服 - 联系我们

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!