1. patch文件结构
  2. 生成patch文件 --diff命令
  3. patch 使用 -- patch命令

    3.1 打path

    3.1撤销patch
  4. 使用举例

    4.1 基本命令使用

    4.2 内核打补丁

1. patch文件的结构

补丁头

补丁头是分别由---/+++开头的两行,用来表示要打补丁的文件。

一个补丁文件中的多个补丁

一个补丁文件中可能包含以---/+++开头的很多节,每一节用来打一个补丁。所以在一个补丁文件中可以包含好多个补丁。

块是补丁中要修改的地方。它通常由一部分不用修改的东西开始和结束。他们只是用来表示要修改的位置。

他们通常以@@开始,结束于另一个块的开始或者一个新 的补丁头。

块的缩进

块会缩进一列,而这一列是用来表示这一行是要增加还是要删除的。

块的第一列

+号表示这一行是要加上的。

-号表示这一行是要删除的。

没有加号也没有减号表示这里只是引用的而不需要修改。

diff --git a/arch/x86/Makefile b/arch/x86/Makefile
index f5d7f4134524..26a2198c59b3 100644
--- a/arch/x86/Makefile
+++ b/arch/x86/Makefile
@@ -220,9 +220,6 @@ KBUILD_CFLAGS += -fno-asynchronous-unwind-tables # Avoid indirect branches in kernel to deal with Spectre
ifdef CONFIG_RETPOLINE
-ifeq ($(RETPOLINE_CFLAGS),)
- $(error You are building kernel with non-retpoline compiler, please update your compiler.)
-endif
KBUILD_CFLAGS += $(RETPOLINE_CFLAGS)
endif

2. 生成patch文件 -- diff命令

diff命令比较两个文件的不同,然后记录下来,也就是所谓的diff补丁。

语法格式:

diff 【选项】 源文件(夹) 目的文件(夹)
diff -Naur 旧的目录 新的目录 > patch文件
diff -Naur 旧的文件 新的文件 > patch文件
给源文件(夹)打个补丁,使之变成目的文件(夹),术语也就是“升级”。

下面介绍三个最为常用选项:

-r 是一个递归选项,设置了这个选项,diff会将两个不同版本源代码目录中的所有对应文件全部都进行一次比较,包括子目录文件。

-N 选项确保补丁文件将正确地处理已经创建或删除文件的情况。

-u 选项以统一格式创建补丁文件,这种格式比缺省格式更紧凑些。

diff 对目录有一些限制,需要保持目录的一致性。

在创建patch的时候文件夹的层数应当是一样的,比如

代码:

--- old/modules/pcitableMon Sep 27 11:03:56 1999

+++ new/modules/pcitableTue Dec 19 20:05:41 2000

这样是可以的。

而代码:

--- old/try1/other/modules/pcitableMon Sep 27 11:03:56 1999

+++ new/modules/pcitableTue Dec 19 20:05:41 2000

这样做可能会有一些问题。

3.patch使用 -- patch命令

patch命令,利用diff制作的补丁来实现源文件(夹)和目的文件(夹)的转换。这样说就意味着你可以有源文件(夹)――>目的文件(夹),

也可以目的文件(夹)――>源文件(夹)。

对于一个patch文件,有两种常用使用方法:

cat new-patch | patch -p0
patch -p0 < new-patch
patch -R -p0 < new-patch
-p0 选项要从当前目录查找目的文件(夹)
-p1 选项要忽略掉第一层目录,从当前目录开始查找。
-E 选项说明如果发现了空文件,那么就删除它
-R 选项说明在补丁文件中的“新”文件和“旧”文件现在要调换过来了(实际上就是给新版本打补丁,让它变成老版本)

patch命令里面的层数(-p0?-p1?)

参数-p来指定从第几层开始比较。比如有一个patch文件的补丁头是这样的:

代码:

--- old/modules/pcitableMon Sep 27 11:03:56 1999

+++ new/modules/pcitableTue Dec 19 20:05:41 2000

如果使用参数-p0,就表示从当前目录,找一个叫作new的目录,在它下面找一个叫modules的目录,再在它下面找一个叫 pcitableMon的目录。

如果使用参数-p1,就表示忽略第一层,从当前目录找一个叫modules的目录,在它下面找一个叫modules的目录。这样会忽略掉补丁头提到的 new目录。

依此类推。

4. 使用例子

4.1 diff命令生成patch
#diff -aurN a/c a/b
diff -aurN a/c/c/a.c a/b/c/a.c
##diff命令会在补丁文件中记录这两个文件的首次创建时间
--- a/c/c/a.c 2019-02-24 10:59:30.351334765 +0800
+++ a/b/c/a.c 2019-02-24 10:52:19.139836763 +0800
@@ -10,5 +10,6 @@
#include<string.h>
#include "head.h"
int main(){
+ printf("hello world\r\n");
return 0;
}
diff -aurN a/c/c/head.h a/b/c/head.h
--- a/c/c/head.h 2019-02-24 10:59:30.355336764 +0800
+++ a/b/c/head.h 2019-02-24 10:52:32.942734763 +0800
@@ -7,5 +7,6 @@
************************************************************************/
#ifndef HEADER_H
#define HEADER_H
+extern void test();
#endif
4.2 patch使用

打patch

lin@lin:~/mytest$ patch -p0 < 123.patch
patching file a/c/c/a.c
patching file a/c/c/head.h

查看文件

