信盈达—您身边的嵌入式&人工智能专家
全国免费咨询热线:400-8788-909

嵌入式培训:如何快速高效掌握Linux内核编译过程

时间:2018-08-26 00:00:00 来源:信盈达 作者:信盈达

Linux内核编译

 

一、linux内核的配置与编译:

 

1.配置内核

 

1)导入默认配置:

 

make xxxx_defconfig

 

1:xxxx表示内核支持的芯片的名称 比如make exynos_defconfig

 

2:内核源码中对每个支持的芯片都有默认的配置,默认配置很少只能保证系统完成最基本的功能

 

3:可以通过直接修改.config文件来进行内核的配置(麻烦),所有内核配置的本质都是修改.config文件

 

4:配置文件中xxxx=y表示添加了该功能,注释表示不添加该功能(减小内核体积)

 

2)修改配置(内核提供了几种简单的配置方法,其本质都是修改.config文件中的配置)

 

make gconfig 依赖于GTK库

 

make xconfig 依赖于QT库

 

make config 麻烦

 

-> make menuconfig

 

选中的状态切换:空格

 

搜索一个选项:/

 

[ ] 有两种状态。

 

输入Y,显示为“*”,表示内核中该功能被选中,相关功能代码将会被编译进内核。

 

输入N,显示为空,表示内核中该功能没有被选中。

 

< > 有三种状态

 

输入Y,显示为“*”,表示内核中该功能被选中

 

输入N,显示为空,表示内核中该功能没有被选中

 

输入M,则显示为“M”,表示内核中该功能被选为模块(编译后与内核镜像不在同一文件)

 

2.编译内核

 

make uImage 编译内核镜像(编译选中为“*”的选项到内核)

 

make modules 编译内核模块(编译选中为“M”的选项)

 

make dtbs 编译设备树文件(使驱动与设备建立关系的文件)

 

make clean 删除文件

 

二、在内核中添加子菜单:

 

每一级目录中都包含一个Kconfig文件,用于生成选配菜单的。按照Kconfig的语法修改对应的Kconfig文件就可以在菜单中添加自己的选项。在一个Kconfig文件中menu是本级菜单,如menu "Character devices",如果我们想在该菜单下自己添加子菜单按照语法添加在menu和endmenu之间即可

 

语法:

 

config FS4412_LED # 固定语法,config XXX,XXX用来与MakeFile关联,即配置后MakeFile会按照该配置进行编译

 

tristate "FS4412LED Device Support"

 

# tristate “菜单显示名称”;tristate表示该选项有三个状态<>(即可以选择成N/Y/M),bool表示有两个选项[](N/Y)

 

default n

 

# default n 默认是'N',即不添加该功能 default y 默认添加该功能default m 默认编译成模块(重新配置后生效)

 

depends on ARCH_EXYNOS4

 

# depends on 依赖的菜单 当有依赖关系的选项没有被选择时该选项也不显示

 

help

 

support leddevice on FS4412 develop board # 帮助文档 可有可无

 

详情参考:Documentation/kbuild

 

将自己的模块添加到菜单的步骤:

 

1.修改Kconfig添加子菜单

 

2.添加对应的.C文件(驱动文件)

 

3.修改对应的Makefile使菜单与编译关联(将驱动文件编译)

 

三、linux内核(uImage)的编译过程:

 

1.在顶层目录下执行make uImage

 

2.顶层目录下的MakeFile

 

155 srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))

 

# srctree为当前MakeFile所在目录

 

198 ARCH ?= arm # 自己修改的

 

203 SRCARCH := $(ARCH)

 

504 include $(srctree)/arch/$(SRCARCH)/Makefile

 

-> include arch/arm/MakeFile

 

-> 执行make uImage后会根据我们设置的CPU架构,执行对应架构下的MakeFile

 

3.arch/arm/MakeFile

 

291 boot := arch/arm/boot

 

299 BOOT_TARGETS = zImage Image xipImage bootpImage uImage

 

# uImage属于BOOT_TARGETS中的一个成员

 

304 $(BOOT_TARGETS): vmlinux

 

# 生成uImage就要生成BOOT_TARGETS大目标

 

# 依赖源码顶层目录下的vmlinux(编译过程中生成的文件)

 

305 $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

 

# Q是对@的封装 不输出编译信息

 

# MAKE是对make -C的封装 -C表示切换到指定目录下的MakeFile

 

# $(build)=$(boot) -> build = arch/arm/boot

 

# MACHINE=$(MACHINE) -> MACHINE := arch/arm/mach-exynos

 

# $(boot)/$@ -> arch/arm/boot/uImage

 

-> make -C arch/arm/boot MACHINE=arch/arm/mach-exynos arch/arm/boot/uImage

 

