现在的位置: 首页 > 综合 > 正文

内核映像的形成——编译配置

2013年09月04日 ⁄ 综合 ⁄ 共 4596字 ⁄ 字号 评论关闭

2.2 内核编译分析

关于Kbuild的预备知识先说到这里,有了上面的这些知识大家就可以上手了,其他的那些重要的知识主要涉及到一些细节,等我们遇到了再去查GNU Make手册和linux-2.6.34.1/Documentation/kbuild下的那些文档(注意,这是一种很重要的学习方法)。

 

在递归访问目录之前,顶层Makefile要完成设置环境变量以及递归访问的准备工作。顶层Makefile包含的公共部分,而 arch/$(ARCH)/Makefile 包含着针对某一特定CPU架构的配置信息。所以,要在arch/$(ARCH)/Makefile 中设置一部分变量,并定义一些目标。

 

Kbuild执行的几个步骤(大致)

1) 根据内核配置生成文件 .config

2) 将内核的版本号存储在 include/linux/version.h

3) 生成指向 include/asm-$(ARCH) 的符号链接

4) 更新所有编译所需的文件: 附加的文件由 arch/$(ARCH)/Makefile 指定。

5) 递归向下访问所有在下列变量中列出的目录: init-* core* drivers-* net-* libs-*,并编译生成目标文件。这些变量的值可以在 arch/$(ARCH)/Makefile 中扩充。

6) 联接所有的目标文件,在源代码树顶层目录中生成 vmlinux。最先联接是在 head-y中列出的文件,该变量由 arch/$(ARCH)/Makefile 赋值。

7) 最后完成具体架构的特殊要求,并压缩vmlinux生成最终的内核镜像。

-- 包含生成启动指令

-- 准备 initrd 镜像或类似文件

 

2.2.1 编译配置

那么针对内核,我们从哪里下手呢?我们是在内核源代码最高目录linux-2.6.34.1上运行的make menuconfig,那么就从KBuild的顶层——linux-2.6.34.1/Makefile文件下手。

 

在这个文件找“menuconfig:”这一行。可是找不到怎么办,难道我们的思路出了问题?大家在学习代码的时候,如果一条路走不通了,可千万别急,有百度呢。查一查,GNU make手册,Makefile跟我们的C语言一样,可以include呢。也就是说,可以把其他Makefile嵌入进来。所以,我们再在该文件下搜一搜所以的include关键字,有这么几行:

 

309 include $(srctree)/scripts/Kbuild.include

452 include $(srctree)/arch/$(SRCARCH)/Makefile

486 -include include/config/auto.conf

491 -include include/config/auto.conf.cmd

535 include $(srctree)/arch/$(SRCARCH)/Makefile

 

注意到srctree宏,我们可以猜到就是当前目录,SRCARCHx86,再加上我们只关注Makefile文件,所以我们就只关注/usr/src/kernels/linux-2.6.34.1/arch/x86/Makefile。搜一下,还是没有。这下我怀疑真的是我的思路错了。别着急,继续百度。终于在一个论坛上查到了,linux-2.6.34.1/Makefile文件的459行有段这样的代码:

459 %config: scripts_basic outputmakefile FORCE

460     $(Q)mkdir -p include/linux include/config

461     $(Q)$(MAKE) $(build)=scripts/kconfig $@

 

%”就是前面讲到的模式规则,这里就用到了。所有以config结尾的目标(如:menuconfig xconfig gconfig)都采用这个规则。另外,$(Q)286行定义,它根据KBUILD_VERBOSE是否设置而定义,如果设置就是空的,没有设置就是“@”。$(Q)$(MAKE)这两个宏组合在一起,就是@make,我们知道,就是不把命令详细信息打印出来。所以make menuconfig最终会运行@make $(build)=scripts/kconfig menuconfig命令。根据前面的知识,$@就是指目标menuconfig。至于后面的$(build)变量是什么?待会儿再讲。

 

这个规则有三个依赖:scripts_basicoutputmakefileFORCE。下面看一下这三个依赖:

我们从最简单的开始,首先分析一下这个FORCE依赖,它的规则定式义在1553行:

FORCE:

 

这个规则没有命令也没有依赖,只做一个.PHONY: $(PHONY)动作。这里要回顾一下GUN make的隐含规则。我们知道,make 的“隐含规则”功能会自动为我们自动去推导目标的依赖目标和生成命令。如果推导出来的目标(是什么,不知道,make的那么多隐含规则,谁也说不清楚)与伪目标(如%config)同名,也就是已经存在于系统中了,那么伪目标中的命令就不会被执行。而执行FORCE这个依赖的作用就是在执行此规则时,目标FORCE总会被认为是最新的。这样当它作为其它规则的依赖时,因为依赖总被认为被更新过的,所以那个%config目标中定义的命令总会被执行。其实我们也可以从force这个单词的中文意思中猜测(分析内核就需要这种连蒙带猜的精神)。

 

