读书频道 > 系统 > linux > 深度探索Linux操作系统系统构建和原理解析
3.2.3 vmlinux的构建过程
2013-09-28 16:02:27     我来说两句 
收藏    我要投稿   
全书一共8章:第1章介绍了如何准备工作环境。在第2章中构建了编译工具链,这是后面构建操作系统各个组件的基础。在这一章中,不仅详细讲解了工具链的构建过程,而且还通过对编译链接过程的探讨,深入讨论了工具链  立即去当当网订购

所有的体系结构都需要构建vmlinux,所以vmlinux的构建规则在顶层的Makefile中。
linux-3.7.4/Makefile:
cmd_link-vmlinux = $(CONFIG_SHELL) $< $(LD) $(LDFLAGS) \
                    $(LDFLAGS_vmlinux)

vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
    +$(call if_changed,link-vmlinux)

注意,构建vmlinux的命令使用了make的内置函数call。这是一个比较特殊的内置函数,make使用它来引用用户自己定义的带有参数的函数。if_changed是kbuild定义的一个函数,这里通过call引用这个函数,传递的实参是link-vmlinux。函数if_changed的定义如下:
linux-3.7.4/scripts/Kbuild.include:
if_changed = $(if $(strip $(any-prereq) $(arg-check)), \
    @set -e;                                                     \
    $(echo-cmd) $(cmd_$(1));                                  \
    echo ‘cmd_$@ := $(make-cmd)’ > $(dot-target).cmd)

在if_changed中,any-prereq检查是否有依赖比目标新,或者依赖还没有创建;arg-check检查编译目标的命令相对上次是否发生变化。如果两者中只要有一个发生改变,就执行if函数的if块。注意if块中的使用黑体标识的部分,其中“1”代表的就是传给if_changed的第一个实参。由此可见,if_changed核心功能就是当目标的依赖或者编译命令发生变化时,执行表达式“cmd_$(1)”展开后的值。

这里,传给if_changed的第一个实参是link-vmlinux,因此,cmd_$(1)展开后为cmd_link-vmlinux。注意cmd_link-vmlinux中的第二项“$<”,这是make的自动变量,翻译自“Automatic Variable”,意指变量名相同,但是make根据具体上下文,将其自动替换为合适的值。这里,make会将这个自动变量替换为构建vmlinux中的规则中的第一个依赖,即shell脚本文件scripts/link-vmlinux.sh,该脚本文件中负责vmlinux链接的脚本如下:
linux-3.7.4/scripts/link-vmlinux.sh:
vmlinux_link()
{
    local lds="${objtree}/${KBUILD_LDS}"

    if [ "${SRCARCH}" != "um" ]; then
        ${LD} ${LDFLAGS} ${LDFLAGS_vmlinux} -o ${2}                  \
            -T ${lds} ${KBUILD_VMLINUX_INIT}                     \
            --start-group ${KBUILD_VMLINUX_MAIN} --end-group ${1}
    else
        ...
    fi
}
...
vmlinux_link "${kallsymso}" vmlinux

根据函数vmlinux_link的实现,如果平台不是“um”,那么就调用链接器将变量KBUILD_ VMLINUX_INIT、KBUILD_VMLINUX_MAIN中记录的目标文件链接为vmlinux。我们看看这两个变量的定义:
linux-3.7.4/Makefile:
# Externally visible symbols (used by link-vmlinux.sh)
export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y)\
   $(net-y)

我们以core-y为例来分析变量KBUILD_VMLINUX_MAIN的值。
linux-3.7.4/Makefile:

core-y      := usr/
...
core-y      += kernel/ mm/ fs/ ipc/ security/ crypto/ block/
...
core-y      := $(patsubst %/, %/built-in.o, $(core-y))

patsubst是make的内置函数,功能是在输入的文本中查找与模式匹配的字符串,然后使用特定字符串进行替换。 具体到这里,其目的就是在变量core-y的值中将字符串“/”替换为“/built-in.o”。经过函数patsubst替换后,最后变量core-y的值如下:
core-y := user/built-in.o kernel/built-in.o mm/built-in.o fs/built-in.o \
      ipc/built-in.o security/built-in.o crypto/built-in.o \
      block/built-in.o

除了各个子目录下的built-in.o,有些子目录(如lib)下还会编译lib.a。总之,vmlinux就是由这些目录下的built-in.o、lib.a等链接而成的。

那么这些子目录下面的目标文件built-in.o或者lib.a是在什么时机构建的呢?我们来回顾一下vmlinux的构建规则;
linux-3.7.4/Makefile:
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE

我们看到,除了依赖scripts/link-vmlinux.sh,vmlinux的另外一个依赖是vmlinux-deps,其构建规则也在顶层Makefile中定义:
linux-3.7.4/Makefile:
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-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT)
                 $(KBUILD_VMLINUX_MAIN)
...
$(sort $(vmlinux-deps)): $(vmlinux-dirs) ;
...
$(vmlinux-dirs): prepare scripts
    $(Q)$(MAKE) $(build)=$@