-> 依赖顶层目录下的vmlinux,切到arch/arm/boot下的MakeFile(参数是MACHINE)执行,在arch/arm/boot/下生成目标uImage(uImage就是在该目录)

 

/***************MACHINE变量的来源******************/

 

.config/291 CONFIG_ARCH_EXYNOS=y

 

155 machine-$(CONFIG_ARCH_EXYNOS) += exynos

 

-> 在最开始导入配置时产生了.config文件,.config中只对CONFIG_ARCH_EXYNOS进行了赋值(其他都注释)

 

-> machine-y += exynos 即 machine-y = exynos

 

232 ifneq ($(machine-y),)

 

# 比较machine-y与空的值(machine-y = exynos),不相等

 

233 MACHINE := arch/arm/mach-$(word 1,$(machine-y))/ # word 1,$(machine-y) word为函数 表示从machine-y变量中取第一个单词

 

-> MACHINE := arch/arm/mach-exynos

 

/********************************************************************/

 

4.arch/arm/boot/Makefile

 

78 $(obj)/uImage: $(obj)/zImage FORCE

 

# 依赖于zImage obj表示当前MakeFile所在路径 FORCE强制编译不管是否编译过

 

79 @$(check_for_multiple_loadaddr)

 

80 $(call if_changed,uimage)

 

81 @$(kecho) ' Image $@ is ready' # 输出信息

 

-> 依赖当前目录下的zImage生成uImage

 

-> 追踪zImage

 

30 ifeq ($(CONFIG_XIP_KERNEL),y)

 

# CONFIG_XIP_KERNEL在.config中定义 没有赋值

 

41 else

 

54 $(obj)/zImage: $(obj)/compressed/vmlinux FORCE

 

# 依赖arch/arm/boot/compressed/vmlinux(与之前不是同一个)

 

55 $(call if_changed,objcopy)

 

# 使用gcc编译出的程序都是elf格式的,需要进行二进制格式的转换

 

56 @$(kecho) ' Kernel: $@ is ready' # 显示信息

 

51 $(obj)/compressed/vmlinux: $(obj)/Image FORCE

 

# vmlinux依赖于arch/arm/boot/Image

 

52 $(Q)$(MAKE) $(build)=$(obj)/compressed $@

 

# make -C arch/arm/boot/compressed arch/arm/boot/compressed/vmlinux

 

-> 依赖arch/arm/boot/Image,切换到arch/arm/boot/compressed/下的MakeFile生成vmlinux

 

/***********MACHINE参数的引用*******************/

 

arch/arm/boot/Makefile

 

14 ifneq ($(MACHINE),)

 

# arch/arm/MakeFile中传参进来MACHINE=arch/arm/mach-exynos

 

15 include $(srctree)/$(MACHINE)/Makefile.boot

 

# include arch/arm/mach-exynos/Makefile.boot

 

16 endif

 

arch/arm/mach-exynos/Makefile.boot

 

1 zreladdr-y += 0x40008000

 

# 内核的入口地址(运行起始地址)

 

2 params_phys-y := 0x40000100

 

# uboot和内核协商好的存放参数的位置

 

arch/arm/boot/Makefile

 

22 ZRELADDR := $(zreladdr-y)

 

# ZRELADDR = 0x40008000

 

23 PARAMS_PHYS := $(params_phys-y)

 

# PARAMS_PHYS = 0x40000100

 

24 INITRD_PHYS := $(initrd_phys-y)

 

25

 

26 export ZRELADDR INITRD_PHYS PARAMS_PHYS

 

# 导出让全局可用

 

/*****************************************************************/

 

5.arch/arm/boot/compressed/MakeFile

 

185 $(obj)/vmlinux: $(obj)/vmlinux.lds $(obj)/$(HEAD) $(obj)/piggy.$(suffix_y).o \

 

186 $(addprefix $(obj)/, $(OBJS)) $(lib1funcs) $(ashldi3) \

 

187 $(bswapsdi2) FORCE

 

# vmlinux依赖很多文件产生

 

# 依赖文件的来源

 

# $(obj)/vmlinux.lds

 

# arch/arm/boot/compressed/vmlinux.lds 链接脚本决定elf文件的排版(内核源文件)

 

# $(obj)/$(HEAD) = arch/arm/boot/compressed/$(HEAD)

 

# 25 HEAD = head.o 由 head.S生成(内核源文件)

 

# $(obj)/piggy.$(suffix_y).o

 

# 86 suffix_$(CONFIG_KERNEL_GZIP) = gzip

 

# .config/40 CONFIG_KERNEL_GZIP=y 导入默认配置时配置的内核的压缩格式

 

# ->suffix_y = gzip

 

# -> $(obj)/piggy.$(suffix_y).o = arch/arm/boot/compressed/piggy.gzip.o 由 piggy.gzip.S(编译中间生成)生成

 