再来看看scripts_basic这个依赖的规则在384行定义:

PHONY += scripts_basic

scripts_basic:

       $(Q)$(MAKE) $(build)=scripts/basic

       $(Q)rm -f .tmp_quiet_recordmcount

 

注意,PHONY这个变量是我们第一次见到,在内核源码中的Makefile随处可见,它的作用在最后一行定义:.PHONY: $(PHONY),表示PHONY变量中所有的目标都是伪目标,而不是具体的文件,所以,这里的scripts_basic就是个伪目标。

 

下面来讲build这个变量,它定义在我们刚才includescripts/kbuild.include114行:

build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

 

KBUILD_SRC是当前目录,后面会讲到,所以srctree参数也是当前目录,所以省去,上面的规则可写成如下形式:

scripts_basic:

    @make –f scripts/Makefile.build obj=scripts/basic

 

这个规则的命令最终会进入scripts目录,执行Makefile.build文件(传说中的Kbuild脚本),并传递参数obj=scripts/basic

 

最后再来看outputmakefile,参数构建规则在396行开始定义:

outputmakefile:

ifneq ($(KBUILD_SRC),)

       $(Q)ln -fsn $(srctree) source

       $(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile /

           $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif

 

这个规则的命令运行一个shell脚本scripts/mkmakefile,并传递四个参数。这个脚本主要是在$(objtree)参数指定的目录中生成一个Makefile文件。由于这里KBUILD_SRC为空,所以这个脚本并不会被执行。

 

在他的依赖被处理完后,开始执行规则的命令。第一个命令创建了两个目录,第二个命令扩展后为:

@make  –f  scripts/Makefile.build  obj=scripts/kconfig  menuconfig

 

这个命令依然是执行scripts/Makefile.build这个makefile文件。并传递参数obj=scripts/kconfigmenuconfig

 

scripts/Makefile.build脚本的具体代码我就不去分析了,但必须知道上面命令的原理,这是KBuild机制的重中之重。前办部分是@make –f scripts/Makefile.build,这整个部分就是KBuild的主题命令名称;obj=scripts/kconfig表示对应目录,KBuild只关心该目录中的Makefile文件;menuconfig是传进去的参数,意思是执行Makefile里面menuconfig的目标,我们看到在 scripts/kconfig/Makefile20行定义:

8 ifdef KBUILD_KCONFIG

9 Kconfig := $(KBUILD_KCONFIG)

10 else

11 Kconfig := arch/$(SRCARCH)/Kconfig

12 endif

20 menuconfig: $(obj)/mconf

21    $< $(Kconfig)

 

从这个命令可以看出,最终会以arch/x86/Kconfig为参数运行scripts/kconfig/mconf这个脚本,出现配置界面。注意,mconf是个C程序,该程序并不属于内核,而是一个用户态程序。Linux 源代码中这一类程序还有很多。如在scripts/kconfig/目录下就有mconf,gconf,qconf 等等。它们用来执行内核的配置工作。

 

scripts/kconfig/mconf 这个程序采用了ncurses类库。这是一个在文本界面下进行画图操作类库。由于要适应不同平台,源代码中的mconf不是预编译

 

好的elf 可执行文件,而是在使用时才去编译生成。这使用户在运行make menuconfig时要依赖ncurses的开发包。所以,如果你机器上没有ncursesrpm包,建议去下载并安装。

 

arch/x86/Kconfig,准确地说是各个Kconfig 文件记录了各个内核配置的选项。我们在make menuconfig 或者make xconfig 时显示的菜单项和帮助信息,都是从这个文件中读出来的。我们来看看这个文件的内容:

……

config NEED_PER_CPU_EMBED_FIRST_CHUNK

       def_bool y

config NEED_PER_CPU_PAGE_FIRST_CHUNK

       def_bool y

config HAVE_CPUMASK_OF_CPU_MAP

       def_bool I386_SMP

……

 

当年配置完menuconfig以后,就会在主目录下生成一个.config文件,我们再来看看这个文件的部分内容:

……

CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y

CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y

CONFIG_HAVE_CPUMASK_OF_CPU_MAP=y

……

 

不错,生成一些CONFIG_XXX的东西。这些东西作为全局宏变量,由我们的Makefile引用。至于如何引用,且听下回分解。

抱歉!评论已关闭.