我们首先看看变量vmlinux-deps,显然,其记录的就是我们前面讨论的最终链接为vmlinux的内核子目录下的目标文件的名字,如built-in.o等。也就说,vmlinux的构建规则表达得很清楚,要最后链接vmlinux,首先需要构建这些目标文件。但是注意目标vmlinux-deps的构建规则,其“规则体”是空的,也就是说这个构建规则下没有任何命令可执行,但是可以看到这些目标文件依赖于另外一个目标vmlinux-dirs,我们继续跟踪目标vmlinux-dirs的构建。

我们来关注一下变量vmlinux-dirs的值。注意该变量的赋值脚本,其中函数filter也是make的内置函数,其功能是过滤掉输入文本中不以“/”结尾的字符串。前面我们看到,输入到filter的这些变量,比如core-y,其中所有的子目录都以“/”结尾,因此,这里filter的目的是过滤掉这些变量中的非目录。patsubst这个make的内置函数我们刚刚讨论过,显然是将过滤出来的子目录后面的字符“/”去掉。因此,正如其名字所揭示的,变量vmlinux-dirs的值是多个目录,所以构建vmlinux-dirs的规则也是一个多目标规则,等价于:
init: prepare scripts
    $(Q)$(MAKE) $(build)=$@
kernel: prepare scripts
    $(Q)$(MAKE) $(build)=$@
...

规则中的命令展开后为:
make -f script/Makefile.build obj=$@

其中“$@”是make的自动变量,表示规则的目标,所以这里会被make自动替换为构建的子目录,如init、kernel等,即相当于逐个编译这些子目录,使用的Makefile是Makefile.build。如3.2.1节讨论的那样,Makefile.build将包含构建目录中的Makefile或Kbuild,最终形成完整地Makefile。make命令中没有显式指定构建目标,因此,将构建Makefile.build中默认的目标。Makefile.build中的默认目标是__build,脚本如下所示:
linux-3.7.4/scripts/Makefile.build:
src := $(obj)
PHONY := __build
__build:
...
__build: $(if $(KBUILD_BUILTIN),$(builtin-target) \
        $(lib-target) $(extra-y)) \
        $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
        $(subdir-ym) $(always)

目标__build涵盖了内核映像和模块,这里我们只关注内核映像的构建,不关注模块的构建。对于编译内核映像来说,目标__build依赖builtin-target、lib-target、extra-y、subdir-ym和always。我们先来看builtin-target和lib-target:
linux-3.7.4/scripts/Makefile.build:
ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
lib-target := $(obj)/lib.a
endif
ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) \
    $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif

根据上述脚本片断可见,builtin-target代表的就是子目录下的built-in.o,lib-target代表的就是子目录下的lib.a。对于构建的子目录,如果变量obj-y等值非空,那么就构建built-in.o。如果变量lib-y等的值非空,那么就构建lib.a。我们来看看built-in.o和lib.a的构建:
linux-3.7.4/scripts/Makefile.build:

cmd_link_o_target = $(if $(strip $(obj-y)),\
            $(LD) $(ld_flags) -r -o $@ $(filter $(obj-y), $^) \
            $(cmd_secanalysis),\
            rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@)
$(builtin-target): $(obj-y) FORCE
    $(call if_changed,link_o_target)
...
cmd_link_l_target = rm -f $@; $(AR) rcs$(KBUILD_ARFLAGS) $@ $(lib-y)
$(lib-target): $(lib-y) FORCE
    $(call if_changed,link_l_target)

前面已经讨论过函数if_changed,如果理解了这个函数,就很容易理解built-in.o和lib.a的构建过程了,对于built-in.o,就是调用链接器将变量obj-y中记录的各个目标文件链接为built-in.o。对于lib-target,就是调用创建静态库的程序AR将变量lib-y中的各个目标文件链接为lib.a。

编译内核时需要一些临时工具,比如我们前面用到的mkpiggy、build等,这些是一定需要编译的,因为构建内核时会用到。因此,kbuild中定义了一个变量always,其中记录的就是必须要编译的构建目标。

另外,可能有多层目录嵌套的情况,因此__build依赖列表中有这么一项:subdir-ym。目标subdir-ym的规则如下:
linux-3.7.4/scripts/Makefile.build:
$(subdir-ym):
    $(Q)$(MAKE) $(build)=$@

上面的代码看上去是不是很熟悉?没错,它和前面处理vmlinux-dirs的规则完全相同,显然,这是在处理目录中还有子目录的情况。

至此,链接vmlinux的目标文件经构建完成。回顾一下vmlinux的构建过程,kbuild将依次构建Makefile中指定的子目录,生成builtin.o、lib.a等目标文件,然后调用链接器将这些目标文件链接为vmlinux,并保存在顶层目录下。

点击复制链接 与好友分享!回本站首页
分享到: 更多
您对本文章有什么意见或着疑问吗?请到论坛讨论您的关注和建议是我们前行的参考和动力  
上一篇:3.2.2 构建过程概述
下一篇:3.2.4 vmlinux.bin的构建过程
相关文章
图文推荐
3.3.6 GNOME的软件管
3.3.5 GNOME的文件管
3.3.4 GNOME的窗口管
3.3.3 收藏夹和快捷
排行
热门
文章
下载
读书

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训
版权所有: 红黑联盟--致力于做最好的IT技术学习网站