3分钟读完
LinuxKernel电邮列表上问的最多的一个问题就是:怎样给Linux内核打补丁,更具体点就是,众多trees/branches的一个补丁应当应用于那个basekernel。希望这个文档能帮你解答这种疑惑。
本文不仅介绍怎么打补丁以及撤消补丁,还介绍了不同的内核树。
#哪些是补丁?
补丁就是一个文本文件,该文件包含了一个源码树的两个不同版本之间增量变化。补丁文件是由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系统)。
在下边所有的反例中,我使用下边的句型嵌入式linux驱动程序设计从入门到精通,通过标准输入stdin提供(feed)补丁文件(未压缩方式):
patch -p1 < path/to/patch-x.y.z
假如你的补丁文件用gzip或bzip2压缩,而你不想解压后打补丁linux内核邮件列表,这么你可以这样来应用补丁:
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‘quiteright’)的问题,它有两个选择,一是拒绝修复更新变化,退出;二是尝试找到一个方式对补丁做一些小的改动。
一个反例(补丁不太正确,patch命令企图修补):上下文匹配,须要更改的行匹配,而且行号不匹配。这是可能发生的,比如,patch命令对文件做一些变动,并且因为个别缘由,在文件开始处有几行须要被添加或则删掉。在这些情况下,一切看上去都没问题,它只是向下或向上联通一下,patch命令一般会调整行号,之后修复补丁。
每每patch命令使用它不得不进行稍为更改的补丁文件时,它将提示”补丁通过’fuzz’被使用“来告诉你。你应当提防这些变化,由于虽然patch命令可能让补丁文件正确,它不可能总使补丁文件正确,结果有时可能会出错。
当patch命令遇见它不能通过fuzz修复的变化时linux内核邮件列表,它完全拒绝这个文件,而且生成一个扩充名为.rej的文件(一个rejectfile)。你还能从这个rej文件找出那个change不能被修复,所以你还能自动修补它。
假如你没有第三方补丁来修复你的内核源码,只有从获得的补丁,以正确的次序打补丁,不对源文件做出更改(译者注:应当是指补丁源文件,这儿要说明的意思是,你使用完整的上的补丁文件,使用patch命令执行基本不会出问题),这么你应当看不到来自patch命令的fuzz或则reject消息。而且假如你看见了fuzz或则reject消息,这么这可能是一个高风险状况:你的本地sourcetree或则补丁文件受损了。在这些情况下,你应当尝试重新下载补丁,假如问题还没有解决,建议你从下载一个全新的sourcetree(译者注:应当是完整的内核源码,由于这个问题可能说明你本地内核代码被恶意篡改了).
让我们看一下patch命令可能形成的其它信息。
假如patch命令停止,出现”Filetopatch:”提示,说明patch命令没有找到补丁文件。你很可能忘掉了指定-p1选项,或则你的当前工作目录不对。其它情况,补丁文件须要-p0选项而不是-p1选项(假如是这些情况,阅读补丁文件就可以发觉,倘若是的话,这是由写补丁的人犯的错误,这不算严重)。
假如显示”Hunk#2succeededat1887withfuzz2(offset7lines).”或者类似这个信息的,这说明patch命令不得不调整change的位置(前面这个事例中,它须要联通7行)。因为补丁文件和期望的文件不同,结果文件可能是也可能不是正确的。假如企图将一个内核版本的补丁应用到另一个不同版本的内核上,时常会出现这些提示消息。
假如显示”Hunk#3FAILEDat2387.”,说明这个补丁文件不能否正确修复,patch命令用fuzz方式也不能正确执行。这将会形成一个.rej文件,该文件包含造成patch命令失败的change,就会形成一个.orig文件,该文件包含不能改变的原始内容。
假如显示”Reversed(orpreviouslyapplied)patchdetected!Assume-R?[n]”,说明patch命令测量到补丁文件中的change早已修复了内核。假如你之前确实打过该补丁,你只是误操作,这么输入[n]o来终止patch命令。假如你之前打过该补丁,如今想撤消它,却忘掉了指定-R选项,这么输出[y]es来撤消补丁。假如补丁的作者在创建补丁时reversedthesource和目标目录,这也会发生,在这些情况下,恢复/撤消补丁将在实际中应用它。
假如显示类似”patch:**unexpectedendoffileinpatch”或者“patchunexpectedlyendsinmiddleofline”的消息,表示patch命令不能正确解析你输入的补丁文件。其实是你下载的文件毁坏了,你企图输入一个压缩的补丁文件,其实是你使用的补丁文件因为电邮顾客端/短信传输代理在传输过程中毁坏了,比如,通过分隔长行为两行。常常这种警告可以很容易的修补,通过联接被分隔的两行。
正如我里面早已提及的,假如将从获取的补丁应用到正确的未经更改的源码树上,将不会出现这种错误。所以,假若你使用上的补丁,却还是遇见这种错误,这么应当是你的补丁文件或则你的内核源码树毁坏了,建议你重新下载一个完整的内核树和你想使用的补丁文件。
#有patch命令的取代选择吗?
Yes,其实有其它选择。
你可以使用interdiff命令()来世成一个表示两个补丁差异的补丁,之后使用该补丁。
这容许你一步从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脚本,可以手动下载补丁和打补丁().
其它不错的工具有diffstat,它显示一个补丁文件的change概要;lsdiff,它展示在补丁文件中受影响的文件列表,但是标示(可选的)每位补丁开始的行号;grepdiff,它展示一个补丁更改文件列表,这个补丁包含一个给定的正则表达式。
#在那里可以下载补丁?
在网站可以下载,最新的补丁程序被链接到首页,它们也有自己特定主页。
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/
在服务器上,你可以使用,cc是一个国家代号(countryocde)。这样你就可以从一个地理位置离你最可能近的镜像网站下载,获得更快的下载速率,减轻全球范围的带宽,增加主服务器的负载——这都是好的事情,所以尽可能的使用镜像网站。
#2.6.x内核
那些都是由Linus发布的basestable发行版。编号最高的发行版是最新的。
假如regressions或则其它严重缺陷被发觉,这么在这个base上会有一个-stable修复补丁发布(如下)。一旦发布一个新的2.6.xbase内核,一个补丁会在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:///pub/linux/kernel/v2.6/incr/)。
这种补丁不是递增的,意味着如2.6.12.3补丁不能在2.6.12.2内核源码上使用,而是要用在base2.6.12内核源码上。
所以,为了在你当前的2.6.12.2内核源码上应用2.6.12.3补丁,你必须先撤回(backout)2.6.12.2补丁(这样你就剩下一个base2.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补丁不是增量的,它们应用于base2.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的内核树的每日快照(dailysnapshots)(因为在git库房管理,因而得名)。
这种补丁一般每晚发布一次,表示当前Linus内核树的当前状态。它们比-rc内核愈发试验性,由于它们是手动生成的,假如它们是正常的,甚至都不会简略的看一眼。
-git补丁不是增量的,可以用在base2.6.x内核上或则base2.6.x-rc内核上——从它们的名子,你可以看出。命名为2.6.12-git1的补丁应用到2.6.12内核源码上,命名为2.6.13-rc3-git2应用到2.6.13-rc3内核上。
下边是一些实例常用linux系统,说明怎样应用这种补丁:
# 从 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内核这是由AndrewMorton发布的试验版内核。-mm树作为一种新功能和其它试验补丁的试验场。一旦一个补丁在-mm中证明它的价值,Andrew会将它push到Linus以划入主干。
虽然它鼓励补丁通过-mm树流向Linus,但这也不是强制的。
子系统的维护者(或个人)有时直接将她们的补丁push给Linus,尽管它们已然被合并,但是在–mm中测试过(有时甚至没有经过-mm以前的测试)。
你应当努力让你的补丁步入主干(mainline)通过-mm来确保最大可能的测试。
这个分支在不断变化,包含许多试验性的功能,好多调试的补丁并不适宜mainline等,它是这个文档中最具实验性的分支。
这种内核都不适宜在应当稳定运行的系统中使用,由于它们比其它分支有更多的风险(确保你进行了最新的备份)。
这种内核不仅包含实验性补丁之外,它们一般还包含在发行时mainline-git内核的任何变化。
测试-mm内核是很赞的,由于-mm内核树的要点是在合并到更稳定的主干Linus树之前淘汰regressions,crashes,datacorruptionbugs,buildbreakage(以及任何通常bug)。
-mm内核不会按一个固定的时间表来发布,一般会在每-rc内核间发布几个版本(通常是1~3个)。
-mm内核适用于base2.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 # 重命名源码目录
以上是对不同内核树的解释列表,我希望你如今明白了怎样应用不同的补丁,并能才能帮助测试内核。
#致谢RandyDunlap,RolfEikeBeer,LinusTorvalds,BodoEggert,JohannesStezenbach,GrantCoady,PavelMachek,etc.
#原文链接