在编译LINUX内核时,首先要修改内核源码顶层目录下的makefile文件,将其中ARCH ?= $(SUBARCH)修改为ARCH ?= arm,将CROSS_COMPILE ?= 修改为CROSS_COMPILE ?= arm-linux-gcc,或者不修改,而是将ARCH和CROSS_COMPILE的值通过命令行传入。然后在linux内核源码目录下,执行make menuconfig,那之后发生了什么?
make命令在未指定文件的情况下,默认寻找名为Makefile或GNUMakefile的文件(文件名不区分大小写,无后缀名)。make menuconfig命令没有指定文件,因此默认执行的是 make –f Makefile menuconfig,即执行$(srctree)/Makefile文件中目标menuconfig的相关规则。我使用的源码顶层目录名为linux2.6.30.4,因此$(srctree)/Makefile即linux2.6.30.4/Makefile。
下边只列出了执行make menuconfig后,linux2.6.30.4/Makefile文件中与该命令执行相关的目标规则(此处将makefile文件中include的文件也包含进去并展开了,在include关键字下边,都用{ }括起来,用来表示include进来的文件展开后的内容):
linux2.6.30.4/Makefile
... ...
# Use make M=dir to specify directory of external module to build # Old syntax make ... SUBDIRS=$PWD is still supported # Setting the environment variable KBUILD_EXTMOD take PRecedence ifdef SUBDIRS KBUILD_EXTMOD ?= $(SUBDIRS) endif ifdef M ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M) endif endif
ifeq ($(KBUILD_SRC),)
ifdef O ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O) endif endif
… …
ifneq ($(KBUILD_OUTPUT),)
… …
sub-make: FORCE $(if $(KBUILD_VERBOSE:1=),@)$(MAKE) -C $(KBUILD_OUTPUT) / KBUILD_SRC=$(CURDIR) / KBUILD_EXTMOD="$(KBUILD_EXTMOD)" -f $(CURDIR)/Makefile / $(filter-out _all sub-make,$(MAKECMDGOALS))
… …
skip-makefile := 1
endif # ifneq ($(KBUILD_OUTPUT),) endif # ifeq ($(KBUILD_SRC),)
NOTE: 因为make menuconfig命令没有定义M、O、也没有定义skip-makefile,且没有SUBDIRS、KBUILD_EXTMOD、KBUILD_SRC这三个环境变量(除非自己去设置,否则linux系统不会有这三个个环境变量),因此KBUILD_OUTPUT为空,skip-makefile也为空,KBUILD_EXTMOD为空,KBUILD_SRC为空。
ifeq ($(skip-makefile),)
… …
srctree := $(if $(KBUILD_SRC),$(KBUILD_SRC),$(CURDIR))
NOTE: KBUILD_SRC为空,所以if函数的返回值是$(CURDIR),CURDIR是make的内嵌变量,其值是make命令执行所在目录,对于我这个例子,是进入到D:/linux2.6.30.4后make menuconfig的,因此CURDIR就是D:/linux2.6.30.4
… …
SRCARCH := $(ARCH)
… …
include $(srctree)/scripts/Kbuild.include
{
… …
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
NOTE:KBUILD_SRC为空,所以$(srctree)/的值不被返回,即上句实际为build := -f scripts/Makefile.build obj,
… …
}
… …
PHONY += scripts_basic scripts_basic: $(Q)$(MAKE) $(build)=scripts/basic
… …
PHONY += outputmakefile outputmakefile: ifneq ($(KBUILD_SRC),) $(Q)ln -fsn $(srctree) source $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile / $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL) endif
… …
config-targets := 0 mixed-targets := 0
... ...
ifeq ($(KBUILD_EXTMOD),) ifneq ($(filter config %config,$(MAKECMDGOALS)),) config-targets := 1 ifneq ($(filter-out config %config,$(MAKECMDGOALS)),) mixed-targets := 1 endif endif endif
NOTE: MAKECMDGOALS变量指的是从make命令行参数中传递过来的目标字符串,make menuconfig命令对应的MAKECMDGOALS就是menuconfig。KBUILD_EXTMOD为空,MAKECMDGOALS字串与%config模式符合,故config-targets := 1,而MAKECMDGOALS中除了%config模式外,没有其他模式的字串了,因此$(filter-out config %config,$(MAKECMDGOALS))为空,故mixed-targets仍然为0
ifeq ($(mixed-targets),1)
… …
else
ifeq ($(config-targets),1)
… …
%config: scripts_basic outputmakefile FORCE $(Q)mkdir -p include/linux include/config $(Q)$(MAKE) $(build)=scripts/kconfig $@
else
… …
endif #ifeq ($(config-targets),1) endif #ifeq ($(mixed-targets),1)
… …
endif # skip-makefile
… …
PHONY += FORCE FORCE:
.PHONY: $(PHONY)
NOTE: make menuconfig后,经过一些逻辑判断(就是前面ifeq ($(config-targets),1)之类的),最终来执行%config目标的规则,%config目标的规则中有两条命令,第一条创建两个目录,第二条执行make命令,Q的值被定义为@或者为空,而@……的意思是不将此行规则在执行时显示在屏幕上,因此$(Q)无关紧要。MAKE是内嵌变量,其值为make,$@表示当前目标,即menuconfig,故第二条规则实际就是make -f scripts/Makefile.build obj=scripts/kconfig menuconfig,进入到scripts/makefile.build文件中,make的目标是menuconfg。menuconfg目标有三个依赖scripts_basic outputmakefile FORCE 。scripts_basic目标的规则的命令为$(Q)$(MAKE) $(build)=scripts/basic,展开即make -f scripts/Makefile.build obj=scripts/basic。查看Makefile.build文件,可以看到,该文件的默认目标是__build,__build目标有两条规则,第一条是空规则,第二条规则有命令。按照makefile规则,当一个目标有多条规则时,只能有一条规则有生成目标的命令,多条规则的中的命令和依赖在makefile文件被读取时合并,因此说,这里__build目标的实质性规则是
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) / $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) / $(subdir-ym) $(always) @:
KBUILD_BUILTIN、KBUILD_MODULES在顶层makefile文件中定义,并通过export关键字定义,使在makefile递归进行时,这两个变量被传递进子makefile。KBUILD_BUILTIN和KBUILD_MODULES在顶层makefile文件中定义赋为1后,就没有被改变过。所以此处__build目标的依赖就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)。命令“:”在bash中表示什么都不干,只是单纯的返回true. 经过分析,发现builtin-target、lib-target、extra-y、subdir-ym都为空串,只有always有值,always在scripts/kconfig/Makefile中定义为dochecklxdialog,而dochecklxdialog目标所在规则的注释写着# Check that we have the required ncurses stuff installed for lxdialog (menuconfig),也就是说,__build目标的依赖dochecklxdialog是用来检查生成配置对话框所需的ncurses库是不是已经安装在本机了,如果没有安装,make过程会报错退出。因此在make menuconfig前,我们要保证该库已经被安装在本地。
outputmakefile 目标所在规则实际上什么也不做。FORCE所在规则为空,也是什么都不做。FORCE被定义为一个伪目标,所以它作为依赖时总是被认为是最新的(比目标新),故有FORCE作为依赖的目标每次make时必然会重新生成,在这里FORCE伪目标的规则命令为空,故FORCE在Kbuild体系中,就是相当于是一个关键字,如果我们想要某个目标每次make的时候都一定会被重新生成,就把FORCE写为该目标的依赖。
linux2.6.30.4/scripts/Makefile.build
src := $(obj)
PHONY := __build __build:
… …
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file)
{
… …
ifdef KBUILD_KCONFIG Kconfig := $(KBUILD_KCONFIG) else Kconfig := arch/$(SRCARCH)/Kconfig endif
… …
menuconfig: $(obj)/mconf $< $(Kconfig)
… …
NOTE: 由make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值为scripts/kconfig,与/%的字串模式相符,因此$(filter /%,$(src))就是scripts/kconfig,故kbuild-dir就被赋值为$(src),即kbuild-dir为scripts/kconfig。由于scripts/kconfig目录下并没有Kbuild文件,因此函数$(wildcard $(kbuild-dir)/Kbuild)查找失败,返回为空,从而kbuild-file值被赋为$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着include $(kbuild-file),目标menuconfig就被找到了。menuconfig目标的规则的命令是$< $(Kconfig),展开为$(obj)/mconf $(Kconfig), obj的值为scripts/kconfig,因为没有定义KBUILD_KCONFIG,而且SRCARCH之前已被赋值为$(ARCH),即SRCARCH为arm,因此Kconfig的值为arch/arm/Kconfig。故menuconfig目标的规则的命令为scripts/kconfig/mconf arch/arm/Kconfig。mconf在这里实际上是scripts/kconfig目录下的一个可执行文件,此条命令里arch/arm/Kconfig字符串作为命令行参数传入该可执行文件运行,该可执行文件实际上就是依据arch/arm/Kconfig文件提供的菜单配置,生成配置界面。NOTE: 这里为什么说scripts/kconfig/mconf就是一个可执行文件呢?继续往下看scripts/kconfig/Makefile中的内容:
lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o
… …
mconf-objs := mconf.o zconf.tab.o $(lxdialog)
… …
ifeq ($(MAKECMDGOALS),menuconfig) hostprogs-y += mconf endif
… …
# Check that we have the required ncurses stuff installed for lxdialog (menuconfig) PHONY += $(obj)/dochecklxdialog $(addprefix $(obj)/,$(lxdialog)): $(obj)/dochecklxdialog $(obj)/dochecklxdialog: $(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOST_LOADLIBES)
always := dochecklxdialog
… …
} NOTE: 如果在编译内核的过程中,需要现编译出一些可执行文件供内核编译阶段使用,就需要借助Kbuild框架的本机程序支持的特性。Kbuild 框架中,专门使用hostprogs-y变量来指示在内核编译阶段需要使用的一些可执行文件,通过hostprogs-y += mconf,就向make程序指明mconf是一个编译阶段需要使用的可执行文件。另外,Kbuild框架使用-objs后缀来指明相应的可执行文件需要通过多个目标文件来链接生成,mconf-objs := mconf.o zconf.tab.o $(lxdialog)就是向make指明,mconf文件是由mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o链接生成的。再有,未明确写明生成规则时,KBuild框架默认.o文件是由同名.c或.S文件编译生成的。我们在scripts/kconfig以及scripts/kconfig/lxdialog目录下可以找到前边8个.o文件的同名.c文件。
新闻热点
疑难解答