# $(addprefix $(obj)/, $(OBJS)) # 给变量$(OBJS))添加前缀$(addprefix $(obj)/

 

# 26 OBJS += misc.o decompress.o -> OBJS = misc.o decompress.o

 

# -> $(addprefix $(obj)/, $(OBJS)) = arch/arm/boot/compressed/misc.o decompress.o 由 misc.c decompress.c生成(内核源文件)

 

# 这两个文件用于解压内核 内核当中包含了解压的代码

 

# $(lib1funcs)

 

# 148 lib1funcs = $(obj)/lib1funcs.o -> lib1funcs = arch/arm/boot/compressed/lib1funcs.o(lib1funcs.S生成)

 

# 150 $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S(内核源文件)

 

# $(ashldi3)

 

# 154 ashldi3 = $(obj)/ashldi3.o -> ashldi3 = arch/arm/boot/compressed/ashldi3.o(ashldi3.S生成)

 

# 156 $(obj)/ashldi3.S: $(srctree)/arch/$(SRCARCH)/lib/ashldi3.S(内核源文件)

 

# $(bswapsdi2)

 

# 160 bswapsdi2 = $(obj)/bswapsdi2.o -> bswapsdi2 = arch/arm/boot/compressed/bswapsdi2.o(bswapsdi2.S生成)

 

# 162 $(obj)/bswapsdi2.S: $(srctree)/arch/$(SRCARCH)/lib/bswapsdi2.S(内核源文件)

 

/******************************piggy.gzip.o********************************/

 

# 195 $(obj)/piggy.$(suffix_y).o: $(obj)/piggy.$(suffix_y) FORCE -> piggy.gzip.o:piggy.gzip -> piggy.gzip.o依赖piggy.gzip

 

# 192 $(obj)/piggy.$(suffix_y): $(obj)/../Image FORCE -> piggy.gzip:arch/arm/boot/Image FORCE -> piggy.gzip依赖上层目录的Image

 

-> 退回上层目录追踪arch/arm/boot/Image

 

/*************************************************************************/

 

6.arch/arm/boot/MakeFile

 

47 $(obj)/Image: vmlinux FORCE

 

# 依赖顶层源码下的vmlinux FORCE强制执行

 

48 $(call if_changed,objcopy)

 

49 @$(kecho) ' Kernel: $@ is ready'

 

-> 退回顶层源码下追踪vmlinux

 

7.顶层目录MakeFile

 

817 vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE

 

# scripts/link-vmlinux.sh 编译需要的脚本

 

# 809 vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

 

# 追踪得到一些路径下的源文件

 

# 802 export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)

 

# 803 export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)

 

# 804 export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds

 

# 链接脚本(两次链接过程)

 

逆序总结:uImage由zImage通过uboot中的工具为其添加头部生成

 

zImage由arch/arm/boot/compressed/vmlinux(elf格式)文件经过二进制格式转换生成

 

vmlinux由/arch/arm/boot/compressed/piggy.gzip(压缩后的内核)和misc.c、decompress.c、(用于解压内核的代码)按照vmlinux.lds链接生成(二次链接)

 

piggy.gzip由/arch/arm/boot/Image文件(不包含解压代码的二进制内核文件)压缩得到

 

Image文件是由源码顶层目录下的vmlinux(elf格式)经过二进制转换生成的

 

vmlinux是由一系列的内核源码与工具按照链接脚本arch/arm/kernel/vmlinux.lds链接生成(一次链接)

 

顺序总结:一系列的源码与工具按照arch/arm/kernel/vmlinux.lds与各级Makefile的规则编译生成源码顶层目录下的vmlinux(elf格式内核)即第一次链接

 

vmlinux经过二进制转换生成arch/arm/boot/Image(二进制格式的内核)

 

Image文件经过压缩得到piggy.gzip文件(压缩后的二进制内核)(piggy.gzip文件的运行需要解压)

 

piggy.gzip与解压该文件的代码misc.c、decompress.c、等按照链接脚本arch/arm/boot/compressed/vmlinux.lds链接成一个文件(第二次链接)vmlinux(elf)

 

vmlinux文件经过二进制转换生成zImage

 

zImage经过添加头部生成uImage


信盈达2008年在深圳特区南山高新科技园成立。自成立至今近九年来专注为企业和个人提供高端方案设计、高端嵌入式/Android培训等服务。公司下设信盈达实训学院、信盈达研发中心、信盈达教学仪器三大业务板块。九年来公司坚持"技术领先、服务领先",以雄厚的实力和专业的品质成为国内唯一有实力从产品最底层研发到系统层开发的嵌入式实训、产品解决方案提供商。为中国IT行业提供最具价值的职业教育服务。专业嵌入式、物联网、人工智能Java、单片机等课程,想了解更多信息点击立马咨询