最近工作不太忙,研究了一下Linux内核的编译过程,在此简要记录一下。
$(obj)/zImage: $(obj)/compressed/vmlinux FORCE
$(call if_changed,objcopy)
linux的内核 zImage 的生成依赖于 vmlinux。
分析内核的底层 makefile 如下:
vmlinux: scripts/link-vmlinux.sh vmlinux_prereq $(vmlinux-deps) FORCE
+$(call if_changed,link-vmlinux)
vmlinux_prereq: $(vmlinux-deps) FORCE
发现vmlinux的生成主要依赖于 vmlinux-deps。
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y2) $(drivers-y) $(net-y) $(virt-y)
export KBUILD_VMLINUX_LIBS := $(libs-y1)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) $(KBUILD_VMLINUX_LIBS)
KBUILD_LDS:是一个链接文件
KBUILD_VMLINUX_MAIN:是一些库和驱动
这里重点分析一下KBUILD_VMLINUX_INIT
init-y := $(patsubst %/, %/built-in.o, $(init-y))
...
KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
可以看出来 KBUILD_VMLINUX_INIT 是各个字目录下的 built-in.o 文件。
那么 built-in.o 是怎么来的呢?
查看 /scripts/Makefile.build 文件
ifneq ($(strip $(obj-y) $(obj-m) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif
...
$(builtin-target): $(obj-y) FORCE
$(call if_changed,link_o_target)
各个目录下的 built-in.o 依赖 (obj-y),至此已经明朗了,$(obj-y) 就是各个目录下的源文件编译出来的 *.o 文件。
拓展:
当我们在内核中添加自己的源码 并希望其编译进内核时,通常在子目录下这样修改makefile
...
obj-y += myself_file.o
...
在makefile文件中,$(obj-y) 的顺序也很重要,是源码的编译顺序 以及链接到built-in.o 中的顺序。所以一些初始化函数 (module_init() / _initcall)会在启动的时候按照排列顺序调用,一旦改变链接顺序,也可能会改变设备的初始化顺序,因此会导致系统启动出现问题。