linuxpatchlinux内核文档commandheader类unix操作系统有一个很有趣的特性就是源代码级的补丁包。在windows上我们打补丁都是运行一个可执行的程序,然后就可以把补丁打完了,这对于最终用户是非常方便的,但是对我们这些求知欲比较强的Linux fans来说就有点不过瘾了,因为我们不知道里面是怎么做的。而Linux的补丁就有趣多了,我们首先获得程序的源代码和对应的补丁文件,然后给源代码打补丁,产生新的源代码文件。然后再编译这个新的源代码文件,就获得了打过补丁的新程序了。

如果你现在还不懂得如何打补丁,那没有关系,我们就一起来试试,因为在刚开始写这篇文章的时候,我也不会给自己的程序打补丁。

确切的说,这篇文章不是我写的,而是我根据Daniel P. Bovet, Marco Cesati, 和Cosimo Comella写的一篇英文文档翻译并修改得出来的。之所以这么说,我是不想落抄袭之嫌。 
言归正传:

解释patch文件 
我们可以使用diff命令加参数-ruN来比较两个文件并生成一个补丁文件。这个补丁文件会列出这两个不同版本文件的差异来。我们将通过一个特定例子来解释这个由diff命令生成的补丁文件(patch file)。

假定:我们对检查linux-2.2.13和linux-2.2.14这两个不同的版本的差别很感兴趣。 
第一步,我们使用如下命令:

make distclean

这样可以在两个源代码目录中删去所有非文本文件。 
然后我们继续第二步: 
*****************************************************************************

diff -ruN linux-2.2.13 linux-2.2.14 > /tmp/patch-2.2.14

***************************************************************************** 
COMMAND EXECUTION: 
-r 是一个递归选项,设置了这个选项,diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,正如你所料,这种比较也是包括子目录中的文件的。 
-N 选项表明如果一个文件存在于一个目录中,那么它就必须被认为是在这个目录中的,哪怕这个文件在对应的目录中是一个空文件。(举个例子,如果在老版本中有这么一个文件,但是在新版本中这个文件被去掉了,那么diff仍然会把它记录下来,我们打完补丁以后,在得到的新版本代码中,老版本的那个文件名仍然会存在,但是是一个空文件) 
-u 选项指明正在使用的是统一的输出格式。

下面我们查看一下经过重定向后生成的补丁文件/tmp/patch-2.2.14,下面是我们从该文件中摘抄的一部分补丁信息: 
*****************************************************************************

diff -ruN linux-2.2.13/arch/i386/kernel/signal.c 
linux-2.2.14/arch/i386/kernel/signal.c

这里第一个版本的名字,linux-2.2.13是参考版本(就是旧版本),所有的在linux-2.2.14(新版本)中发现的问题都是和第一个版本相关的。 
***************************************************************************** 
DIFF HEADER: 
diff命令会在补丁文件中记录这两个文件的首次创建时间,如下: 
***************************************************************************** 
*** linux-2.2.13/arch/i386/kernel/signal.c Tue Jun 8 01:14:20 1999 
--- linux-2.2.14/arch/i386/kernel/signal.c Sun Jan 23 17:29:25 2000 
***************************************************************************** 
DIFF BODY: 
diff 命令在这两个文件之间发现了3类差异。

a) 添加(addition): 
这一行在旧版本的文件中没有,但是被添加到了新版本的文件中。 
b) 置换(replacement): 
在新版本文件中用连续的几行替换掉了旧版本文件中连续的几行。 
c) 删除(deletion): 
在旧版本文件中的一行在新版本文件中不再出现。

在每一种情况下,发生变化的行号都会被提示出来。 
让我们解释一下diff用来指明这三种情况时使用的符号: 
***************************************************************************** 
添加(ADDITION): 
看补丁文件中如下的行: (相对于arch/i386/kernel/signal.c文件的新旧两个版本)

*** 419,431 **** 
--- 419,437 ---- 
? current->exec_domain->signal_invmap[sig] 
: sig), 
&frame->sig); 
+ if (err) 
+ goto give_sigsegv;

