上一节我们找到了整个KBuild体系的第一个目标,根据是否进行了编译配置而分别是385行的scripts_basic和949行的include/config/kernel.release。更重要的是,通过第一个目标的寻找,我们把KBuild体系的整个顶层Makefile文件脉络梳理了一遍,这对我们研究vmlinux的形成及其重要。下面我们就从第一个目标开始,对编译配置后make的整个过程来过一遍。
949行,第一个目标:
949 include/config/kernel.release: include/config/auto.conf FORCE
950 $(Q)rm -f $@
951 $(Q)echo $(kernelrelease) > $@
执行的命令很简单,删除旧的kernel.release,新建一个kernel.release并把kernelrelease变量的值转移标准输出,写入该文件:
358 KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
948 kernelrelease = $(KERNELVERSION)$(localver-full)
第一个目标完成后,来到966行的prepare3目标:
966 prepare3: include/config/kernel.release
967 ifneq ($(KBUILD_SRC),)
968 @$(kecho) ' Using $(srctree) as source for kernel'
969 $(Q)if [ -f $(srctree)/.config -o -d $(srctree)/include/config ]; then /
970 echo " $(srctree) is not clean, please run 'make mrproper'";/
971 echo " in the '$(srctree)' directory.";/
972 /bin/false; /
973 fi;
974 endif
没有问题,不会去执行的,因为$(KBUILD_SRC)是空的。所以来到977行:
977 prepare2: prepare3 outputmakefile
我们看到prepare2什么命令也不执行,但它除了prepare3还有个依赖outputmakefile:
396 outputmakefile:
397 ifneq ($(KBUILD_SRC),)
398 $(Q)ln -fsn $(srctree) source
399 $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile /
400 $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
401 endif
同样也不会执行这个命令。那么就来到了979行:
979 prepare1: prepare2 include/linux/version.h include/generated/utsrelease.h /
980 include/config/auto.conf
981 $(cmd_crmodverdir)
看到它的依赖:
1013 include/linux/version.h: $(srctree)/Makefile FORCE
1014 $(call filechk,version.h)
有个FORCE,所以尽管include/linux/version.h是存在的,但也要执行1014的命令。我们只是学习学习新知识,1014行代码,函数call。call 关键字是唯一一个可以用来创建新的参数化的函数。你可以写一个非常复杂的表达式,这个表达式中,你可以定义许多参数,然后你可以用call 函数来向这个表达式传递参数。其语法是:
$(call <expression>,<parm1>,<parm2>,<parm3>...)
当 make 执行这个函数时,<expression>参数中的变量,如$(1),$(2),$(3)等,会被参数<parm1>,<parm2>,<parm3>依次取代。而<expression>的返回值就是call 函数的返回值。
例如:
reverse = $(1) $(2)
foo = $(call reverse,a,b)
那么,foo 的值就是“a b”。当然,参数的次序是可以自定义的,不一定是顺序的,如:
reverse = $(2) $(1)
foo = $(call reverse,a,b)
此时的 foo 的值就是“b a”。
所以我们猜到,1014行的代码是让filechk变量等于变成另一个字符串。那么,filechk变量在哪儿定义的呢?顶层Makefile找不到,那么在/scripts/Kbuild.include中找,49行:
define filechk
$(Q)set -e; /
$(kecho) ' CHK $@'; /
mkdir -p $(dir $@); /
$(filechk_$(1)) < $< > [email protected]; /
if [ -r $@ ] && cmp -s $@ [email protected]; then /
rm -f [email protected]; /
else /
$(kecho) ' UPD $@'; /
mv -f [email protected] $@; /
fi
endef
调用call以后,filechk变量就会变成:
$(Q)set -e; /
$(kecho) ' CHK $@'; /
mkdir -p $(dir $@); /
$(filechk_ version.h) < $< > [email protected]; /
if [ -r $@ ] && cmp -s $@ [email protected]; then /
rm -f [email protected]; /
else /
$(kecho) ' UPD $@'; /
mv -f [email protected] $@; /
fi
其中的$@是include/linux/version.h,所以我们看到make之后屏幕输出的第一行肯定是:
CHK include/linux/version.h
第三个依赖,include/generated/utsrelease.h,来自1016行:
1016 include/generated/utsrelease.h: include/config/kernel.release FORCE
1017 $(call filechk,utsrelease.h)
跟第二个依赖一样,所以不去管它了。
最后一个依赖include/config/auto.conf,make menucofig生成的,所以就执行prepare1的命令了:
cmd_crmodverdir = $(Q)mkdir -p $(MODVERDIR) /
$(if $(KBUILD_MODULES),; rm -f $(MODVERDIR)/*)
这个命令我没看懂是什么,只知道在373行有个$(MODVERDIR)变量的定义:
export MODVERDIR := /
$(if $(KBUILD_EXTMOD),$(firstword $(KBUILD_EXTMOD))/).tmp_versions
不管怎样,这个文件夹终究会被删除的,不去详细分析了。那么prepare1也结束了,这时候才来到了archprepare的第二个依赖,scripts_basic。有关scripts_basic我们在编译配置里已经详细谈过了,其结果是在scripts/basic/生成三个c程序:fixdep、docproc、hash。具体是怎么得到他们的请参照前面讲解的KBuild体系去查看scripts/basic/Makefile文件。至于他们是干什么的,以后用到了再研究。
scripts_basic目标结束后,archprepare因为没有附带任何命令,也结束了。所以来到985行的prepare0:
prepare0: archprepare FORCE
$(Q)$(MAKE) $(build)=.
$(Q)$(MAKE) $(build)=. missing-syscalls
翻译一下:
@make -f scripts/Makefile.build obj=.
@make -f scripts/Makefile.build obj=. missing-syscalls
这里是将顶层目录,也就是/usr/src/kernels/linux-2.6.34.1赋值给obj。当然,顶层Makefile本身也是个KBuild的参数,所以会在屏幕上打印以下一些信息:
CC kernel/bounds.s
GEN include/generated/bounds.h
CC arch/x86/kernel/asm-offsets.s
GEN include/generated/asm-offsets.h
CALL scripts/checksyscalls.sh
至于为啥,我也没弄懂,还要请教广大网友帮我研究研究。prepare0完毕以后,prepare也就完成了,$(vmlinux-dirs)的第一个依赖也就搞完了。一路走来还是很艰辛的,这就是内核!来看第二个依赖,scripts,在473行:
473 scripts: scripts_basic include/config/auto.conf include/config/tristate.conf
474 $(Q)$(MAKE) $(build)=$(@)
scripts_basic目标已经执行过了,include/config/tristate.conf文件也存在,所以直接执行474行的命令:
@make -f scripts/Makefile.build obj= scripts
我们就去看scripts目录下的Makefile文件。具体内容我就不列出来了,主要是理清思路:先是subdir-y,有subdir-$(CONFIG_MODVERSIONS) += genksyms等内容,那么就到genksyms等等这些子目录去执行他们的Makefile,执行完后,再根据hostprogs-y的内容生成主机程序。
hostprogs-$(CONFIG_XXX)是去根据CONFIG_XXX标志主机程序,再去形成这些主机程序,也就是说直接去生成c程序而不是形成.o以制作built-in.o。具体的原理请去查阅前面有关KBuild体系主机程序的内容。
scripts目标执行完后,整个前期准备工作就算完成了。后面就是更加艰难的形成vmlinux工作。