• 让你快速高效的掌握linux内核编译过程


    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

    嵌入式物联网需要学的东西真的非常多,千万不要学错了路线和内容,导致工资要不上去!

    无偿分享大家一个资料包,差不多150多G。里面学习内容、面经、项目都比较新也比较全!某鱼上买估计至少要好几十。(点击找小助理领取)

     

  • 相关阅读:
    TypeScript手写+项目实际
    Python学习小组课程P3-Python爬虫(1)HTML与Json解析
    关于CSS 盒子模型的基础教程
    代理IP、Socks5代理与网络工程:解析技术世界的无限可能
    Linux C/C++ 系统错误
    慢SQL,压垮团队的最后一根稻草!
    【Python 千题 —— 基础篇】列表倒转
    Fauce:Fast and Accurate Deep Ensembles with Uncertainty for Cardinality Estimation 论文解读(VLDB 2021)
    node.js 简介 以及 模块化 的介绍
    利用yolov5进行目标检测,并将检测到的目标裁剪出来
  • 原文地址:https://blog.csdn.net/m0_70888041/article/details/127997607