[TOC]
阅读技巧
- 使用
make V=1
来查看详细的编译过程 - 使用
make -p
来查看所有的变量和规则
顶层makefile
判断GUN make 版本
- 检查当前的 Make 版本以确保满足条件
- 如果 output-sync 不在其中,这意味着当前使用的 GNU Make 版本低于 4.0,因此代码会触发一个错误,提示用户需要 GNU Make 4.0 或更高版本,并显示当前的 Make 版本 $(MAKE_VERSION)。
1 | ifeq ($(filter output-sync,$(.FEATURES)),) |
检测内部变量是否被覆盖
- 检查
$(MAKECMDGOALS)
变量中是否包含以__
开头的目标。$(MAKECMDGOALS)
是一个特殊变量,包含了命令行中指定的所有目标。如果发现有以__
开头的目标,代码会触发一个错误,提示这些目标仅供内部使用。这种检查有助于防止用户意外调用内部目标,确保构建过程的正确性和安全性。
1 | $(if $(filter __%, $(MAKECMDGOALS)), \ |
makefile路径引用
- $(MAKEFILE_LIST) 是一个特殊变量,它包含了当前正在处理的所有 Makefile 文件的列表。$(lastword …) 函数会返回这个列表中的最后一个元素,即当前正在执行的 Makefile 文件的路径。
- $(dir …) 函数会返回 this-makefile 所在目录的路径,而 $(realpath …) 函数会将这个路径转换为绝对路径。因此,abs_srctree 变量最终包含了当前 Makefile 文件所在目录的绝对路径。
1 | this-makefile := $(lastword $(MAKEFILE_LIST)) |
指定模块输出目录
word
函数获取KBUILD_EXTMOD
的第二个单词,如果有则报错foreach
将%
和:
赋值给x
,然后执行findstring
函数,从KBUILD_EXTMOD
中查找$(x)
,如果找到,则触发错误,提示模块目录路径不能包含
1 | ifeq ("$(origin M)", "command line") |
判断源目录不能包含空格或冒号
subst
将:
替换为空格words
函数计算替换后的单词数ifneq
判断单词数是否不等于1,如果不等于1,则报错
1 | ifneq ($(words $(subst :, ,$(abs_srctree))), 1) |
KBUILD_BUILTIN 编译内置对象 KBUILD_MODULES 编译模块
1 | # 决定是构建 built-in、modular,还是两者兼而有之。 |
scripts 脚本构建
scripts/Kbuild.include 通用定义
1 | # 顶层makefile导入Kbuild.include文件 |
cmd 判定有调用命令执行,无调用报错
- 这段代码的目的是根据条件执行特定的命令。如果 cmd_$(1) 变量非空,则执行一系列命令,包括设置错误退出模式、打印日志信息、执行清理操作以及实际的命令执行。如果 cmd_$(1) 变量为空,则执行空命令。这种结构有助于在Makefile中实现灵活的命令控制和错误处理
1 | # scripts/Kbuild.include |
$(build) 设置编译的目录为Makefile.build 目标为输入的目录
- 调用scripts/Makefile.build的构建规则,执行目标为输入的构建内容
1 | ### |
if_changed 执行命令并后写入命令到dot-target.cmd
if_changed
的作用是执行一个命令,并在命令执行后处理生成的 .d 依赖项文件。它会检查命令是否发生了变化,如果发生了变化,则执行命令并更新依赖项文件。- 并且会写入执行的命令到
dot-target.cmd
中 $(cmd)
调用cmd
函数
1 | # 查找任何比 target 更新或不存在的先决条件。 |
if_changed_dep 执行命令并后处理生成的 .d 依赖项文件,将命令写入dot-target.cmd
- 执行
cmd
函数,调用 fixdep 工具来处理依赖关系。 - $(depfile) 是依赖文件的路径
- $@ 是一个自动变量,表示当前目标的名称。
- $(make-cmd) 是一个变量,通常包含 make 命令的调用。
$(dot-target).cmd 将 fixdep 工具的输出重定向到 $(dot-target).cmd 文件中
- 最后删除依赖文件
1 | if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:) |
scripts/Makefile.lib
subdir path
1 | # Add subdir path |
always-y 总是构建的目标
1 | always-y += $(always-m) #为空 scripts/Makefile.build line:l9 |
subdir-ym 子目录构建的目标
suffix-search multi-search real-search 搜索
suffix-search
: 遍历后缀列表$3
,并将$1
中匹配$(strip $2)
后缀的部分替换为每个后缀s
。通过这种方式,可以扩展文件列表或模式,将其转换为具有不同后缀的文件列表。strip
: 删除字符串两端的空格multi-search
: 遍历$1
中的每个元素m
,并检查是否存在与$2
和$3
匹配的后缀。如果存在,则返回该元素m
。real-search
: 遍历$1
中的每个元素m
,并检查是否存在与$2
和$3
匹配的后缀。如果存在,则返回扩展后的文件列表,否则返回原始元素m
。
1 | # Expand $(foo-objs) $(foo-y) etc. by replacing their individuals |
multi-obj-y multi-obj-m multi-obj-ym real-obj-y real-obj-m 对象
multi-obj-y
:.o
和-objs -y
匹配的对象multi-obj-m
:.o
和-objs -y -m
匹配的对象multi-obj-ym
:multi-obj-y
和multi-obj-m
的组合real-obj-y
:.o
和-objs -y
匹配的对象real-obj-m
:.o
和-objs -y -m
匹配的对象
1 | # If $(foo-objs), $(foo-y), $(foo-m), or $(foo-) exists, foo.o is a composite object |
scripts/Makefile.build
i最开始的nclude
1 | # Read auto.conf if it exists, otherwise ignore |
必要时包含其他构建规则
- 其中$(hostprogs)由
scripts/Makefile.lib
传入
1 | # scripts/Makefile.lib |
$(obj)/: 构建传入的目录下的目标
- 如果
KBUILD_BUILTIN
有值,则构建targets-for-builtin
- 如果
KBUILD_MODULES
有值,则构建targets-for-modules
subdir-ym
和$(always-y)
在此时还没有在本文件值,在后面会赋值;在这里是scripts/Makefile.lib
中进行了赋值
1 | # scripts/Makefile.lib |
targets-for-builtin 内置目标
targets-for-builtin
=$(extra-y)
$(lib-y)
、$(lib-m)
和$(lib-)
有一个不为空,添加构建$(obj)/lib.a
- 有定义
need-builtin
,添加构建$(obj)/built-in.a
1 | targets-for-builtin := $(extra-y) |
scripts/basic fixdep:用于在构建过程中生成依赖信息
1 | hostprogs-always-y += fixdep |
scripts/Makefile.host 主机程序构建
host-csingle 从单个 .c 文件创建可执行文件
1 | # Create executable from a single .c file |
- 示例如下
1 | # HOSTCC scripts/basic/fixdep |
outputmakefile 输出目录的构建
- 可以看到outputmakefile的构建依赖于
building_out_of_srctree
变量,在不是当前目录构建时才会触发
1 | ifdef building_out_of_srctree |
building_out_of_srctree
在构建目录就是当前目录时,没有值,否则有值
1 | ifeq ($(srcroot),$(CURDIR)) |
- 如果判定源码树不干净,将会打印错误
1 | outputmakefile: |
- 具体的构建过程如下:
- 创建软连接
- 生成Makefile,有
KBUILD_EXTMOD
时,会将KBUILD_OUTPUT
和KBUILD_EXTMOD_OUTPUT
写入Makefile;
没有KBUILD_EXTMOD
时,会将KBUILD_OUTPUT
写入Makefile - 生成.gitignore
1 | ifdef KBUILD_EXTMOD |
scripts_basic 基础脚本构建
- 展开为
make -f ./scripts/Makefile.build obj=scripts/basic
1 | scripts_basic: |
./scripts/Makefile.build
执行了$(obj)/:
构建
1 | $(obj)/: $(subdir-ym) $(always-y) |
- 在
./scripts/Makefile.build
下导入了./scripts/Makefile.lib
,将always-y
和subdir-ym
赋值
1 | #./scripts/Makefile.build |
- 所以
$(obj)/:
构建的目标为fixdep
,进而调用./scripts/Makefile.build
执行scripts/basic/fixdep
的构建 - 这时候进入
scripts/Makefile.lib
,hostprogs-always-y
在scripts/basic/Makefile
进行了赋值为fixdep
1 | hostprogs += $(hostprogs-always-y) $(hostprogs-always-m) |
- 所以
scripts/Makefile.build
导入了scripts/Makefile.host
1 | hostprogs := $(sort $(hostprogs)) |
- 在
scripts/Makefile.host
中,执行了host-csingle
的构建
1 | $(host-csingle): $(obj)/%: $(obj)/%.c FORCE |
- 最终执行了
gcc
命令,生成fixdep
1 | gcc -Wp,-MMD,scripts/basic/.fixdep.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -I ./scripts/include -o scripts/basic/fixdep scripts/basic/fixdep.c |
scripts/basic/fixdep.c 处理依赖关系,确保只有需要修改的配置会被重新编译
fixdep
是 Linux 内核构建过程中使用的一个工具,用于处理依赖关系。它的主要作用是优化由gcc -MD
生成的依赖项列表,以避免不必要的重新编译。以下是fixdep
的作用及其工作原理的详细解释:
作用
优化依赖项列表:
fixdep
工具读取由gcc -MD
生成的依赖项文件,并对其进行处理,以优化依赖关系。具体来说,它会过滤掉对autoconf.h
的依赖,并添加对每个include/config/CONFIG_OPTION
文件的依赖。避免不必要的重新编译:
通过优化依赖项列表,fixdep
工具可以避免在用户重新运行make *config
后,重新编译所有包含autoconf.h
的文件。这样可以显著减少不必要的重新编译,提高构建效率。
工作原理
fixdep
工具的工作原理可以分为以下几个步骤:
读取依赖项文件:
fixdep
工具首先读取由gcc -MD
生成的依赖项文件。这个文件包含了目标文件及其依赖的源文件列表。1
2
3void *read_file(const char *filename) {
// 打开并读取文件内容
}解析依赖项文件:
fixdep
工具解析依赖项文件,提取目标文件及其依赖的源文件。它会过滤掉对autoconf.h
的依赖,并添加对每个include/config/CONFIG_OPTION
文件的依赖。1
2
3static void parse_dep_file(char *p, const char *target) {
// 解析依赖项文件内容
}处理 CONFIG_ 选项*:
fixdep
工具会查找依赖项文件中提到的所有CONFIG_*
选项,并将它们添加到依赖关系中。这样,当配置选项发生变化时,相关的目标文件会被重新编译。1
2
3static void parse_config_file(const char *p) {
// 查找并处理 CONFIG_* 选项
}生成优化后的依赖项列表:
fixdep
工具生成优化后的依赖项列表,并将其输出到标准输出。这个列表包含了目标文件及其依赖的源文件,以及对include/config/CONFIG_OPTION
文件的依赖。1
2
3int main(int argc, char *argv[]) {
// 读取依赖项文件并生成优化后的依赖项列表
}
示例
假设有一个依赖项文件 foo.d
,内容如下:
1 | foo.o: foo.c bar.h include/generated/autoconf.h |
fixdep
工具会读取这个文件,并生成如下的优化后的依赖项列表:
1 | savedcmd_foo.o := gcc -MD -o foo.o foo.c |
通过这种方式,fixdep
工具可以确保在配置选项发生变化时,相关的目标文件会被重新编译,而不会因为 autoconf.h
的变化而重新编译所有文件。
总结
fixdep
工具在 Linux 内核构建过程中起到了关键作用,通过优化依赖项列表,避免不必要的重新编译,提高了构建效率。它通过读取和解析依赖项文件,处理 CONFIG_*
选项,并生成优化后的依赖项列表,确保构建过程的高效和准确。
scripts/kconfig
scripts/kconfig/conf
1 | gcc -Wp,-MMD,scripts/basic/.fixdep.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -std=gnu11 -I ./scripts/include -o scripts/basic/fixdep scripts/basic/fixdep.c |
conf
是 Linux 内核配置系统的一部分,用于处理内核配置文件(.config
)的生成和更新。它提供了多种模式来配置内核选项,并确保配置文件的一致性和正确性。以下是 conf
的作用及其工作原理的详细解释:
作用
生成和更新配置文件:
conf
工具用于生成和更新内核配置文件(.config
)。它可以根据用户输入或预定义的配置文件来设置内核选项。支持多种配置模式:
conf
工具支持多种配置模式,包括交互式配置、自动配置、同步配置等。每种模式适用于不同的使用场景,提供了灵活的配置方式。确保配置文件的一致性:
conf
工具会检查配置文件中的依赖关系和约束条件,确保生成的配置文件是有效的,并且符合内核的要求。
工作原理
conf
工具的工作原理可以分为以下几个步骤:
解析命令行参数:
conf
工具首先解析命令行参数,以确定使用的配置模式和相关选项。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21static const struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
{"silent", no_argument, NULL, 's'},
{"oldaskconfig", no_argument, &input_mode_opt, oldaskconfig},
{"oldconfig", no_argument, &input_mode_opt, oldconfig},
{"syncconfig", no_argument, &input_mode_opt, syncconfig},
{"defconfig", required_argument, &input_mode_opt, defconfig},
{"savedefconfig", required_argument, &input_mode_opt, savedefconfig},
{"allnoconfig", no_argument, &input_mode_opt, allnoconfig},
{"allyesconfig", no_argument, &input_mode_opt, allyesconfig},
{"allmodconfig", no_argument, &input_mode_opt, allmodconfig},
{"alldefconfig", no_argument, &input_mode_opt, alldefconfig},
{"randconfig", no_argument, &input_mode_opt, randconfig},
{"listnewconfig", no_argument, &input_mode_opt, listnewconfig},
{"helpnewconfig", no_argument, &input_mode_opt, helpnewconfig},
{"olddefconfig", no_argument, &input_mode_opt, olddefconfig},
{"yes2modconfig", no_argument, &input_mode_opt, yes2modconfig},
{"mod2yesconfig", no_argument, &input_mode_opt, mod2yesconfig},
{"mod2noconfig", no_argument, &input_mode_opt, mod2noconfig},
{NULL, 0, NULL, 0}
};读取配置文件:
根据命令行参数,conf
工具读取现有的配置文件或生成新的配置文件。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50switch (input_mode) {
case defconfig:
if (conf_read(defconfig_file)) {
fprintf(stderr, "*** Can't find default configuration \"%s\"!\n", defconfig_file);
exit(1);
}
break;
case savedefconfig:
case syncconfig:
case oldaskconfig:
case oldconfig:
case listnewconfig:
case helpnewconfig:
case olddefconfig:
case yes2modconfig:
case mod2yesconfig:
case mod2noconfig:
conf_read(NULL);
break;
case allnoconfig:
case allyesconfig:
case allmodconfig:
case alldefconfig:
case randconfig:
name = getenv("KCONFIG_ALLCONFIG");
if (!name)
break;
if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
if (conf_read_simple(name, S_DEF_USER)) {
fprintf(stderr, "*** Can't read seed configuration \"%s\"!\n", name);
exit(1);
}
break;
}
switch (input_mode) {
case allnoconfig: name = "allno.config"; break;
case allyesconfig: name = "allyes.config"; break;
case allmodconfig: name = "allmod.config"; break;
case alldefconfig: name = "alldef.config"; break;
case randconfig: name = "allrandom.config"; break;
default: break;
}
if (conf_read_simple(name, S_DEF_USER) && conf_read_simple("all.config", S_DEF_USER)) {
fprintf(stderr, "*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n", name);
exit(1);
}
break;
default:
break;
}处理配置选项:
根据配置模式,conf
工具处理配置选项。对于交互式模式,它会提示用户输入配置选项;对于自动模式,它会根据预定义的规则设置配置选项。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49switch (input_mode) {
case allnoconfig:
conf_set_all_new_symbols(def_no);
break;
case allyesconfig:
conf_set_all_new_symbols(def_yes);
break;
case allmodconfig:
conf_set_all_new_symbols(def_mod);
break;
case alldefconfig:
conf_set_all_new_symbols(def_default);
break;
case randconfig:
conf_set_all_new_symbols(def_random);
break;
case defconfig:
conf_set_all_new_symbols(def_default);
break;
case savedefconfig:
break;
case yes2modconfig:
conf_rewrite_tristates(yes, mod);
break;
case mod2yesconfig:
conf_rewrite_tristates(mod, yes);
break;
case mod2noconfig:
conf_rewrite_tristates(mod, no);
break;
case oldaskconfig:
rootEntry = &rootmenu;
conf(&rootmenu);
input_mode = oldconfig;
/* fall through */
case oldconfig:
case listnewconfig:
case helpnewconfig:
case syncconfig:
/* Update until a loop caused no more changes */
do {
conf_cnt = 0;
check_conf(&rootmenu);
} while (conf_cnt);
break;
case olddefconfig:
default:
break;
}写入配置文件:
处理完所有配置选项后,conf
工具将生成的配置写入配置文件auto.conf
与.config
中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17if (input_mode == savedefconfig) {
if (conf_write_defconfig(defconfig_file)) {
fprintf(stderr, "*** Error while saving defconfig to: %s\n", defconfig_file);
return 1;
}
} else if (input_mode != listnewconfig && input_mode != helpnewconfig) {
if (!no_conf_write && conf_write(NULL)) {
fprintf(stderr, "\n*** Error during writing of the configuration.\n\n");
exit(1);
}
/* Create auto.conf if it does not exist. */
if (conf_write_autoconf(sync_kconfig) && sync_kconfig) {
fprintf(stderr, "\n*** Error during sync of the configuration.\n\n");
return 1;
}
}
总结
conf
工具在 Linux 内核配置系统中起到了关键作用,通过支持多种配置模式,生成和更新内核配置文件,并确保配置文件的一致性和正确性。它通过解析命令行参数、读取配置文件、处理配置选项和写入配置文件,提供了灵活且高效的内核配置方式。
*config 构建
- config选项有如下
1 | 配置目标: |
- 可以看到*config的构建都是通过这里来实现的
- config构建依赖于
outputmakefile
和scripts_basic
1 | ifdef config-build |
- 举例
make stm32def_config
1 | # 展开为 |
- 进入
scripts/kconfig/Makefile
后,执行%_defconfig
构建.其中依赖于conf
用于处理.config
的程序
1 | %_defconfig: $(obj)/conf |
- 从而执行
conf
程序,处理stm32_defconfig
转成.config
vmlinux 构建
- 当使用
make -j
构建时,由于没有指定目标,makefile
将会使用第一个目标作为默认构建目标 - 当编译内核时,
vmlinux
就是默认目标
1 | # 当我们在命令行上没有给出 no 时,这就是我们的默认目标 |
vmlinux
的构建规则
1 | PHONY += vmlinux |
依赖构建 vmlinux.a
- 可以看到
vmlinux
的所有依赖 cmd_ar_vmlinux.a
: 这段代码定义了如何创建和操作 vmlinux.a 归档文件。它首先删除旧的归档文件,然后使用 ar 工具创建一个新的归档文件,并将指定的对象文件添加到归档文件中。最后,它对归档文件中的对象文件进行排序和移动操作,以确保特定的对象文件位于归档文件的开头。这种方式确保了构建过程中的文件依赖关系和操作的一致性。
1 | # '$(AR) mPi' needs 'T' to workaround the bug of llvm-ar <= 14 |
vmlinux.a
依赖于KBUILD_VMLINUX_OBJS
1
2
3
4
5
6
7
8KBUILD_VMLINUX_OBJS := ./built-in.a
ifdef CONFIG_MODULES
KBUILD_VMLINUX_OBJS += $(patsubst %/, %/lib.a, $(filter %/, $(libs-y)))
KBUILD_VMLINUX_LIBS := $(filter-out %/, $(libs-y))
else
# 将 $(libs-y) 中的所有目录转换为相应的 lib.a 文件路径,并将结果赋值给 KBUILD_VMLINUX_LIBS
KBUILD_VMLINUX_LIBS := $(patsubst %/,%/lib.a, $(libs-y))
endifKBUILD_VMLINUX_OBJS
依赖于built-in.a
,built-in.a
调用如下1
2
3
4
5
6
7
8
9
10
11# scripts/Makefile.build
# 将一组 .o 文件编译为一个 .a 文件的规则(无符号表)
#
# 要使此规则能够抵御“Argument list too long”错误,请删除 $(obj)/ 前缀,然后通过 shell 命令恢复它。
quiet_cmd_ar_builtin = AR $@
cmd_ar_builtin = rm -f $@; \
$(if $(real-prereqs), printf "$(obj)/%s " $(patsubst $(obj)/%,%,$(real-prereqs)) | xargs) \
$(AR) cDPrST $@
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)- 展开为:
1
2
3# AR init/built-in.a
rm -f init/built-in.a; printf "init/%s " main.o version.o do_mounts.o do_mounts_rd.o do_mounts_initrd.o initramfs.o calibrate.o init_task.o | xargs arm-none-eabi-ar cDPrST init/built-in.a
make -f ./scripts/Makefile.build obj=usr \$(real-obj-y)
所需要的obj-y
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25# Kbuild
# Ordinary directory descending
# ---------------------------------------------------------------------------
obj-y += init/
obj-y += usr/
obj-y += arch/$(SRCARCH)/
obj-y += $(ARCH_CORE)
obj-y += kernel/
obj-y += certs/
obj-y += mm/
obj-y += fs/
obj-y += ipc/
obj-y += security/
obj-y += crypto/
obj-$(CONFIG_BLOCK) += block/
obj-$(CONFIG_IO_URING) += io_uring/
obj-$(CONFIG_RUST) += rust/
obj-y += $(ARCH_LIB)
obj-y += drivers/
obj-y += sound/
obj-$(CONFIG_SAMPLES) += samples/
obj-$(CONFIG_NET) += net/
obj-y += virt/
obj-y += $(ARCH_DRIVERS)
vmlinux_o 构建
- 执行
scripts/Makefile.vmlinux_o
的makefile
1 | vmlinux_o: vmlinux.a $(KBUILD_VMLINUX_LIBS) |
scripts/Makefile.vmlinux_o
的默认构建为如下:
1 | __default: vmlinux.o modules.builtin.modinfo modules.builtin |
- 构建
vmlinux.o
- 这段代码定义了一个链接命令,用于生成 vmlinux.o 文件。它通过指定链接器、链接选项、输入文件和库文件,确保在构建过程中正确地链接所有必要的对象文件和库文件。这种方式确保了构建过程中的文件依赖关系和操作的一致性。
1 | ifdef CONFIG_LTO_CLANG |
- 展开为
1 | # LD vmlinux.o |
KBUILD_LDS vmlinux.lds
1 | #Makefile |
vmlinux 构建
1 | ARCH_POSTLINK := $(wildcard $(srctree)/arch/$(SRCARCH)/Makefile.postlink) |
Image 构建
- 使用objcopy将vmlinux转换为Image
- objcopy 是一个 GNU Binutils 工具,用于在不同的目标文件格式之间转换对象文件。它可以用于从一个格式转换到另一个格式,剥离不需要的部分,或者添加特定的部分。在内核构建过程中,objcopy 通常用于将内核镜像从 ELF 格式转换为二进制格式。
1 | # arch/arm/boot/Makefile |
zImage 构建
- 通过makefile的指令可以看出使用的是linux系统的gzip命令
- -n:不保存原始文件名和时间戳。这意味着压缩后的文件不会包含原始文件的名称和修改时间,这对于生成可重复的构建非常有用。
- -f:强制覆盖输出文件。如果目标文件已经存在,gzip 将覆盖它,而不会提示确认。
- -9:使用最高压缩级别。gzip 提供了从 -1 到 -9 的压缩级别,其中 -1 是最快的压缩(但压缩比最低),-9 是最慢的压缩(但压缩比最高)。
1 | # arch/arm/boot/compressed/Makefile |
1 | cat arch/arm/boot/compressed/../Image | gzip -n -f -9 > arch/arm/boot/compressed/piggy_data |