分类: LINUX
2014-09-23 09:11:02
原文地址:Linux内核设计与实现(20)--补丁、开发和社区 作者:leon_yu
Linux最大的优势就是它有一个紧密团结了众多使用者和开发者的社区。
1.社区
(1)Linux kernel mailing list,内核邮件列表
发送一下纯文本信息订阅内核邮件列表
subscribe linux-kernel
(2)
内核邮件列表的FAQ
(3)
适合内核开发初级黑客的乐土。
(4)
Linux新闻周刊,有一个专门报道有关内核的重要新闻。
2.Linux编码风格
Linux指定了一套编码风格,并不是说这套编码风格多么优秀(可能确实还不错)或者你原来风格有多么拙劣,而是因为保持编码风格的一致性,有助于提高编程效率。
(1) 缩进
缩进风格用制表符(Tab),每次缩进8个字符长度,不能用空格代替;八个字符宽度的缩进能让不同的代码块看起来一目了然,特别是在连续几个小时的开发之后,效果更加明显。
(2) switch语句
switch语句下属的case标记应该缩进到与switch声明对齐,这样有助于减少8个字符的tab键的排版缩进。
点击(此处)折叠或打开
当执行逻辑需要有意地从一个case声明尾部进入另外一个case声明时,对齐进行注释。
(3) 空格
空格放在关键字周围,函数名和圆括号之间无空格。
点击(此处)折叠或打开
括号内,参数前后也不假空格;
对于大多数二元或三元操作符,在操作符的两边加上空格;
点击(此处)折叠或打开
对大多数一元操作符,在操作符和操作数之间不加空格;
点击(此处)折叠或打开
在提领运算符的周围加上合适的空格尤为重要,
点击(此处)折叠或打开
(4) 花括号
左括号紧跟在语句的最后,与语句在相同的一行,而右括号新起一行。
函数,左右括号都新起一行;
不需要一定使用括号的语句可以忽略它:
点击(此处)折叠或打开
(5) 每行代码的长度
尽可能地保证每行代码长度不超过80个字符;
可以在圆括号内分行,对齐排列函数参数;
(6) 命名规范
不允许使用骆驼拼写法(CamelCase), Studly Caps,匈牙利命名法或者其他混合的大小写字符。
全局变量和函数,都应该选择包含描述性内容的名称,并且使用小写字母,必要时加上下划线区分单词。
(7) 函数
根据经验,函数的代码长度不应该超过两屏,局部变量不应超过10个。一个函数应该功能单一并且实现精准。
将一个函数分解成一些更短小的函数的组合不会带来危害,如果你担心函数调用导致的开销,可以使用inline关键字。
(8) 注释
一般情况下,你应该描述的是你的代码,要做什么,为什么要做,而不是具体通过什么方式实现的,怎么实现应该由代码本身展现。
此外,注释不应该包含谁写了哪个函数、修改日期和其他那些琐碎而无实际意义的内容,这些信息应该集中在文件开头的地方。
注释中,重要信息常以“xxx:”开头,而bug通常以“FIXME:”开头
点击(此处)折叠或打开
内核包含一套自动文档生成工具,它源自GNOME-doc, 略加修改后命名为Kernel-doc,如果想要生存独立的HTML格式文档,运行
make htmldocs
如果想要postscript格式的话,运行
make psdocs
有关此方面更多细节,参看Documentation/kernel-doc-nano-HOWTO.txt文件。
(9) typedef
内核开发者们强烈返回使用typedef语句,理由是:
typedef掩盖了数据的真实类型;
由于数据类型隐藏起来了,所以很容易因此犯错误,比如以传值的方式向栈中推入结构;
使用typedef往往是因为想要偷懒;
使用typedef的情况:
当需要隐藏变量与体系结构相关的实现细节的时候,当某种类型将来有可能发生变化,而现有程序必须要考虑到向前兼容问题的时候,需要使用typedef.
(10) 多用现成的东西
请勿闭门造车,内核本身就提供了字符串操作函数、压缩函数和一个链表接口,所以请使用它们。
不要为了使现存接口更通用化而对它们进行封装,当移植时,隐藏的接口可能不兼容,所以请直接使用内核提供的接口。
(11) 在源码中减少使用ifdef
不赞成在源码中使用ifdef预处理指令,绝不应该出现如下实现方法:
点击(此处)折叠或打开
正确做法,在CONFIG_FOO没定义的时候,应该让foo()函数为空。
点击(此处)折叠或打开
(12) 结构初始化
内核支持新的C99标识符格式初始化
点击(此处)折叠或打开
对于结构体其他未初始化成员,按ANSIC规定的默认值(指针设为NULL, 整型设为0,浮点型设为0.0).
(13) 代码的事后修正,利用Indent工具,格式化得到GNU风格代码
indent -kr -i8 -ts8 -sob -l80 -ss -bs -psl
还可通过scripts/Lindent自动按照所需的格式调用indent
3.管理系统
所有做出卓越贡献的黑客都能在源码树根目录上的CREDITS文件中留名。
内核中几乎每个部分都对应一个维护者(一个或几个对内核特定部分负责的人)。驱动程序和子系统的维护者也能在源代码树根目录上的MAINTAINERS文件中找到。
还有一类特殊的内核维护者,他们负责维护的实际上就是代码树本身。以前Linus自己负责维护开发板的内核,稳定版最开始的一段时间也有他来维护,等该内核版本稳定下来,他就会把火炬传递给最好的内核开发者中的一些人手上,由他们维护该代码树,而Linus也会启动下一个开发版本的内核开发工作。
4.提交错误报告
提交一个错误报告最重要的莫过于对问题进行清楚的描述,讲清楚症状,系统输出信息,完整并经过解码的oops,更好重要的是,应该尽可能地提供能够准确地重现这个错误的步骤,并提供你机器的硬件配置基本信息。
MAINTAINERS文件列举了每个相关的设备驱动程序和子系统的单独信息—接收关于其所维护的代码的所有问题。如果找不到对此问题感兴趣的人,的内核邮件列表
文档REPORTING-BUGS和Documentation/oops-tracing.txt中有更多相关信息。
5.补丁
对内核的任何修改都是以补丁的形式发布的,补丁其实是GNU diff程序的一种特定格式的输出,该格式的信息能够被patch程序接收。
(1) 创建补丁
创建补丁最简单的办法是通过两份内核源代码进行,一份源码,另一份是加进了修改部分的源代码。比如源码为linux-x.y.x, 修改过的代码起名为Linux,下面命令可以创建补丁:
diff –urN linux-x.y.x linux/ > my-patch
-u是格式化字串,-r参数保证遍历所有子目录,-N指明做出修改的源代码中所有新加入的文件在diff操作时都会包含在内。
如果想单独对一个文件进行diff
diff –u linux-x.y.z/some/file linux/some/file > my-patch
注意,在代码所在目录执行diff很重要,这样方便别人使用补丁。
打补丁:
在自己的源码树下,执行下面命令
patch –p1 < ../my-patch
补丁的名字叫my-patch, 它位于当前目录的上一级目录上,-p1参数用来剥去补丁中头一个目录的名称。
diffstat是一个很有用的工具,它可以列出补丁所引起的变更的统计,输出关于补丁的信息,执行:
diffstat –p1 my-patch
在向lkml贴出自己的补丁时,附带上这份信息往往会很有用。
(2) 用Git创建补丁
用Git管理源代码树时,可以用git工具来生成补丁。
把所做的修改提交到你的Git版本库:
git commit –a
-a参数表示提交所有的修改,如果仅仅想提交某个指定文件
git commit some/file.c
即使有了-a参数,git并不立即提交新文件,知道把它们添加到版本库中才提交。
要增加一个文件,然后再提交
git add some/other/file.c
git commit –a
执行git的commit命令时,git会要求输入一个更改日志,用来详细完整的记载修改缘由。可以为每个提交创建一个补丁
git format-patch origin
对于所有的提交,这样产生的补丁放在你的版本库中而不是原始树中,git产生的补丁位于源码树的根目录中。如果只想为最后第N此提交产生补丁,则可以执行
git format-path -N
(3) 提交补丁
如果补丁涉及了某个特定的驱动或子系统,应该把它发送给MAINTAINER中列举的相关部分的维护者,发送一份拷贝。只有在广泛讨论之后,或者补丁的修改很细微并且很容易就能保证正确的时候,才应该向内恶化维护者(比如Linus)提交。
一个补丁邮件,主题一览应该以“[PATCH] 简要说明”格式写出,邮件主题应该描述所作的改变的技术细节,以及这么做的原因,越详细越好;在Email中还要注明补丁对应的内核版本。
内核开发者们希望能够通过邮件阅读补丁,并且能够将其保存为一个单独文件,因此,最好把补丁直接插入邮件,放在所有信息的最后。
如果你的补丁很大或者包含了几个不同的逻辑的修改,那么应该将你的补丁分成几块,每块对应一个逻辑。如果任何一个部分需要其他的补丁先行,要明确注明这一点。