
顶层MAKEFILE1.环境准备作用设置MAKEFLAGSexport全局变量处理命令行参数V1、Oxxx、C1。MAKEFLAGS -rR-r和-R告诉 Make 禁用所有内置的隐式规则和变量。为什么因为 Linux 内核极其庞大如果不禁用Make 可能会用内置的规则去胡乱编译文件导致灾难。整个编译规则必须由 Kbuild 自己全盘接管。通过$(origin V)、$(origin O)拦截命令行参数决定是静默编译还是详细输出决定产物是放源码树里还是放到指定的O目录。把你之前看到的export ARCH、CROSS_COMPILE、CC、LD、KBUILD_CFLAGS等等统统塞进环境变量。这就是给后面的子目录 Makefile 下发“红头文件”。2.config 配置目标作用生成唯一的.config我们之前详细拆解过这句make -f scripts/Makefile.build objscripts/kconfig imx_v7_defconfig。它先用宿主机的 GCC 编译出配置工具然后读取开发板的默认配置文件最后生成.config。没有这一步Kbuild 就像个无头苍蝇不知道该编译谁。1给Makefile看的include/config/auto.confCONFIG_XXXy CONFIG_YYYm然后顶层Makefile会通过-include include/config/auto.conf将其包含进来。这样所有CONFIG_配置就都变成了Make环境里的变量。作用Kbuild的Makefile就可以用obj-$(CONFIG_XXX)这样的语法来动态决定是编译进内核obj-y、编译成模块obj-m还是根本不编译obj-为空。2给C代码看的include/generated/autoconf.h3.定义各模块目录all依赖于vmlinux, vmlinux依赖于$(KBUILD_LDS(KBUILD_VMLINUX_INIT),$(KBUILD_VMLINUX_MAIN),$(KBUILD_VMLINUX_MAIN)。1$(KBUILD_LDS)链接脚本施工图纸KBUILD_LDS : arch/$(SRCARCH)/kernel/vmlinux.lds作用当你把成千上万个.o文件丢给链接器ld时链接器会一脸懵逼“谁排前面谁排后面代码段放哪数据段放哪”vmlinux.lds就是告诉链接器的规矩。比如它会规定 CPU 启动执行的第一条指令head.o必须被放在内存的最开头。它规定了哪些段.text、.data、.bss按什么顺序排列。2$(KBUILD_VMLINUX_INIT)启动与初始化依赖地基export KBUILD_VMLINUX_INIT : $(head-y) $(init-y)head-y由架构相关的 Makefile 提供比如 ARM 下是arch/arm/kernel/head.o。这是内核启动的第一段汇编代码负责设置 CPU 模式、开启 MMU 等。init-y通常是init/built-in.o。包含了内核启动时的初始化流程比如著名的start_kernel函数就在这里。它在顶层make中被定义。3$(KBUILD_VMLINUX_MAIN)内核主体依赖大楼主体export KBUILD_VMLINUX_MAIN : $(core-y) $(libs-y) $(drivers-y) $(net-y)core-y核心功能进程调度kernel/、内存管理mm/、文件系统fs/等。libs-y内核通用库lib/里的各种算法和字符串处理。drivers-y所有的设备驱动drivers/目录下所有的built-in.o。net-y网络协议栈net/。这些模块的文件夹也是在顶层Makefile中定义。4.启动kbuild引擎init-y : $(patsubst %/, %/built-in.o, $(init-y))core-y : $(patsubst %/, %/built-in.o, $(core-y))drivers-y : $(patsubst %/, %/built-in.o, $(drivers-y))net-y : $(patsubst %/, %/built-in.o, $(net-y))libs-y1 : $(patsubst %/, %/lib.a, $(libs-y))libs-y2 : $(patsubst %/, %/built-in.o, $(libs-y))libs-y : $(libs-y1) $(libs-y2)首先各个目录会被替换成init-y/built-in.o core-y/built-in.o......。然后把这些依赖排序并打包。$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;vmlinux-dirs定义如下:vmlinux-dirs : $(patsubst %/,%,$(filter %/, $(init-y) $(init-m) \$(core-y) $(core-m) $(drivers-y) $(drivers-m) \$(net-y) $(net-m) $(libs-y) $(libs-m)))而后匹配到如下规则即可启动递归编译。$(vmlinux-dirs): prepare scripts$(Q)$(MAKE) $(build)$# → make -f scripts/Makefile.build obj/init# → make -f scripts/Makefile.build obj/kernelKBUILD系统以make -f scripts/Makefile.build obj/drivres为例对KNUILD的运行进行理解。make程序会把scripts/Makefile.build作为makefileobj作为第一个参数运行。第一个规则如下:__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \$(subdir-ym) $(always)最重要的是(builtin-target这个目标.$(builtin-target): $(obj-y) FORCE$(call if_changed,link_o_target)其由我们我们传进来的/drivres生成:builtin-target : $(obj)/built-in.o如上图obj-y由drivers里面的子makefile定义此时obj-y {drivers/pwm,drivers/mmc....}等组成。然后在 kuild.lib会生成各个子目录的目标。obj-y {drivers/pwm/build-in.o,drivers/mmc/build-in.o}obj-y : $(patsubst %/, %/built-in.o, $(obj-y))然后生成obj-y后就可以链接就可以生成drivers/build-in.o。$(builtin-target): $(obj-y) FORCE$(call if_changed,link_o_target)同时 kuild.lib会利用如下代码生成subdir-ym {drivers/pwm,drivers/mmc....}subdir-ym : $(addprefix $(obj)/,$(subdir-ym)然后由kuild.include触发递归。PHONY $(subdir-ym)$(subdir-ym):$(Q)$(MAKE) $(build)$再下一次递归比如make -f scripts/Makefile.build obj/drivres/pwm。此时就是叶子节点了也就是/drivres/pwm下面全是C文件obj-y {drivers/pwm/core/.o,drivers/pwm/sysf/.o}。此时$(subdir-ym)为空不再触发往下的递归。obj-y匹配如下规则:$(obj)/%.o: $(src)/%.c $(recordmcount_source) FORCE$(call cmd,force_checksrc)$(call if_changed_rule,cc_o_c)生成obj-y而后调用链接规则生成drivers/pwm.o让生成调用组合$(builtin-target): $(obj-y) FORCE$(call if_changed,link_o_target)