使用+号指明在“&frame->sig);”这一行后面要加两个新行。这两个新行就是用+符号开头的两行。 
*** 419,431 ****向读者指明可以从旧文件的419行到431行来查阅这些变化;同样的,--- 419,437 ----向读者指明可以从新文件的419到437行来查阅这些变化。这样一来,新旧一比较,就可以知道哪些地方发生了什么变化。 
不过在新版本的diff中,似乎并不是用这种方法来表明新旧文件对应的行号的,而使用@这个符号,对应于上例中的: 
*** 419,431 **** 
--- 419,437 ---- 
我们看到的新的标识可能是: 
@@ -419,431 +419,437 @@ 
对于这种表示方法,我还不是很懂,如果有哪位朋友比较懂的话,非常欢迎你将这部分内容加进来。 
不过,有一点需要说明一下,就是这个行号并不是完全必需的,其实这个行号在给源代码打补丁的时候是没有用的,这里提示出来主要是给开发人员比较分析时使用的。

置换(REPLACEMENT): 
看补丁文件中如下的行: (相对于arch/i386/kernel/signal.c文件的新旧两个版本)

*************** 
*** 367,377 **** 
printk("I/O APIC #%d Version %d at 0x%lX./n", 
m->mpc_apicid,m->mpc_apicver, 
m->mpc_apicaddr); 
! /* 
! * we use the first one only currently 
! */ 
! if (ioapics == 1) 
! mp_ioapic_addr = m->mpc_apicaddr; 

mpt+=sizeof(*m); 
count+=sizeof(*m);

然后后面又紧跟着如下的行:

--- 368,376 ---- 
printk("I/O APIC #%d Version %d at 0x%lX./n", 
m->mpc_apicid,m->mpc_apicver, 
m->mpc_apicaddr); 
! mp_apics [mp_apic_entries] = *m; 
! if (++mp_apic_entries > MAX_IO_APICS) 
! --mp_apic_entries; 

mpt+=sizeof(*m); 
count+=sizeof(*m);

这里就指明了在旧版本文件中用!符号标识的5行被在新版本文件中用!标识的3行替换了。 
由此可见,符号!就意味着替换。但是为什么会有替换,而不是先删除再添加,这里我就不清楚了。还是得请知道的朋友指点一下了。

删除(DELETION): 
看补丁文件中如下的行: (相对于drivers/net/Config.in文件的新旧两个版本) 
*************** 
*** 93,100 **** 
fi 
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then 
tristate 'RealTek 8129/8139 (not 8019/8029!) support' CONFIG_RTL8139 
- tristate 'SiS 900 PCI Fast Ethernet Adapter support' CONFIG_SIS900 
- tristate 'Packet Engines Yellowfin Gigabit-NIC support' CONFIG_YELLOWFIN 
fi 
bool 'Other ISA cards' CONFIG_NET_ISA 
if [ "$CONFIG_NET_ISA" = "y" ]; then 
--- 94,99 ---- 
*************** 
在旧版本文件中用-符号标识的两行说明这两行在新版本的文件中不会再出现了,也就是说,在新版本的文件中,这两行被删除了。

建立一个自己的补丁

你现在修改并测试了一个新的Linux版本,就称它为Linux-2.4.5kh3,这个版本和你当前使用的称作Linux-2.4.5kh2的“老”版本有一些些不同。

现在你想制作一个可以将Linux-2.4.5kh2升级到Linux-2.4.5kh3的补丁程序。顺便多说句废话,理所当然的这个补丁程序要比Linux内核的源代码小的多。 
这个补丁文件一般使用一张软盘就可以装下来,因此这对于升级另一台计算机上的旧的操作系统内核是非常有用的。

本质上制作补丁程序只有两个步骤,如下描述: 
a) 在计算机A上产生一个补丁文件(计算机A就是那台既有新内核的源代码又有老内核的源代码的计算机)。并且将这个补丁文件复制到一张软盘上。 
b) 在计算机B上读取保存有补丁文件的软盘,并利用补丁文件将计算机B上的旧内核升级为新的内核。

下面我们进行详细的说明。其中第1到第4步描述了怎样制作一个补丁文件,并把它复制到软盘上。第5到第6步描述了怎么样使用补丁文件将旧的操作系统内核升级到新的版本。 
***************************************************************************** 
第1步:清理两个内核的源代码文件(没有*.o的文件或者.*文件) 
***************************************************************************** 
我们假定这两个内核的源代码路径分别是: 
/usr/src/linux-2.4.5kh2和/usr/src/linux-2.4.5kh3

