如何给Linux内核打补丁
Linux Kernel邮件列表上问的最多的一个问题就是:如何给Linux内核打补丁,更具体点就是,诸多trees/branches的一个补丁应该应用于哪个base kernel。希望这个文档能帮你解答这些疑问。
本文除了介绍如何打补丁以及撤销补丁,还介绍了不同的内核树。
#什么是补丁?
补丁就是一个文本文件,该文件包含了一个源码树的两个不同版本之间增量变化。补丁文件是由diff
命令创建的。
为了正确打一个补丁,你需要知道这个补丁是从哪个根(base)产生的,以及这个补丁会使源码树升级成哪个版本。这些应该是出现在补丁文件的元数据(metadata),或者可以从文件名来推断。
#如何应用和撤销补丁?
通过patch
命令应用一个补丁,patch命令读取一个diff/patch文件,根据文件中的描述信息来改变源码树。
Linux内核补丁相对于包含内核源码目录的目录生成。
这表示补丁文件中的文件路径包含内核源代码目录名字(或者其它目录名如’a/’和’b/’).
由于它不太可能匹配你本地内核源码目录的名字(但通常是非常有用的信息,用来查看生成补丁的版本),你应该进入你的内核源码目录,然后过滤掉补丁文件中的所有文件名路径的第一个元素(patch命令的 -p1选项有这个功能)。
如果要撤销当前已经打好的补丁,使用patch命令的 -R选项。 所以,如果你使用如下命令打补丁:
patch -p1 < ../patch-x.y.z
你可以撤销该补丁,使用下面的命令:
patch -R -p1 < ../patch-x.y.z
#怎么给patch命令提供(feed)一个patch/diff文件?
这里有几种不同的方法(通常是Linux和其它类UNIX系统)。
在下面所有的例子中,我使用下面的语法,通过标准输入stdin提供(feed)补丁文件(未压缩形式):
patch -p1 < path/to/patch-x.y.z
如果你的补丁文件用gzip或bzip2压缩,而你不想解压后打补丁,那么你可以这样来应用补丁:
zcat path/to/patch-x.y.z.gz | patch -p1
bzcat path/to/patch-x.y.z.bz2 | patch -p1
如果你想在打补丁之前手动解压补丁文件(我假定你已经进行过下面的操作),那么你可以简单的运行gunzip或者bunzip2,如下:
gunzip patch-x.y.z.gz
bunzip2 patch-x.y.z.bz2
上面的命令将产生一个纯文本的patch-x.y.z文件,你可以通过标准输入(stdin,加上管道)或者-i选项把这个文件输入到patch命令.
patch命令的其它有用选项:
-s 在patch执行过程中只输出错误信息。
--dry-run 只是输出操作清单。
--verbose 让patch输出尽可能多的信息。
#打补丁时的常见错误
当patch命令应用一个补丁文件时,它会用不同的方法验证补丁文件的完整性。
patch命令做的两个完整性检查:
检查文件是不是一个有效的补丁文件;
检查修改的bits附近的代码是否匹配补丁文件中相应位置上下文;
如果patch命令执行过程中遇到一些不太正确(not ‘quite right’)的问题,它有两个选择,一是拒绝修补更新变化,退出;二是尝试找到一个方法对补丁做一些小的改动。
一个例子(补丁不太正确,patch命令试图修复):上下文匹配,需要修改的行匹配,但是行号不匹配。这是可能发生的,例如,patch命令对文件做一些变动,但是由于某些原因,在文件开始处有几行需要被添加或者删除。在这种情况下,一切看起来都没问题,它只是向上或向下移动一下,patch命令通常会调整行号,然后修补补丁。
每当patch命令使用它不得不进行稍微修改的补丁文件时,它将提示”补丁通过’fuzz’被使用“来告诉你。你应该警惕这种变化,因为即使patch命令可能让补丁文件正确,它不可能总使补丁文件正确,结果有时可能会出错。
当patch命令遇到它不能通过fuzz修补的变化时,它完全拒绝这个文件,并且生成一个扩展名为.rej的文件(一个reject file)。你能够从这个rej文件找出哪个change不能被修补,所以你能够手动修复它。
如果你没有第三方补丁来修补你的内核源码,只有从kernel.org获得的补丁,以正确的顺序打补丁,不对源文件作出修改(译者注:应该是指补丁源文件,这里要说明的意思是,你使用完整的kernel.org上的补丁文件,使用patch命令执行基本不会出问题),那么你应该看不到来自patch命令的fuzz或者reject消息。但是如果你看到了fuzz或者reject消息,那么这可能是一个高风险状况:你的本地source tree或者补丁文件损坏了。在这种情况下,你应该尝试重新下载补丁,如果问题还没有解决,建议你从kernel.org下载一个全新的source tree(译者注:应该是完整的内核源码,因为这个问题可能说明你本地内核代码被恶意篡改了).
让我们看一下patch命令可能产生的其它信息。
如果patch命令停止,出现”File to patch:”提示,说明patch命令没有找到补丁文件。你很可能忘记了指定 -p1选项,或者你的当前工作目录不对。其它情况,补丁文件需要 -p0选项而不是-p1选项(如果是这种情况,阅读补丁文件就可以发现,如果是的话,这是由写补丁的人犯的错误,这不算严重)。
如果显示”Hunk #2 succeeded at 1887 with fuzz 2 (offset 7 lines).”或者类似这个信息的,这说明patch命令不得不调整change的位置(上面这个例子中,它需要移动7行)。由于补丁文件和期望的文件不同,结果文件可能是也可能不是正确的。如果试图将一个内核版本的补丁应用到另一个不同版本的内核上,经常会出现这种提示消息。
如果显示”Hunk #3 FAILED at 2387.”,说明这个补丁文件不能够正确修补,patch命令用fuzz方法也不能正确执行。这将会产生一个.rej文件,该文件包含导致patch命令失败的change,还会产生一个.orig文件,该文件包含不能改变的原始内容。
如果显示”Reversed (or previously applied) patch detected! Assume -R? [n]”,说明patch命令检测到补丁文件中的change已经修补了内核。 如果你之前确实打过该补丁,你只是误操作,那么输入[n]o来中止patch命令。如果你之前打过该补丁,现在想撤销它,却忘记了指定-R选项,那么输出[y]es来撤销补丁。 如果补丁的作者在创建补丁时reversed the source 和目标目录,这也会发生,在这种情况下,恢复/撤销补丁将在实际中应用它。
如果显示类似”patch: ** unexpected end of file in patch” 或者 “patch unexpectedly ends in middle of line”的消息,表示patch命令不能正确解析你输入的补丁文件。或许是你下载的文件损坏了,你试图输入一个压缩的补丁文件,或许是你使用的补丁文件由于邮件客户端/邮件传输代理在传输过程中损坏了,例如,通过分隔长行为两行。往往这些警告可以很容易的修复,通过连接被分隔的两行。
正如我上面已经提到的,如果将从kernel.org获取的补丁应用到正确的未经修改的源码树上,将不会出现这些错误。所以,如果你使用kernel.org上的补丁,却还是遇到这些错误,那么应该是你的补丁文件或者你的内核源码树损坏了,建议你重新下载一个完整的内核树和你想使用的补丁文件。
#有patch命令的替代选择吗?
Yes,当然有其它选择。
你可以使用interdiff
命令 (http://cyberelk.net/tim/patchutils/)来生成一个表示两个补丁差别的补丁,然后使用该补丁。
这允许你一步从2.6.12.2升级到2.6.12.3。interdiff命令的-z选项允许你直接输入gzip和bzip压缩文件,而不必先使用zcat和bzcat或者解压操作。
下面命令演示了如何通过一步操作完成从2.6.12.2到2.6.12.3的升级:
interdiff -z ../patch-2.6.12.2.bz2 ../patch-2.6.12.3.gz | patch -p1
尽管interdiff命令可以为你节省一两步操作,还是建议你加上其它操作,因为interdiff在一些情况下很容易出错。
另外一个选择是ketchup
命令,它是一个Python脚本,可以自动下载补丁和打补丁(http://www.selenic.com/ketchup/).
其它不错的工具有diffstat, 它显示一个补丁文件的change概要;lsdiff,它展示在补丁文件中受影响的文件列表,并且标识(可选的)每个补丁开始的行号;grepdiff,它展示一个补丁修改文件列表,这个补丁包含一个给定的正则表达式。
#在哪里可以下载补丁?
在http://kernel.org/网站可以下载,最新的补丁程序被链接到首页,它们也有自己特定主页。
2.6.x.y (-stable) 和 2.6.x 补丁位于:
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/
-rc补丁位于:
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/testing/
-git补丁位于:
ftp://ftp.kernel.org/pub/linux/kernel/v2.6/snapshots/
-mm补丁位于:
ftp://ftp.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/
在ftp.kernel.org服务器上,你可以使用ftp.cc.kernel.org,cc是一个国家代号(country ocde)。这样你就可以从一个地理位置离你最可能近的镜像网站下载,获得更快的下载速度,减少全球范围的带宽,降低kernel.org主服务器的负载——这都是好的事情,所以尽可能的使用镜像网站。
#2.6.x内核
这些都是由Linus发布的base stable发行版。编号最高的发行版是最新的。
如果regressions或者其它严重缺陷被发现,那么在这个base上会有一个 -stable 修补补丁发布(如下)。一旦发布一个新的2.6.x base 内核,一个补丁会在2.6.x内核和新内核之间按照增量的方式编写。
要从版本2.6.11升级到2.6.12, 执行如下操作(注意这些补丁不适用于2.6.x.y上的内核,但是2.6.x上的内核,如果你需要从2.6.x.y升级到2.6.x+1,你需要先撤销2.6.x.y的补丁)。
下面是一些例子:
# 从2.6.11升级到2.6.12
$ cd ~/linux-2.6.11 #切换到源代码目录
$ patch -p1 < ../patch-2.6.12 # 应用 2.6.12 补丁
$ cd ..
$ mv linux-2.6.11 linux-2.6.12 # 重命名源代码目录
# 从2.6.11.1升级到2.6.12
$ cd ~/linux-2.6.11.1 # 切换到源代码目录
$ patch -p1 -R < ../patch-2.6.11.1 # 撤销2.6.11.1补丁
# 源码目录现在是 2.6.11
$ patch -p1 < ../patch-2.6.12 # 应用新2.6.12 补丁
$ cd ..
$ mv linux-2.6.11.1 linux-2.6.12 # 重命名源代码目录
#2.6.x.y内核 用4位数字命名的版本是 -stable 内核。它们包含少量的关键修复,包括对给定2.6.x中的安全问题或者重大regressions的修复。
这是为某些用户推荐的分支,这些用户想使用最新稳定内核,却不想帮助测试开发/试验版本。
如果没有可用的2.6.x.y内核,那么最高编号的2.6.x内核是当前稳定内核。
注:-stable 团队通常为最新的mainline发行版编写增量补丁,但是我下面只涉及(cover)非增量的补丁。增量补丁可以在这找到( ftp://ftp.kernel.org/pub/linux/kernel/v2.6/incr/)。
这些补丁不是递增的,意味着如2.6.12.3补丁不能在2.6.12.2内核源码上使用,而是要用在base 2.6.12内核源码上。
所以,为了在你当前的2.6.12.2内核源码上应用2.6.12.3补丁,你必须先撤回(back out)2.6.12.2补丁(这样你就剩下一个base 2.6.12内核源码),然后应用新的2.6.12.3补丁。
下面是一个实例:
$ cd ~/linux-2.6.12.2 # 切换到内核源码目录
$ patch -p1 -R < ../patch-2.6.12.2 # 撤销 2.6.12.2 补丁
$ patch -p1 < ../patch-2.6.12.3 # 使用新的 2.6.12.3 补丁
$ cd ..
$ mv linux-2.6.12.2 linux-2.6.12.3 # 重命名内核源码目录
#-rc 内核 这些都是发行候选内核。这些都是Linus发布的开发版内核,当他认为当前git(内核源码管理工具)树是在一个合理的正常状态足够测试时。
这些内核都是不稳定的,如果你打算运行它们,你应该做好偶尔breakage的准备。但是,这是主开发分支最稳定的,也是最终转变成下一代稳定内核,所以这些内核被尽可能多的人来测试是非常重要的。
这是一个好的分支,可以让一部分人来运行,这些人愿意帮助测试开发版内核,却不愿意运行真正的试验东西(这样的人应该看一下下面关于-git和-mm内核的章节)。
-rc 补丁不是增量的,它们应用于base 2.6.x内核,就像上面所说的2.6.x.y补丁一样。-rcN后缀前面内核版本表示-rc 内核将转变成的内核版本。
所以2.6.13-rc5表示这是2.6.13内核的第5个发行候选版,补丁应该应用在2.6.12内核源码上。
下面有3个实例,关于如何打这些补丁:
# 从2.6.12 升级到 2.6.13-rc3
$ cd ~/linux-2.6.12 # 切换到 2.6.12 源码目录
$ patch -p1 < ../patch-2.6.13-rc3 # 应用 2.6.13-rc3 补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.13-rc3 # 重命名内核源码目录
# 从 2.6.13-rc3 升级到 2.6.13-rc5
$ cd ~/linux-2.6.13-rc3 # 切换到 2.6.13-rc3 目录
$ patch -p1 -R < ../patch-2.6.13-rc3 # 撤销 2.6.13-rc3 补丁
$ patch -p1 < ../patch-2.6.13-rc5 # 应用 2.6.13-rc5 新补丁
$ cd ..
$ mv linux-2.6.13-rc3 linux-2.6.13-rc5 # 重命名内核源码目录
# 从 2.6.12.3 升级 2.6.13-rc5
$ cd ~/linux-2.6.12.3 # 切换到内核源码目录
$ patch -p1 -R < ../patch-2.6.12.3 # 撤销 2.6.12.3 补丁
$ patch -p1 < ../patch-2.6.13-rc5 # 应用 2.6.13-rc5 新补丁
$ cd ..
$ mv linux-2.6.12.3 linux-2.6.13-rc5 # 重命名内核源码目录
#-git内核
这些都是Linus的内核树的每日快照(daily snapshots)(由于在git仓库管理,因此得名)。
这些补丁通常每天发布一次,表示当前Linus内核树的当前状态。它们比-rc内核更加试验性,因为它们是自动生成的,如果它们是正常的,甚至都不会粗略的看一眼。
-git补丁不是增量的,可以用在 base 2.6.x内核上或者base 2.6.x-rc内核上——从它们的名字,你可以看出。命名为2.6.12-git1的补丁应用到2.6.12内核源码上,命名为2.6.13-rc3-git2应用到2.6.13-rc3内核上。
下面是一些实例,说明如何应用这些补丁:
# 从 2.6.12 升级到 2.6.12-git1
$ cd ~/linux-2.6.12 # 切换到内核源码目录
$ patch -p1 < ../patch-2.6.12-git1 # 应用 2.6.12-git1 补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.12-git1 # 重命名内核源码目录
# 从 2.6.12-git1 升级到 2.6.13-rc2-git3
$ cd ~/linux-2.6.12-git1 # 切换到内核源码目录
$ patch -p1 -R < ../patch-2.6.12-git1 # 撤销 2.6.12-git1 补丁
# 现在有了一个 2.6.12 内核
$ patch -p1 < ../patch-2.6.13-rc2 # 应用 2.6.13-rc2 补丁
# 内核现在是 2.6.13-rc2
$ patch -p1 < ../patch-2.6.13-rc2-git3 # 应用 2.6.13-rc2-git3 补丁
# 内核现在是 2.6.13-rc2-git3
$ cd ..
$ mv linux-2.6.12-git1 linux-2.6.13-rc2-git3 # 重命名内核源码目录
#-mm内核 这是由Andrew Morton发布的试验版内核。 -mm树作为一种新功能和其它试验补丁的试验场。 一旦一个补丁在-mm中证明它的价值,Andrew会将它push到Linus以归入主干。
尽管它鼓励补丁通过-mm树流向Linus,但这也不是强制的。
子系统的维护者(或个人)有时直接将他们的补丁push给Linus,即便它们已经被合并,并且在–mm中测试过(有时甚至没有经过-mm先前的测试)。
你应该努力让你的补丁进入主干(mainline)通过-mm来确保最大可能的测试。
这个分支在不断变化,包含许多试验性的功能,很多调试的补丁并不适合mainline等,它是这个文档中最具实验性的分支。
这些内核都不适合在应该稳定运行的系统中使用,因为它们比其它分支有更多的风险(确保你进行了最新的备份)。
这些内核除了包含实验性补丁之外,它们通常还包含在发行时mainline -git内核的任何变化。
测试-mm内核是很赞的,因为-mm内核树的要点是在合并到更稳定的主干Linus树之前淘汰regressions, crashes, data corruption bugs, build breakage(以及任何一般bug)。
-mm内核不会按一个固定的时间表来发布,通常会在每-rc内核间发布几个版本(一般是1~3个)。
-mm内核适用于base 2.6.x内核(当没有-rc内核发布时),或者Linus的 -rc内核。
下面是一些应用-mm内核的实例:
# 从 2.6.12 升级到 2.6.12-mm1
$ cd ~/linux-2.6.12 # 切换到 2.6.12 源码目录
$ patch -p1 < ../2.6.12-mm1 # 应用 2.6.12-mm1 补丁
$ cd ..
$ mv linux-2.6.12 linux-2.6.12-mm1 # 重命名源码目录
# 从 2.6.12-mm1 升级到 2.6.13-rc3-mm3
$ cd ~/linux-2.6.12-mm1 # 切换到 2.6.12-mml 源码目录
$ patch -p1 -R < ../2.6.12-mm1 # 撤销 2.6.12-mm1 补丁
# 现在有了 2.6.12 源码
$ patch -p1 < ../patch-2.6.13-rc3 # 应用 2.6.13-rc3 补丁
# 现在有了 2.6.13-rc3 源码
$ patch -p1 < ../2.6.13-rc3-mm3 # 应用 2.6.13-rc3-mm3 补丁
$ cd ..
$ mv linux-2.6.12-mm1 linux-2.6.13-rc3-mm3 # 重命名源码目录
以上是对不同内核树的解释列表,我希望你现在明白了如何应用不同的补丁,并能能够帮助测试内核。
#致谢 Randy Dunlap, Rolf Eike Beer, Linus Torvalds, Bodo Eggert,Johannes Stezenbach, Grant Coady, Pavel Machek, etc.
#原文链接 https://www.kernel.org/doc/Documentation/app1ying-patches.txt
留下评论