如何进行Linux Kernel 开发? (Take 3)
译者序:这是一篇很重要的文档,它介绍了内核开发的方方面面。这篇文档已被加入到内核源码树的Documentation文档里(名字为HOWTO),你可以在最新的内核树里找到它。尽管已经有网友翻译过这篇文档,但是我还是决定自己再翻译一遍。翻译完之后,我的感触是如果依靠翻译来进行学习,速度太慢了。以后的技术文档直接看英文,适当的做做笔记即可。
山涛
-----------------------------------------------------
How to do Linux Kernel development
-----------------------------------------------------
关于如何进行Linux Kernel development,这篇文档是最值得你阅读的一篇。它指导你如何成为一名Linux内核开发者以及如何和Linux内核开发社区一同工作。尽管它不包含内核编程的技能方面的知识,但是本篇能够给你正确的指导去做内核开发。
如果这篇文档讲述的任何东西已经过时了的话,请给这篇文档的维护者发送你的更新。
Greg Kroah-Hartman greg@kroah.com
Introduction
-----------------
你想成为一名Linux内核开发者吗?或者你的老板曾经告诉你:去给某个设备写个Linux驱动程序。这篇文档的目标是,通过描述你进行开发时需要经历的一些流程规则去指导你如何与社区一起工作,教会你所需要的一切从而让你实现你的目标(成为一名合格的内核开发者,或者写出合格的令老板满意的驱动程序);这篇文档也会说明内核社区工作的风格和原因。
内核绝大部分代码是基于C语言编程,与体系结构有关的一小部分由汇编完成。很好的理解和掌握C语言,是内核开发的必备要求。汇编语言(不同的体系结构有不同的汇编语言)不是必需的,除非你计划做体系结构相关的底层开发。如果你想加强C语言的掌握,很好的参考资料如下:
- "The C Programming Language" by Kernighan and Ritchie [Prentice Hall]
- "Practical C Programming" by Steve Oualline [O'Reilly]
Linux内核是使用GNU C和GNU工具链完成的。尽管它遵循ISO C89标准,但是内核的编写也使用了许多的GNU C的扩展特性,这些特性不属于标准的一部分。内核的C编程环境自成体系,不依赖于C标准库,所以C标准的一部分特性没有被支持:例如Arbitrary long long divisions和浮点指针不被支持。有时你会很难理解内核基于GNU工具链的一些假定以及内核使用的一些GNU C扩展,不幸的是对于这类问题没有确定性的参考资料。如果你遇到这类问题,建议你查阅GCC的info pages来获取相关的信息(在Linux PC上,通过命令 info gcc可以获得信息)。
请记住你正在学习如何与已经存在的内核开发社区一起工作。内核开发社区由全球不同地方的开发人员组成,它以代码、风格、开发流程的高质量标准著称。这些高质量的标准使内核开发社区(这个组织非常大,地理位置非常分散)能够非常有效的进行。应当提早努力学习这些高质量标准(编程风格、代码要求以及开发流程),它们有很好的文档;不要期望内核开发社区别的开发人员会适应你自己的或者你公司的开发风格。
Legal Issues
------------------
Linux 内核代码基于GPL许可协议发布。请阅读内核源码树的主目录里的COPYING文件,它提供了GPL许可的详细描述。如果你有关于GPL许可的进一步问题,请联系一名律师,不要在Linux kernel mailing list里询问。Linux kernel mailing list里的开发人员不是律师,所以你不应当听取他们的任何关于法律事务的建议。
对于一些通常的关于GPL许可的问题和解答,请参考:
http://www.gnu.org/licenses/gpl-faq.html
Documentation
---------------------
Linux内核源码树里有大量的非常有用的文档用于学习,使你与内核社区相互促进和共同发展。当一个新的特性要加入到内核里,建议相关的文档也要加入到内核里,用于描述如何使用这个新特性;当一个内核的修改导致了内核提供给用户的接口发生了变化,建议你发送信息或者一个补丁给mtk-manpages@gmx.net,告诉manual pages的维护者用户接口的变化。
这里罗列了一些内核源码树里的需要阅读的文档:
README
这篇文档简要的介绍了Linux内核的背景,描述了配置和build内核需要什么。一个刚刚接触内核的新手应当从这里开始。(注:build kernel,就是编译内核源代码,生成可供系统使用的内核二进制文件(vmlinux/zImage)的过程。
Documentation/Changes
这篇文档给出了一个用于成功编译和运行内核的各种软件包的列表的最小集合。
Documentation/CodingStyle
这篇文档描述了Linux内核编码风格,和一些隐藏在背后的基本原理。所有的想加入内核的新代码应当遵循这篇文档的指导。绝大数的内核代码维护者只愿意接受那些符合这篇文档描述的风格的补丁,许多内核开发者也只愿意审查那些符合Linux内核编码风格的代码。
Documentation/SubmittingPatches
Documentation/SubmittingDrivers
这些文档清楚而又详细地告诉你如何成功的创建和向社区递交一个补丁,包括:
----邮件内容
----邮件格式
----发送者和接收者
遵循文档里提倡的规则并不一定保证你提交补丁成功(因为所有的补丁遭受详细而严格的内容和风格的审查),但是不遵循它们,提交补丁肯定不成功。
其他的一些非常优秀的描述如何正确的创建补丁的文档如下:
"The Perfect Patch"
http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
Documentation/stable_api_nonsense.txt
这篇文档描述了有意决定在内核里没有固定内核API的基本原因,包含下面的讨论主题:
---子系统的shim-layers(为了兼容性?)
---操作系统之间的驱动移植性
---减缓内核源码树的快速变化(或者说,防止快速变化)
这篇文档对于理解Linux的开发哲学非常关键,也对于从其他操作系统转移到Linux上的开发人员非常重要。
Documentation/SecurityBugs
如果你确知你在Linux Kernel里发现了security problem,请遵循这篇文档描述的步骤,帮助通知内核的开发者们并解决这类问题。
Documentation/ManagementStyle
这篇文档描述了Linux内核开发者们如何进行管理运作,以及运作方法背后的分享精神(shared ethos)。这篇文档对于那些内核开发新手们(或者那些好奇者)值得一读,因为它解决或解释了很多对于内核维护者独特行为的误解。
Documentation/stable_kernel_rules.txt
这篇文档描述了一个稳定的内核版本如何发布的规则,以及需要做些什么如果你想把一个修改加入到其中的一个版本。
Documentation/kernel-docs.txt
关于内核开发的外部文档列表。如果你在内核开发的内部文档中找不到你想要的资料,请参考这篇文档提供的资料链接。
Documentation/applying-patches.txt
这篇文档很好地描述了什么是补丁(patch),以及如何将它应用到内核的不同开发分支(branch)上。
内核里也有大量的由内核源码自动生成的文档。其中包括了内核内部API的全面描述,和如何处理好锁的规则。这些文档在Documentation/DocBook/下创建,格式可以是PDF、Postscritpt、HTML和man pages,在内核源码主目录下通过运行下面命令自动生成:
make pdfdocs
make psdocs
make htmldocs
make mandocs
Becoming A kernel Developer
------------------------------------------
如果你对Linux内核开发一点也不了解,你应当看看Linux KernelNewbies的项目:
http://kernelnewbies.org
它包含了很有用的邮件列表,通过这些列表你可以提问任何关于内核开发的基本(基础)问题(在提问之前,最好搜索一下邮件列表的归档,也许你的问题在过去已经被提问和解答了)。它还有一个IRC频道,你可以通过这个频道进行实时的提问。它还包含了大量的有用的文档,帮助你学习Linux内核开发。
这个网站包含了关于代码组织、内核子系统、当前的一些内核项目的基本信息。它也描述了一些基本的内核开发流程的信息,比如怎样编译内核和提交补丁。
如果你还不清楚你想从哪里开始,但你想加入内核开发社区并且找一些任务来做,那么你去看看Linux Kernel Janitor 项目:
http://janitor.kernelnewbies.org/
这是一个非常不错的供你起步的地方。它描述了一些在Linux内核源码树里需要整理和修正并且相对简单的问题的列表。和开发者一起工作,并负责这个项目,你将学习到基础的东西:把你的补丁加入到Linux内核源码树,而且开发者会指导你下一步的工作是什么(如果你还没有自己的计划)。
如果你已经有了一堆代码,想把代码加入到内核树里,但是你想获得一些帮助,使这些代码以适当的形式被内核开发者获取,kernel-mentors项目帮助你解决这个问题。它是一个邮件列表,你可以从下面的网址得到它:
http://selenic.com/mailman/listinfo/kernel-mentors
在对Linux内核代码做出任何实际的修改之前,你必须理解你所修改的这部分内核代码是如何工作的。为此,没有比你通读这些代码更好的办法了,也许可以用一些特殊工具帮助你阅读。其中一个值得推荐的工具是Linux Cross-Reference项目,它能够以网页索引的格式显示源码(每个函数或者变量都有web pag格式的索引链接,如果你想查找代码中的某个使用的函数的定义,直接双击它的链接,你就可以获得)。一个非常不错的Linux Cross-Feference项目网站如下:
http://users.sosdg.org/~qiyong/lxr/
The development process
-------------------------------------
当前,Linux内核开发的过程中包含了几个不同的主内核“分支”,以及许多不同的特定子系统(subsystem-specific)的内核分支。这些不同的分支如下:
---main 2.6.x kernel tree
--- 2.6.x.y -- stable kernel tree
--- 2.6.x -- git kernel patches
--- 2.6.x -- mm kernel patches
--- subsystem specific kernel trees and patches
2.6.x kernel tree
-------------------
2.6.x kernels主要由Linus Torvalds维护,你可以在www.kernel.org上的pub/linux/kernel/v2.6/目录下获取。它的开发过程如下:
--- 当一个新内核发布后,一个为期两周的“窗口”打开,在这段时间里,内核维护者向Linus提交修改比较大的补丁,通常这些补丁已经被包含在-mm kernel树里一段时间了(大约几周)。首选的提交较大的补丁的方法是利用git工具(内核代码管理工具,更多的信息请参考http://git.or.cz/),但是普通的补丁提交方式也是可以的。
--- 两周之后,一个-rcl 内核发布了。现在可以向内核里加入那些不会影响kernel稳定性以及不包含新功能的较小的补丁了。注意一个新的驱动(或者是文件系统)补丁在rcl kenel发布后有可能被接受,只要补丁的代码是self-contained的,不影响内核其他代码的正常运行,这样就不会引起内核回归(内核回归测试,指的是测试新版本的内核加入的补丁是否对内核的其他模块有副作用,导致内核不稳定)。git工具用于在-rcl kernel发布后向Linus发送补丁,但这些补丁也同时需要发送到公开的邮件列表中让内核开发者来审查。
--- 当Linus确信当前的git树已经通过足够健全的、合理的测试,一个新的-rc版本就发布了。社区的目标是每周发布一个新的 -rc内核版本。
--- 这个过程不断的继续,直到整个kernel被认为“ready”了,这个过程大概持续大约六周。
--- 对-rc kernel发布版本,有一个著名的跟踪内核回归测试的列表:
http://kernelnewbies.org/known_regressions
值得一提的是,Andrew Morton在linux-kernel邮件列表里关于内核发布的阐述:
“没有人知道新的内核版本什么时候发布,因为内核发布依赖于我们所认识的内核现有的BUG的状态,而不是我们所制定的时间规划表(timeline)。
2.6.x.y – stable kernel tree
-------------------------------
含有四个数字的内核版本是 -stable kernel分支。-stable kernel分支包含了相对较小的、关键的用于解决安全隐患的修改补丁,或者是针对一个给定的2.6.x kernel所发现的重要的回归修订。
对于那些需要最新的稳定内核的用户,以及对内核测试/开发/实验版本不感兴趣的人,推荐使用这个分支上的内核发布版本(即-stable kernel release)。
如果不存在合适的2.6.x.y内核版本,则数字最高的2.6.x(这里这x数值最高)版本就是当前的稳定内核版本。
2.6.x.y由“stable”团队维护<stable@kernel.org>,通常每隔一周发布一次。
内核树中的文档Documentation/stale_kernel_rules.txt说明了哪些修改补丁能够被-stable kernel tree接受,以及-stable kernel发布过程是如何进行的。
2.6.x -git patches
--------------------
这些补丁是Linus kernel tree的每日快照,由git仓库来管理。这些补丁通常每日发布,并且显示Linus tree中的当前状态。与-rc kernels相比它们更具有实验性,因为它们是自动产生的,几乎没人来浏览一下它们,检查它们是否是健全的。
2.6.x -mm kernel patches
-------------------------------
这些是具有实验性质的内核补丁,由Andrew Morton发布。Andrew负责收集所有的不同的子系统内核树和补丁,还有来自linux-kernel邮件列表中的已经被采纳的补丁,把它们集成在一块。这个内核树分支(--mm kernel tree)给内核的new feature和补丁提供试验场。一旦一个补丁在-mm内核树得到验证和采纳,Andrew或者子系统维护者会将这个补丁发送给Linus,最后集成到主内核中(mainline)。
我们鼓励并推荐新补丁在发送给Linus之前,应担在-mm内核树分支上进行测试和验证。
-mm内核树分支不适于用于正常的用户系统上,因与稳定内核版本相比,它具有更多风险性。
如果你愿意帮助内核开发过程做些事情,请测试和使用这些内核发布版本,如果你遇到任何问题,或者内核工作良好,请通过linux-kernel邮件列表提供你的反馈。
除了包含所有试验性的补丁之外,这个内核分支也通常包含了适用于内核发布的-git内核分支的修改补丁。
-mm内核分支没有固定的发布计划表,但通常情况下在每个–rc内核分支发布周期中会有几个-mm内核分支发布(通常是1到3个)。
Subsystem Specific kernel trees and patches
-----------------------------------------------------
许多的不同内核子系统的开发者们开放了了他们的开发树,这样让其他的内核开发者能够明白内核不同的区域对功能特性。这些开发树将被加入到-mm内核分支进行测试和验证。
下面是一些子系统内涵开发树的列表:
git trees:
- Kbuild development tree, Sam Ravnborg <sam@ravnborg.org>
kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git
- ACPI development tree, Len Brown <len.brown@intel.com>
kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git
- Block development tree, Jens Axboe <axboe@suse.de>
kernel.org:/pub/scm/linux/kernel/git/axboe/linux-2.6-block.git
- DRM development tree, Dave Airlie <airlied@linux.ie>
kernel.org:/pub/scm/linux/kernel/git/airlied/drm-2.6.git
- ia64 development tree, Tony Luck <tony.luck@intel.com>
kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
- ieee1394 development tree, Jody McIntyre <scjody@modernduck.com>
kernel.org:/pub/scm/linux/kernel/git/scjody/ieee1394.git
- infiniband, Roland Dreier <rolandd@cisco.com>
kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git
- libata, Jeff Garzik <jgarzik@pobox.com>
kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
- network drivers, Jeff Garzik <jgarzik@pobox.com>
kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
- pcmcia, Dominik Brodowski <linux@dominikbrodowski.net>
kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git
- SCSI, James Bottomley <James.Bottomley@SteelEye.com>
kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
其他的git内核开发树可以在http://kernel.org/git下找到。
quilt trees:
- USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman <gregkh@suse.de>
kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
Bug Reporting
-------------------
Bugzilla.kernel.org是linux内核开发者用于追踪内核bug的网址。用户们被鼓励利用这个工具报告他们所发现的bug。关于kernel bugzilla对详细描述,请参考:
http://test.kernel.org/bugzilla/faq.html
在内核源码树主目录下的文档REPORTING-BUGS是个很好的报告内核bug的模板,它也详细地描述了内核开发者需要什么样的信息用来解决bug。
Mailing lists
---------------
正如上述文档所描述的,主要的核心内核开发者们加入Linux Kernel邮件列表。如何订阅和取消订阅这个邮件列表的描述请参考:
http://vger.kernel.org/vger-lists.html#linux-kernel
在web的许多不同的地方保存了这个邮件列表的归档。使用一个搜索引擎来找到这些归档。例如:
http://dir.gmane.org/gmane.linux.kernel
强烈建议在你想提问之前,搜索一下归档里的内容是否含有你所要的答案。许多问题已经被详细地讨论过了,它们存放在归档中。
许多内核子系统的开发组有他们自己独立的邮件列表,来讨论他们针对自己子系统的开发问题。查看MAINTAINERS文件,获得不同开发组对应的各自的邮件列表。
许多邮件列表被记录在www.kernel.org网站上,他们的信息也可以从下面网站获得:
http://vger.kernel.org/vger-lists.html
在使用这些邮件列表时,请记住遵循一个良好的行为习惯。下面的URL提供了一个简单的guideline告诉你如何和开发组的邮件列表进行有效交互:
http://www.albion.com/netiquette/
如果很多人回复你的mail,邮件的CC(接收者list)会变得很大。不要删除CC上的任何人,或者不要只回复发送者。习惯接收一封邮件两次,一次来自于发送者,一次来自于邮件群列表;不要搞个标新立异的邮件标题,开发者们不喜欢这样做。
记住保证邮件内容和你的回复的完整性。在你的回复内容的上面保留“John Kernelhacker wrote …: “行,在你引用邮件内容的后面添加你的个人评论(或陈述)。
如果你要在你的邮件里增加你的补丁,确保它们是清晰可读的text文本(参考Documentation/SubmittingPatches文档)。内核开发者不愿意处理邮件附件或者是压缩过的补丁;他们会在你的邮件里查看你的补丁时注上他们的评论。确保你使用的邮件程序不会导致邮件的内容空间混乱或者tab符的滥用。一个好的测试办法是首先你给自己发送邮件,然后试图使用邮件里的补丁是否可用。如果补丁不工作,你得修改你的邮件直到能够工作。
综上所述,当你发邮件到时候,请记住要对其他的订阅者(包括内核开发者)表达你的尊重。
Working with the community
-------------------------------------
内核社区的目标是最大可能的提供一个最好的内核。当你提交一个补丁让社区接受时,它将被从技术的优劣角度进行审查。所以,你期望的应当是什么?
----批评
----评论
----修改的请求
----辩护的请求
----沉默
记住,这是让你的补丁进入内核过程中的一部分。你必须能够接受关于你的补丁的任何批评和评论,从技术的角度考虑这些批评和评论,重新修改补丁,或者提出清晰而简明的理由说明为什么补丁不能这样修改。如果你的邮件没有得到回复,请耐心等几天,然后重新尝试一次,有时由于巨大的邮件量会把你的邮件给淹没。
你不应当做什么?
----期望你的补丁在没有任何异议下被接受
----防御别人的评论
----忽略别人的评论
----没有听取任何建议,重新提交你的原来的补丁。
在社区里,追求的是使用最好的技术解决方案,通常有不同的建议关于让一个补丁工作得更好。你必须有合作精神,能够调整你的思路来适应更好的内核补丁方案修改,或者至少能够证明你的方案思路是有意义的。记住,一个事实证明错误的方案是可以原谅的,只要你愿意接受对的方案思路。
你的第一个补丁提交后,你会得到记录你需要做某些更正修改的列表。这并不意味着你的补丁不能被接受,也不意味着社区反对你个人。你仅仅需要做的是,修改完这些问题,然后重新提交你的补丁。
Differences between the kernel community and corporate structures
------------------------------------------------------------------------------------------
内核社区的工作模式不同于那些传统公司的开发环境。下面是一些注意事项,你应当努力去学习这些事项,以避免额外的一些问题。
关于你的被提议的修改,好的说法:
--- “这个补丁能够解决很多问题”
--- “这个补丁能够帮助删除2000行代码”
--- “这个补丁正是我想要描述的修改”
--- “这里有一些列小的补定。。。”
--- “这个补丁会提高性能。。。”
一些你需要避免的不好的说法:
---“我们在AIX/ptx/Solaris上对它进行测试过,所以它肯定好…”
---“我干这个已经20多年了,所以…”
---“这个补丁应用在我们企业的产品线上…”
---“这里有我的1000多页的设计文档,关于描述我的想法”
---“我做这个补丁已经有6个月的时间了…”
---“这里有一个5000行当补丁…”
---“我重写了当前的所有的混乱的代码,它是…”
---“我有一个最终期限,这个补丁需要现在能被接受”
内核社区区别于传统软件工作环境的另外一个不同之处在于内核开发者不用面对面的交互。使用邮件和在线聊天工具作为开发者沟通的主要形式,好处是不会存在性别歧视。Linux内核工作环境接受女人和少数民族进行开发,因为标记你的仅仅是一个邮件地址。国际化的视角让大家处于同一个公平的运动场内(没有任何歧视行为),因为你通过一个开发者的网民无法猜测到他的性别。一个man也许会取名Andrea,而一个woman也可以取名Pat。绝大部分为Linux内核工作的妇女,已经表达了她们的良好的经历。
对于一些不熟悉English的人来说,语言的障碍会带来一些问题。掌握好English是必须的,这样你才可以在邮件里把你的思路描述得清楚。建议你在发送邮件之前,确认你的邮件用英文描述得很清楚。
Break up your changes
------------------------------
Linux内核社区不乐意一次接受一大块代码,内容较大的修改需要进行适当的介绍,讨论,然后划分为细小的比较独立的若干部分。这种做法与传统公司截然相反。你的提议应当在开发过程的早期介绍给大家,这样你可以得到内核社区的及时反馈,也使得社区知道你一直在为这个提议工作,而不是认为你只是简单得为你自己完成的补丁做推销(倾销)。然而,不要一次在邮件列表里发送50封邮件,你的补丁系列邮件应当总是小于这个数目。
这种“分拆大补丁”的方法有如下几个理由:
1)
小补丁更容易被内核社区接受,因为他们不需要花费太多的时间或精力去验证或修正。一个5行当补丁也许被内核维护者在几秒内的浏览后就接受。然而,一个500行当补丁也许要花好几个小时进行审查和修订(审查时间随着代码量的增长是指数级的增长)。
小补丁也非常容易调试,当你发现哪出错了。与大块头补丁比起来,你很容易通过摘掉一个个小补丁来定位出错的位置。
2)
不仅发送小补丁很重要,而且在提交它们之前简化它们也很重要。
这里是内核开发者Al Viro的观点:
“想象一个数学老师评判一个学生的作业的情况。老师并不想看到学生在提出一个解答方案之前的草稿(上面有各种尝试和错误)。老师想看到是一个干净的非常优雅的解答方案。一个优秀的学生知道这一点,所以他不会把草稿(不是最终方案)交给老师。
对于内核开发道理同样。内核维护者和审查者并不想看你解决问题过程中的思路活动,而是想看你提供的最终的简洁而又优雅的解决方案。”
要在你给出一个优雅的解答方案和与社区一起工作来讨论你的未完成工作之间做出一个平衡,也许很有挑战性。好的做法是,在开发过程的早期尽可能地得到反馈,来改进你的工作,同时保持你的补丁比较小,即使你的整个完成任务还没有准备就绪。
当然你也得认识到发送的补丁里不可能包含这样的信息:“当前还没有完成,以后再做。”
Justify your change
-------------------------
当你拆分了你的大补丁后,你必须让内核社区明白为什么要加入你的修改。New features必须被证明是需要的和有用的。
Document your change
-------------------------------
当发送你的补丁的时候,你需要在你的邮件里写一些关于补丁的必要信息。这些信息会成为补丁的ChangeLog,并且永久保存下来提供给给个需要的人。这些信息应当包括:
---为什么修改是必需的
---简要的补丁设计思路
---实现细节
---测试结果
关于这些信息的详细描述,请参考文档:”The Perfect Patch”
http://www.zip.com.au/~akpm/linux/patches/stuff/tpp.txt
上述的所有关于内核开发的行为,有时候很困难做到。这将需要数年使这些实践行为完美。大量不断的的实践和决定也在不断地改进内核开发行为。但是不要放弃,一切皆有可能。很多人(内核开发者)已经付诸了大量的实践,而你们(新手)也在此开始踏上内核开发之旅。