运行如下命令: 
cd /usr/src/linux-2.4.5kh2 
make distclean 
cd /usr/src/linux-2.4.5kh3 
make distclean 
***************************************************************************** 
第2步:在两个内核源代码版本之间产生一个“context diffs”文件(这个文件指明了两个不同版本源代码之间的所有不同)。 
***************************************************************************** 
运行如下命令(首先是旧的内核,然后是新的内核): 
cd /usr/src 
diff -ruN linux-2.4.5kh2 linux-2.4.5kh3 > patch-2.4.5kh3 
***************************************************************************** 
第3步:检查补丁文件。 
***************************************************************************** 
运行如下命令查看补丁文件以确定它没有包含任何的垃圾: 
less patch-2.4.5kh3 
这里所说的垃圾就是非ASCI码的乱码,或控制字符。如果发现补丁文件中存在着不是文本的内容,那就是有垃圾了。这是我们需要重新操作第1至第3步 
***************************************************************************** 
第4步:将补丁文件复制到一张软盘上。 
***************************************************************************** 
mount /flp 
cp /usr/src/patch-2.4.5kh3 /flp 
umount /flp

由于我们的补丁文件一般都很小,所以我们不需要压缩它。现在我们拿着这张带有补丁程序的软盘转移到计算机B前面去。 
***************************************************************************** 
第5步:从软盘中读取补丁文件。 
***************************************************************************** 
cd /usr/src 
mount /flp 
cp /flp/patch-2.4.5kh3 patch-2.4.5kh3 
umount /flp 
***************************************************************************** 
第6步:使用这个补丁文件将旧内核的源代码升级到新内核的版本。 
***************************************************************************** 
a) 执行奇妙的patch命令: 
patch -p0 < patch-2.4.5kh3 
patch命令作用在输入的补丁文件patch-2.4.5kh3上,并将对应的老版本的内核源代码中所有的文件和子目录升级到对应的新版本(当然这里的老版本一定要和我们在计算机A上运行diff命令时的那个老版本是一样的)。在我们的例子中,旧的内核源代码版本是Linux-2.4.5kh2。这里参数–p0用来保证文件名不被改变(既不被修改,也不被删除)。 
b) 重新命名内核源代码: 
mv linux-2.4.5kh2 linux-2.4.5kh3

***************************************************************************** 
撤消一个补丁 
***************************************************************************** 
如果你对新打的补丁不很满意,而你想回复到以前较早的内核版本去,那么我们从上面描述的第6步开始逆顺序操作即可(为什么要采取撤消补丁的方式来回到先前版本的理由很多,我们就不说了): 
a) 将内核源代码的版本恢复到先前的名字:

mv linux-2.4.5kh3 linux-2.4.5kh2

b) 执行那条奇妙的patch命令:

patch -RE -p0 < patch-2.4.5kh3 
这里,-E选项说明如果发现了空文件,那么就删除它;-R选项说明在补丁文件中的“新”文件和“旧”文件现在要调换过来了(实际上就是给新版本打补丁,让它变成老版本,但是这里是否一定要先更改源代码的目录名我还不明确,需要作实验)。

这里有一个疑问,就是为什么要进行代码目录的改名?

***************************************************************************** 
避免make distclean操作 
***************************************************************************** 
根据文档/usr/src/linux/Documentation/SubmittingPatches所描述的,运行一个没有make distclean的递归diff,使用如下命令: 
diff -ruN -X dontdiff linux-2.4.5kh2 linux-2.4.5kh3 > patch.diff 
dontdiff是一个可执行文件,它可以从下面这个网址进行下载: 
http://www.moses.uklinux.net/patches/dontdiff 
但是,就我而言,我还是不知道为什么要这么做,或者说不进行make distclean的目的是什么。如果有那位朋友知道的话,非常感谢你告诉我一声,或者是把这部分内容补充到这份文档中。

制作一个官方的补丁文件 
你已经安装了一个新的Linux,我们称它为Linux-2.4.2。但是差不多每个月,都会有新的Linux补丁推出,这些补丁或者修补了一些问题,或者支持了一些新的硬件或其它新的特性。所以,当你安装了Linux-2.4.2几个月以后,就会有新的稳定发行版推出,我们称它为Linux-2.4.5。

