闲置了这么久的博客,也要拾起来了(为自己的懒惰感到羞愧)。最近一段时间一直和linux的核心代码打交道,也积累了一些经验,现在想把自己对linux的理解写下来,也欢迎朋友指正。
我认为对linux代码的阅读首先要读懂她的Makefile,Makefile是代码的外部框架,熟悉她会让我们对Linux的代码层次有一定的了解,这对我们深读代码很有帮助。工作过程中接手一个新项目的维护工作时,我也会首先读她的Makefile,因为这在一定程序上代表了项目开发者的架构思路,这也是我要学习的地方。
好了,现在我们开始我们的代码之旅。。。
编译内核首先要执行make menuconfig,那我们就从这条命令开始。(P.S. 内核版本 3.8.0)
1. Makefile变量的初始化
KBUILD_EXTMOD 为空,因为命令行中没有”M= xxx“,KBUILD_SRC为空,因为没有“O = xxx“。
no-dot-config-targets := clean mrproper distclean \
cscope gtags TAGS tags help %docs check% coccicheck \
$(version_h) headers_% archheaders archscripts \
no-dot-config-targets kernelversion %src-pkg
config-targets := 0
mixed-targets := 0
dot-config := 1
ifneq ($(filter $(no-dot-config-targets), $(MAKECMDGOALS)),)
ifeq ($(filter-out $(no-dot-config-targets), $(MAKECMDGOALS)),)
# MAKECMDGOALS 中仅包含no-dot-config-targets提供的命令
dot-config := 0
endif
endif
ifeq ($(KBUILD_EXTMOD),)
ifneq ($(filter config %config,$(MAKECMDGOALS)),)
# MAKECMDGOALS 包含config
config-targets := 1
ifneq ($(filter-out config %config,$(MAKECMDGOALS)),)
# MAKECMDGOALS 还包含其他命令
mixed-targets := 1
endif
endif
由上面代码可以得出如下几个变量的值:
config-targets = 1
mixed-targets = 0
dot-config = 1
2. 确定执行规则
根据前面得到的几个变量,那么就可以确定执行Makefile line487~505的代码了。
include $(srctree)/arch/$(SRCARCH)/Makefile
export KBUILD_DEFCONFIG KBUILD_KCONFIG
config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@
我们要先执行依赖规则 scripts_basic 和 outputmakefile,而后执行target。查找代码可以确认依赖规则如下:
# Makefile line 418
scripts_basic:
$(Q)$(MAKE) $(build)=scripts/basic
$(Q)rm -f .tmp_quiet_recordmcount
# Makefile line 429
outputmakefile:
ifneq ($(KBUILD_SRC),) # 不执行
$(Q)ln -fsn $(srctree) source
$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif
3. 执行依赖规则
-
scripts_basic
$(Q)$(MAKE) $(build)=scripts/basic
build定义在scripts/Kbuild.include中,含义如下build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj
那么这行代码展开即是make -f scripts/Makefile.build obj=scripts/basic
,跳到scripts/Makefile.build中执行,条件是obj=scripts/basic那我们把目光转向scripts/Makefile.build
src := $(obj) kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src)) kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile) include $(kbuild-file)
查找scripts/basic目录下所有的Kbuild或者Makefile,include 它们,然后编译其中的小模块,在这里编译出可执行程序fixdep.
outputmakefile
无执行代码。
4. 执行目标规则
$(Q)mkdir -p include/linux include/config
# 创建目录$(Q)$(MAKE) $(build)=scripts/kconfig $@
命令展开为make -f scripts/Makefile.build obj=scripts/kconfig menuconfig
跳到scripts/Makefile.build中执行,条件是obj=scripts/kconfig,目标是menuconfig。
继续解析,执行执行scripts/kconfig中的Makefile,目标规则menuconfig。menuconfig: $(obj)/mconf
$< $(Kconfig)其中$(obj)/mconf的创建是Kernel Makefile中最基础也是非常精彩的部分,linux中大部分子模块均是按照这样的方式组织的。
在scripts/kconfig/Makefile中有如下定义:
mconf-objs := mconf.o zconf.tab.o $(lxdialog)
lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o
lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o
hostprogs-y += mconf
注意这里的mconf-objs和hostprogs-y
进入scripts/Makefile.host
__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m))
host-cmulti := $(foreach m,$(__hostprogs),\
$(if $($(m)-cxxobjs),,$(if $($(m)-objs),$(m))))
host-cobjs := $(sort $(foreach m,$(__hostprogs),$($(m)-objs)))
__hostprogs := $(addprefix $(obj)/,$(__hostprogs))
host-cmulti := $(addprefix $(obj)/,$(host-cmulti))
host-cobjs := $(addprefix $(obj)/,$(host-cobjs))
这里
__hostprogs := scripts/kconfig/mconf
host-cmulti := scripts/kconfig/mconf
host-cobjs := mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o(不用关心这些.o文件是什么,我们只关心makefile的执行流程,这里在每个前面要加上前缀$(obj),即”scripts/kconfig/”)通过如下代码编译成可执行文件
cmd_host-cmulti = $(HOSTCC) $(HOSTLDFLAGS) -o $@ \
$(addprefix $(obj)/,$($(@F)-objs)) \
$(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
$(host-cmulti): $(obj)/%: $(host-cobjs) $(host-cshlib) FORCE
$(call if_changed,host-cmulti)
上面第二个规则匹配目标mconf,而$(host-cobjs)的匹配规则如下
cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $<
$(host-cobjs): $(obj)/%.o: $(src)/%.c FORCE
$(call if_changed_dep,host-cobjs)
实质上是将.c 文件编译成 .o文件,而后调用cmd_host-cobjs生成$(host-cobjs)
各个目标文件。返回$(host-cmulti)
规则,调用cmd_host-cmulti生成最终的可执行文件mconf,叹为观止啊。最后返回最初的menuconfig规则,现在只剩下最后一条命令:
$< $(Kconfig)
,展开为scripts/Kconfig/mconf Kconfig,这是一条shell命令,使用刚编出来的可执行文件mconf来完成make menuconfig的最后步骤,即我们看到的图形化内核配置菜单,至此全部完成。
后面介绍Linux Makefile的其他编译目标,上文中如有不正确的部分欢迎指正。