lin@lin:~/mytest$ cat a/c/c/a.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "head.h"
int main(){
printf("hello world\r\n");
return 0;
}
lin@lin:~/mytest$ cat a/c/c/head.h
#ifndef HEADER_H
#define HEADER_H
extern void test();
#endif

撤销patch

lin@lin:~/mytest$ patch -R -p0 < 123.patch
patching file a/c/c/a.c
patching file a/c/c/head.h

查看文件

lin@lin:~/mytest$ cat a/c/c/head.h
#ifndef HEADER_H
#define HEADER_H
#endif lin@lin:~/mytest$ cat a/c/c/a.c
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "head.h"
int main(){
return 0;
}
4.3  内核打patch

内核不同版本介绍:

(一)Linux内核版本类型及patch简介:

1)基础版本

2.6.x为基础版本,patch位置:

http://www.kernel.org/pub/linux/kernel/v2.6/

2)修正版本

2.6.x.y为2.6.x基础版本之上派生出来的修正版本,称为-stable内核版本,patch位置:

http://www.kernel.org/pub/linux/kernel/v2.6/

3)发布版本

2.6.x.rcN(Release Candidate)内核,是在2.6.(x-1)之上派生出来的之后版本的侯选版本;此种内核

不稳定,发布的时候表示可以用来测试了;

例如对2.6.21加了新功能后派生出2.6.22-rc1,patch位置:

http://www.kernel.org/pub/linux/kernel/v2.6/testing/

4)git版本

git内核是每日内核树的快照,此类型比rc内核更不稳定,从基础版本或rc版本派生出来;

例如2.6.26-git1,2.6.26-rc9-git2,patch位置:

http://www.kernel.org/pub/linux/kernel/v2.6/snapshots/

5)mm版本

mm内核是专门针对内核的mm模块维护的版本,类似于git版本从基础版本或rc版本派生出来;

例如2.6.21-mm2, 2.6.21-rc2-mm1,patch位置:

http://www.kernel.org/pub/linux/kernel/people/akpm/patches/2.6/

不同类型内核的patch名称在前面加上“patch-”(mm内核除外,直接用的版本号),例如:patch-2.6.21, patch-2.6.21.7, patch-2.6.26-git1, patch-2.6.26-rc9-git2, 2.6.21-mm2, 2.6.21-rc2-mm1

(二) 内核补丁类型

  1. 增量补丁

    同一类型的版本的补丁是用本版本对相邻的前一个版本制作的。

    例如,patch-2.6.21是对2.6.20做的补丁,patch-2.6.22是对2.6.21做的补丁,这就是增量补丁。

    2.6.x基础版本内核,采用的是增量补丁。

  2. 非增量补丁

    也增量补丁相反,是基本某一固定版本制作的补丁,而非相邻的前一个版本。

    例如,patch-2.6.21.3是2.6.21.3版本相对于2.6.21基础版本做的补丁,patch-2.6.21.4是2.6.21.4版本相对于2.6.21基础版本做的补丁,

    也就是说2.6.21.4与2.6.21.3之前没有补丁可用。

    rc内核,git内核和mm内核都是非增量补丁,都是相对于当前基础版本或rc版本制作出来的补丁。

另外,对于rc内核,git内核,相应的patch目录下面有inc目录中,包含有增量补丁。

(三)内核版本升级

  1. 同版本升级内核版本

    现有基础内核版本2.6.21,想转成2.6.21.7内核stable版本,应该怎么办?

    (a) 去内核源码目录下载patch-2.6.21.7;

    (b) Linux shell下面,cd到2.6.21内核源文件根目录(linux-2.6.21),

    将patch-2.6.21.7也放在本目录(命令执行的当前止录),执行patch命令:
patch -p1 < patch-2.6.21.7
(p1的意思是忽略patch文件(即diff文件)内容中的第一个路径)

c) 打完补丁后,即变成了2.6.21.7的内核了,如果想回退至2.6.21基础版本,执行如下命令即可:

patch -R -p1 < patch-2.6.21.7
(-R的参数意思表示回退这个patch)

2.不同版本内核升级

现有基础内核版本2.6.21,想转成2.6.23.1内核stable版本,应该怎么办?

a) 同1中下载patch-2.6.22, patch-2.6.23, patch-2.6.23.1文件,并设置执行路径和环境;

b) 多次打补丁

patch -p1 < patch-2.6.22 变成了2.6.22
patch -p1 < patch-2.6.23 变成了2.6.23
patch -p1 < patch-2.6.23.1 变成了2.6.23.1

c) 回退操作

patch -R -p1 < patch-2.6.23.1 变成了2.6.23
patch -R -p1 < patch-2.6.23 变成了2.6.22
patch -R -p1 < patch-2.6.22 变成了2.6.21

3.不同内核版本升级

现有基础内核版本2.6.21-git2,想转成2.6.22-rc1-mm2内核stable版本,应该怎么办?

a) 同1中下载patch-2.6.21-git2, patch-2.6.22-rc1, 2.6.22-rc1-mm2文件,

并设置执行路径和环境;

b)打补丁

patch -R -p1 < patch-2.6.21-git2 变成了2.6.21
patch -p1 < patch-2.6.22-rc1 变成了2.6.22-rc1
patch -p1 < 2.6.22-rc1-mm2 变成了2.6.22-rc1-mm2

c) 回退操作

patch -R -p1 < 2.6.22-rc1-mm2 变成了2.6.22-rc1
patch -R -p1 < patch-2.6.22-rc1 变成了2.6.21
patch -p1 < patch-2.6.21-git2 变成了2.6.21-git2
05-27 08:42