我们将要描述的过程解释了你如何最简便的将自己的Linux从2.4.2升级为最新的2.4.5。

关键的窍门在于使用一系列的补丁文件。 
***************************************************************************** 
第0步:理解官方的Linux补丁文件是如何制作的。 
*****************************************************************************

每一个官方的Linux补丁文件都是一个使用diff工具在两个连续的Linux版本中所有文件间产生的diff文件。

作为一个例子,我们假定这个补丁文件是用来将Linux-2.4.2升级到Linux-2.4.5的,这个补丁文件通过如下方法获得: 
cd /usr/src/linux-2.4.2 
make distclean 
cd /usr/src/linux-2.4.3 
make distclean 
ln -s linux-2.4.2 linux 
diff -ruN linux linux-2.4.3 > patch_2.4.3 
gzip patch_2.4.3

这些补丁文件按照顺序使用gzip压缩,以节省Linux发行版本站点的下载时间。例如我们生成的补丁文件——patch_2.4.3.gz,就是一个经过压缩的补丁文件。

Linux官方补丁总是认为缺省的源代码子树名字为“Linux”。这种方法可能有点混淆,但是它允许一些(版本)连续的补丁不用修改源代码子树的名字就可以连续的被修改到源代码子树上。(下面我们可以看到)

这些补丁文件的名字都是标准的: 
patch_2.4.3 记录着linux-2.4.3 和 linux-2.4.2之间的版本差异。 
patch_2.4.4 记录着linux-2.4.4 and linux-2.4.3之间的版本差异。 
patch_2.4.5 记录着linux-2.4.5 and linux-2.4.4之间的版本差异。

在我们的例子中,我们正好需要这3个补丁文件,他们的名字是patch_2.4.3.gz、patch_2.4.4.gz和patch_2.4.5.gz,用来将Linux-2.4.2升级到Linux-2.4.5。

***************************************************************************** 
第1步:升级Linux源代码目录的名字和Linux符号连接。 
*****************************************************************************

假定内核源代码的路径是:/usr/src/linux-2.4.2/ 
并且所有需要的压缩补丁文件都已经存放在目录/usr/src 中了。 
执行下列命令: 
cd /usr/src 
mv linux-2.4.2 linux-2.4.5 
rm linux 
ln -s linux-2.4.5 linux

***************************************************************************** 
第2步:清理源代码目录(删除中间目标文件和配置文件)。 
***************************************************************************** 
执行下列命令: 
cd linux 
make distclean 
***************************************************************************** 
第3步:解压缩补丁文件。 
***************************************************************************** 
in our example, execute:

cd /usr/src 
gunzip patch-2.4.3.gz 
gunzip patch-2.4.4.gz 
gunzip patch-2.4.5.gz 
***************************************************************************** 
第4步:重复执行patch命令。 
***************************************************************************** 
执行下列命令: 
for i in 3 4 5; do 
patch -p0 < patch_2-4.$i 
done 
其中 
patch -p0 < patchfile

命令作用在输入的补丁文件上,并将对应的老版本的内核源代码中所有的文件和子目录升级到对应的新版本(在我们的例子中,老版本的内核源代码目录是/usr/src/linux)。在我们的例子中,旧的内核源代码版本是Linux-2.4.5kh2。这里参数–p0用来保证文件名不被改变(既不被修改,也不被删除)。 
目标目录是Linux,就是说旧版本的内核源代码经过patch以后,就被升级成了新版本的内核源代码。 

