内核黑客源代码控制指南
Linux 内核开发涉及的许多问题与传统的软件开发流程不同。当处理内核的某个部分(或特定的驱动程序)时,您需要:1) 了解内核其他部分(您与之交互的部分)正在发生的变化;2) 不断地将您的更改应用到快速发展的内核开发发布计划的移动目标上;3) 解决您所做的更改与其他人所做的更改之间的任何合并冲突;4) 能够以其他人可以轻松使用的格式导出您的更改。
多年来,我开发并维护了 USB 转串口驱动程序,然后最终接手维护内核中的所有 USB 代码。在本文中,我将解释我过去用于这项工作的一些工具,并展示一些新工具如何增强了我掌握内核变化的能力,并让我能够更轻松地完成我的工作。
进行内核工作最常见的方法之一是使用 patch 和 diff 程序。您可以使用这种方法,而无需其他类型的源代码控制系统来进行内核开发。一种方法是使用两个不同的目录树:“干净”的目录树和“工作”的目录树。干净的树是已发布的内核版本,而工作树是基于相同的已发布内核版本,但包含您的修改。然后,您可以使用 patch 和 diff 提取您的更改,并将这些更改向前移植到新的内核版本。例如,让我们从干净的 2.4.18 内核(可在 www.kernel.org/pub/linux/kernel/v2.4/linux-2.4.18.tar.gz 获取)开始,在我们的工作目录中
$ ls linux-2.4.18.tar.gz
解压此内核,然后重命名创建的目录,该目录将称为“linux”,改为有意义的名称
$ tar -zxf linux-2.4.18.tar.gz $ mv linux linux-2.4.18 $ ls linux-2.4.18 linux-2.4.18.tar.gz现在创建此内核版本的副本,并将其命名为其他名称
$ tar -zxf linux-2.4.18.tar.gz $ mv linux linux-2.4.18-greg $ ls linux-2.4.18 linux-2.4.18-greg linux-2.4.18.tar.gz现在我们可以在我们的 -greg 目录中进行所有开发,并保持干净的原始内核目录不变。完成工作后,我们需要创建一个补丁发送给其他人。《Documentation/SubmittingPatches》文件解释了大多数内核开发人员喜欢的发送和接收补丁的正确格式。它还解释了 dontdiff 文件的用法,该文件可以帮助生成这些补丁。dontdiff 文件可以在 www.moses.uklinux.net/patches/dontdiff 找到,其中包含您不希望 diff 程序关注的文件列表。
要创建补丁,请使用以下命令
$ diff -Naur -X dontdiff \ linux-2.4.18 linux-2.4.18-greg/ > my_patch
这将创建一个名为 my_patch 的文件,其中包含您的工作与干净的 2.4.18 内核树之间的差异。然后,可以通过电子邮件将此补丁发送给其他人。
如果发布了新的内核版本,并且您想要将您的更改向前移植到新版本,则需要尝试将您生成的补丁应用到干净的内核版本上。可以通过以下步骤完成
按照前面的示例生成您的原始补丁。
使用来自 kernel.org 的官方补丁,将旧内核版本向前移动一个版本
$ cd linux-2.4.18 $ patch -p1 < ../patch-2.4.19 $ cd .. $ mv linux-2.4.18 linux-2.4.19
通过删除您的补丁,然后应用新的更新,将您的工作目录向前移动一个版本
$ cd linux-2.4.18-greg $ patch -p1 -R < ../my_patch $ patch -p1 < ../patch-2.4.19 $ cd .. $ mv linux-2.4.18-greg linux-2.4.19-greg
尝试将您的补丁应用到新的更新之上
$ cd linux-2.4.19-greg $ patch -p1 < ../my_patch
如果您的补丁未干净地应用,请解决创建的所有冲突(patch 会告诉您这些冲突,并留下 .rej 和 .orig 文件供您使用您喜欢的编辑器进行比较和修复)。如果您对源代码树中已被其他人更改的部分进行了更改,则此合并过程可能是最困难的部分。
如果您使用此开发流程,我强烈建议您获取出色的 patchutils 程序集(可在 cyberelk.net/tim/patchutils 找到)。这些程序使您能够以各种有用的方式轻松地操作文本补丁,并且它们为内核开发人员节省了许多小时的繁琐工作。
使用 patch 和 diff 进行内核开发的流程通常效果很好。但是过了一段时间,大多数人都会厌倦它,并寻找一种不同的工作方式,这种方式不会涉及如此繁琐的打补丁和合并。
几年前,我发现了 BitKeeper(可在 www.bitmover.com 获取),并且从那时起一直将其用于内核开发。它最初使我能够轻松地跟踪内核树的外部更改,并使我几乎毫不费力地向前移植我的内核更改。现在 Linus Torvalds 和 Marcelo Tosatti 正在使用 BitKeeper 进行他们的内核开发,它也使我可以轻松地将补丁发送给他们,以便将其包含到主内核树中。
鉴于 BitKeeper 的许可策略,将 BitKeeper 用作内核开发工具是许多人认为有争议的事情。请仔细阅读许可证,并自行决定是否应该使用它。您还应该浏览 BitMover 网站上的教程,以熟悉该工具和一些不同的命令。
要使用 BitKeeper 进行内核工作,您可以将您的内核基于 Linus 或 Marcelo 的内核树,或者您可以创建自己的内核树,其中包含所有不同的版本。但是,除非您计划使用 BitKeeper 将您的补丁发送给 Linus 或 Marcelo,否则我建议创建您自己的内核树。这样,您就不会被所有不同的内核开发人员正在创建的大量不同变更集所淹没,并且您可以专注于您的工作。
同样,使用 BitKeeper,您最终会创建两个不同的树(或我现在称之为存储库)来进行内核工作:一个干净的树和一个工作树。
要创建干净的 BitKeeper 存储库,请从工作目录中已发布的内核开始
$ ls linux-2.4.18.tar.gz
解压此内核
$ tar -zxf linux-2.4.18.tar.gz $ ls linux linux-2.4.18.tar.gz现在创建一个名为 linux-2.4 的 BitKeeper 项目
$ bk setup linux-2.4BitKeeper 会问您几个问题,然后提供一个文件进行编辑,您应该在其中描述您的项目。使用您喜欢的编辑器填写此文件,然后保存。
现在您将拥有一个名为 linux-2.4 的目录,您的项目将保存在其中。现在将原始内核版本导入到新存储库中
$ ls linux linux-2.4 linux-2.4.18.tar.gz $ bk import -tplain linux linux-2.4
这将需要一些时间。BitKeeper 完成导入所有文件后,我建议使用内核版本号标记此点。这将使您将来更容易找到不同的内核版本
$ cd linux-2.4 $ bk tag LINUX_2.4.18现在克隆该存储库,这是一个干净的内核树,位于不同的目录中,以便您可以进行自己的更改
$ bk clone linux-2.4 greg-2.4我们所有的内核工作都将在 greg-2.4 目录中完成。
您可以使用 -l 选项来 bk clone。这将使用更少的磁盘空间,并通过创建指向元数据文件的硬链接来更快地运行。如果文件被修改,BitKeeper 将断开链接并在需要时创建一个新链接。如果您最终在同一磁盘上创建了许多不同的存储库,则应使用此选项。
完成工作后,在整个开发过程中通过签入我们的更改来创建变更集(有关此操作的更多详细信息,请参阅 BitKeeper 教程),我们希望创建一个补丁来显示我们的更改。可以使用 greg-2.4 目录中的一个简单命令来完成此操作
$ bk export -tpatch -rLINUX_2.4.18..+ -h \ > ../my_patch
这将创建一个补丁,显示从标记版本 (LINUX_2.4.18) 到当前变更集的所有更改,并将其保存在 my_patch 文件中。然后,可以像使用 diff 创建的任何补丁一样,通过电子邮件将此补丁发送给其他人。您会注意到,创建此补丁的过程比以前使用 diff 和 patch 的方法要短得多。
当发布新的内核版本时,您将希望将您的更改向前移植到新版本。这是 BitKeeper 真正优于以前的 patch 和 diff 方法的地方。
首先,转到原始的干净内核树并导入新的补丁
$ ls greg-2.4 linux-2.4 patch-2.4.19 $ cd linux-2.4 $ bk import -tpatch -SLINUX_2.4.19 ../patch-2.4.19 .
如果 BitKeeper 认为补丁文件显示为已创建和删除的任何文件实际上可能是已重命名或在树中移动的文件,它将弹出一个 GUI 工具,您可以使用该工具手动显示哪些文件已重命名,哪些文件只是被删除,哪些文件只是被创建。图 1 显示了此对话框的示例。
现在返回到您的工作存储库并将新的更改拉入其中
$ cd ../greg-2.4 $ bk pull
然后,BitKeeper 会将内核 2.4.18 和 2.4.19 之间的所有更改合并到您的工作存储库中。如果您的任何更改与新内核版本中出现的更改之间存在任何合并冲突,它将报告此情况并询问您想要做什么。我建议使用图形三向合并工具来帮助解决这些冲突。此工具显示原始文件,其中包含您所做的更改和补丁(或其他人)所做的更改。然后,它允许您选择要接受的更改,或者您可以手动编辑文件,将两个更改合并在一起。图 2 显示了我对一个文件所做的更改与主内核中发生的更改冲突的示例。
在您完成解决任何冲突后(而且这是否比手动查看 .rej 文件容易得多?),您可以继续在更新的内核中工作。同样,要导出包含您创建的所有更改的补丁,请在 greg-2.4 目录中使用以下命令
$ bk export -tpatch -rLINUX_2.4.19..+ -h \ > ../my_patch
BitKeeper 还允许您轻松查看特定文件随时间发生的所有更改。您可以查看文件是否被主内核补丁或您自己修改过。图 3 中可以看到我的存储库中 drivers/usb/serial/usbserial.c 文件随时间发生的更改示例。使用此工具,您可以查看同时发生的其他更改,甚至可以看到哪个版本修改了哪行代码。
为内核开发使用 BitKeeper 最强大的好处之一是它是一个非常强大的版本控制系统,它允许您同时与同一代码段上的其他开发人员一起工作。您可以允许其他人从您的工作树中拉取,或者您可以设置本地服务器来存储您的工作树。有关如何设置以及如何使用开发生命周期的一些很好的示例,请参阅 BitKeeper 教程和文档。
我已经展示了两种不同的 Linux 内核开发方法,一种仅使用 patch 和 diff,另一种使用 BitKeeper。就我个人而言,BitKeeper 使我能够花费更多时间实际进行开发工作,而减少了处理合并的时间。在尝试跟踪 Linux USB 和 Linux 热插拔 PCI 驱动程序的 2.2、2.4 和 2.5 内核树时,它也让我保持理智。