Linux打补丁的一些问题的更多相关文章

  1. Linux内核补丁批量自动下载工具

    Linux kernel官网cgit工具不支持按变更代码进行补丁搜索,想到个办法就是把补丁都抓下来,这样可以在本地搜索.花了2个小时写了个小工具,话不多说,直接看效果: E:\docs\TOOLS\p ...

  2. kkjcre1p: unable to spawn jobq slave process, slot 0, error 1089(Linux x86_64)补丁

    在shutdown immediately的时候,alert Log出现如下错误信息,并且不能正常关闭 kkjcre1p: unable to spawn jobq slave process, sl ...

  3. linux下补丁制作及打补丁实例【转】

    转自:http://www.latelee.org/using-gnu-linux/diff-and-patch-on-linux.html 搞ARM有一段时日了,期间看了不少开发板的手册,手册的内容 ...

  4. linux下patch命令使用详解---linux打补丁命令

    http://blog.csdn.net/pashanhu6402/article/details/51849354 语 法:patch [-bceEflnNRstTuvZ][-B <备份字首字 ...

  5. Linux打补丁的一个简单例子

        前言 在做开发的过程中难免需要给内核及下载的一些源码打补丁或者说是升级,所以我们学习在Linux下使用diff制作补丁以及如何使用patch打补丁显得尤为重要. diff与patch命令介绍 ...

  6. 提交 linux kernel 补丁流程备忘录

    1. 订阅 linux 邮件列表 linux 邮件列表 Kernel Mailing Lists 是所有 linux kernel 开源贡献者协同工作的平台,可以通过向 VGER.KERNEL.ORG ...

  7. linux 打补丁 2原理

    1.创建目录 demo cd demo 有bug文件a.txt: 当前目录为demo mkdir -p old/a/p vi old/a/p/foo.txt old_line_1 old_line_2 ...

  8. Linux 制作补丁 打补丁 撤销补丁

    1.制作补丁 diff - 逐行比较文件 格式 diff   参数   旧文件/旧文件夹   新文件/新文件夹 -N   将不存在的文件看作是空的 -a   将所有文件都视为文本文件 -u   以合并 ...

  9. linux下补丁制作和使用方法

    两个文件的情况: 制作补丁: $ diff test1.c test2.c > test.patch 给test1.c打补丁: $ patch test1.c < test.patch 还 ...

随机推荐

  1. 【刷题】BZOJ 2754 [SCOI2012]喵星球上的点名

    Description a180285幸运地被选做了地球到喵星球的留学生.他发现喵星人在上课前的点名现象非常有趣. 假设课堂上有N个喵星人,每个喵星人的名字由姓和名构成.喵星球上的老师会选择M个串来点 ...

  2. [NOI2016]优秀的拆分 后缀数组

    题面:洛谷 题解: 因为对于原串的每个长度不一定等于len的拆分而言,如果合法,它将只会被对应的子串统计贡献. 所以子串这个限制相当于是没有的. 所以我们只需要对于每个位置i求出f[i]表示以i为开头 ...

  3. Unity3D for VR 学习(2): 暴风魔镜框架探索

    学习一个新技术,有三个法宝: 法宝1: 掌握厂家提供的用户API手册 法宝2: 掌握厂家提供的demo样例 法宝3:<每个研发人员都应树立的一个demo模式> 故,学习魔镜4技术,亦如是也 ...

  4. 【bzoj4869】相逢是问候

    Portal-->bzoj4869 Solution 这道题的话..长得就是线段树的样子qwq 如果做过的话..可能会联想到bzoj3211(没写博qwq晚点再说吧哈哈..) 首先大胆猜一波结论 ...

  5. Python多线程、进程、协程

    本节内容 操作系统发展史介绍 进程.与线程区别 python GIL全局解释器锁 线程 语法 join 线程锁之Lock\Rlock\信号量 将线程变为守护进程 Event事件 queue队列 生产者 ...

  6. MDK5.13新建工程步骤

    http://www.stmcu.org/module/forum/thread-600249-1-1.html 本人也是接触stm32没多久,之前用的MDK是5.1,现在用的是5.13,MDK5.0 ...

  7. The 14th Zhejiang Provincial Collegiate Programming Contest Sponsored by TuSimple - F 贪心+二分

    Heap Partition Time Limit: 2 Seconds      Memory Limit: 65536 KB      Special Judge A sequence S = { ...

  8. 改变 jq中 data-id 的值

    if (_this.hasClass('default_btn_is')){ _this.removeClass('default_btn_is'); _this.addClass('default_ ...

  9. SSH免密码登录,实现数据传输备份

    简单来说,就是通过ssh-keygen -t rsa命令来产生一组公私钥,私钥是id_rsa,公钥是id_rsa.pub,把公钥上传到另一台服务器对应账号的.ssh/authorized_keys,即 ...

  10. libcurl在mingw下编译

    通过命令提示符进入 curl-7.27.0 文件夹输入 mingw32-make mingw32 进行生成(这里我只需要普通的功能,于是没有加附加的选项)编译完成后,在 lib 文件夹中会有我们需要的 ...