KBUILD系统原理分析
这些.o文件所在的规则之所以会被执行,是因为这些.o文件出现在“终极目标”的依赖列表中。如果在Makefile中一个规则所描述的目标不是“终极目标”所依赖的(或者“终极目标”的依赖文件所依赖的),那么这个规则将不会被执行。除非明确指定这个规则(可以通过make的命令行指定重建目标,那么这个目标所在的规则就会被执行,例如 “make clean”)。在编译或者重新编译生成一个.o文件时,make同样会去寻找它的依赖文件的重建规则(是这样一个规则:这个依赖文件在规则中作为目标出现),就是.c和.h文件的重建规则。在上例的Makefile中没有哪个规则的目标是.c或者.h文件,所以没有重建.c和.h文件的规则。
完成了对.o文件的创建(第一次编译)或者更新之后,make程序将处理终极目标“edit”所在的规则,分为以下三种情况:
1.目标文件“edit”不存在,则执行规则创建目标“edit”。 2.目标文件“edit”存在,其依赖文件中有一个或者多个文件比它“更新”,则根据规则重新链接生成“edit”。
3.目标文件“edit”存在,它比它的任何一个依赖文件都“更新”,则什么也不做。 上例中,如果更改了源文件“insert.c”后执行make,“insert.o”将被更新,之后终极目标“edit”将会被重生成;如果我们修改了头文件“command.h”之后运行“make”,那么“kbd.o”、“command.o”和“files.o”将会被重新编译,之后同样终极目标“edit”也将被重新生成。
以上我们通过一个简单的例子,介绍了Makefile中目标和依赖的关系。对于Makefile中的目标。在执行“make”时首先执行终极目标所在的规则,接下来一层层地去寻找终极目标的依赖文件所在的规则并执行。当终极目标的规则被完全的展开以后,make将从最后一个被展开的规则处开始执行,之后处理倒数第二个规则,……依次回退。最后一步执行的就是终极目标所在的规则。整个过程就类似于C语言中的递归实现一样。在更新(或者创建)终极目标的过程中,如果出现错误make就立即报错并退出。整个过程make只是负责执行规则,而对具体规则所描述的依赖关系的正确性、规则所定义的命令的正确性不做任何判断。就是说,一个规则的依赖关系是否正确、描述重建目标的规则命令行是否正确,make不做任何错误检查。
因此,需要正确的编译一个工程。需要在提供给make程序的Makefile中来保证其依赖关系的正确性、和执行命令的正确性。
1.6 总结
make的执行过程如下:
1. 依次读取变量“MAKEFILES”定义的makefile文件列表
2. 读取工作目录下的makefile文件(根据命名的查找顺序“GNUmakefile”,
“makefile”,“Makefile”,首先找到那个就读取那个)
6
KBUILD系统原理分析
3. 依次读取工作目录makefile文件中使用指示符“include”包含的文件 4. 查找重建所有已读取的makefile文件的规则(如果存在一个目标是当前读取
的某一个makefile文件,则执行此规则重建此makefile文件,完成以后从第一步开始重新执行)
5. 初始化变量值并展开那些需要立即展开的变量和函数并根据预设条件确定执
行分支
6. 根据“终极目标”以及其他目标的依赖关系建立依赖关系链表
7. 执行除“终极目标”以外的所有的目标的规则(规则中如果依赖文件中任一个
文件的时间戳比目标文件新,则使用规则所定义的命令重建目标文件) 8. 执行“终极目标”所在的规则
Chapter 2. KBUILD MAKE原理介绍
2.1 概述
Linux内核的Makefile分为5个部分: Makefile .config arch/$(ARCH)/Makefile scripts/Makefile.* kbuild Makefiles 顶层Makefile阅读的.config文件,而该文件是由内核配置程序生成的。顶层Makefile负责制作:vmlinux(内核文件)与模块(任何模块文件)。制作的过程主要是通过递归向下访问子目录的形式完成。并根据内核配置文件确定访问哪些子目录。顶层Makefile要原封不动的包含一具体架构的Makefile,其名字类似于 arch/$(ARCH)/Makefile。该架构Makefile向顶层Makefile提供其架构的特别信息。
每一个子目录都有一个Kbuild Makefile文件,用来执行从其上层目录传递下来的命令。Kbuild Makefile从.config文件中提取信息,生成Kbuild完成内核编译所需的文件列表。
scripts/Makefile.*包含了所有的定义、规则等信息。这些文件被用来编译基于kbuild Makefile的内核。
顶层Makefile 内核配置文件 具体架构的Makefile 通用的规则等,面向所有的Kbuild Makefiles。 内核源代码中大约有500个这样的文件 2.2 Kbuild文件
大部分内核中的Makefile都是使用Kbuild组织结构的Kbuild Makefile。这章介绍了Kbuild Makefile的语法。Kbuild文件倾向于\这个名字,\也是可以用的。但如果\同时出现的话,使用的将会是\文件。
7
KBUILD系统原理分析
3.1节 目标定义是一个快速介绍,以后的几章会提供更详细的内容以及实例。
2.2.1 目标定义
目标定义是Kbuild Makefile的主要部分,也是核心部分。主要是定义了要编译的文件,所有的选项,以及到哪些子目录去执行递归操作。
最简单的Kbuild makefile 只包含一行: obj-y += foo.o
该例子告诉Kbuild在这目录里,有一个名为foo.o的目标文件。foo.o将从foo.c或foo.S文件编译得到。
如果foo.o要编译成一模块,那就要用obj-m了。所采用的形式如下: obj-$(CONFIG_FOO) += foo.o
$(CONFIG_FOO)可以为y(编译进内核) 或m(编译成模块)。如果CONFIG_FOO不是y和m,那么该文件就不会被编译联接了。
2.2.2 编译进内核 - obj-y
Kbuild Makefile 规定所有编译进内核的目标文件都存在$(obj-y)列表中。而这些列表依赖内核的配置。
Kbuild编译所有的$(obj-y)文件。然后,调用\ -r\将它们合并到一个build-in.o文件中。稍后,该build-in.o会被其父Makefile联接进vmlinux中。
$(obj-y)中的文件是有顺序的。列表中有重复项是可以的:当第一个文件被联接到built-in.o中后,其余文件就被忽略了。
联接也是有顺序的,那是因为有些函数(module_init()/__initcall)将会在启动时按照他们出现的顺序进行调用。所以,记住改变联接的顺序可能改变你SCSI控制器的检测顺序,从而导致你的硬盘数据损害。
#drivers/isdn/i4l/Makefile
# Makefile for the kernel ISDN subsystem and device drivers. # Each configuration option enables a list of files. obj-$(CONFIG_ISDN) += isdn.o
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o
2.2.3 编译可装载模块 - obj-m
$(obj-m) 列举出了哪些文件要编译成可装载模块。
一个模块可以由一个文件或多个文件编译而成。如果是一个源文件,KbuildMakefile只需简单的将其加到$(obj-m)中去就可以了。
#drivers/isdn/i4l/Makefile
obj-$(CONFIG_ISDN_PPP_BSDCOMP) += isdn_bsdcomp.o 注意:此例中 $(CONFIG_ISDN_PPP_BSDCOMP) 的值为'm'
如果内核模块是由多个源文件编译而成,那你就要采用上面那个例子一样的方法去声明你所要编译的模块。
8
KBUILD系统原理分析
Kbuild需要知道你所编译的模块是基于哪些文件,所以你需要通过变量$(
#drivers/isdn/i4l/Makefile obj-$(CONFIG_ISDN) += isdn.o
isdn-objs := isdn_net_lib.o isdn_v110.o isdn_common.o
在这个例子中,模块名将是isdn.o,Kbuild将编译在$(isdn-objs)中列出的所有文件,然后使用\ -r\生成isdn.o。
Kbuild能够识别用于组成目标文件的后缀-objs和后缀-y。这就让KbuildMakefile可以通过使用 CONFIG_ 符号来判断该对象是否是用来组合对象的。
#fs/ext2/Makefile
obj-$(CONFIG_EXT2_FS) += ext2.o ext2-y := balloc.o bitmap.o
ext2-$(CONFIG_EXT2_FS_XATTR) += xattr.o
在这个例子中,如果 $(CONFIG_EXT2_FS_XATTR) 是 'y',xattr.o将是复合对象 ext2.o的一部分。
注意:当然,当你要将其编译进内核时,上面的语法同样适用。所以,如果你的 CONFIG_EXT2_FS=y,那Kbuild会按你所期望的那样,生成 ext2.o文件,然后将其联接到 built-in.o中。
2.2.4 目标库文件 - lib-y
在 obj-* 中所列文件是用来编译模块或者是联接到特定目录中的 built-in.o。同样,也可以列出一些将被包含在lib.a库中的文件。在 lib-y 中所列出的文件用来组成该目录下的一个库文件。
在 obj-y 与 lib-y 中同时列出的文件,因为都是可以访问的,所以该文件是不会被包含在库文件中的。同样的情况, lib-m 中的文件就要包含在 lib.a 库文件中。
注意,一个Kbuild makefile可以同时列出要编译进内核的文件与要编译成库的文件。所以,在一个目录里可以同时存在 built-in.o 与 lib.a 两个文件。
#arch/i386/lib/Makefile lib-y := chechsum.o delay.o
这将由 checksum.o 和delay.o 两个文件创建一个库文件 lib.a。为了让Kbuild 真正认识到这里要有一个库文件 lib.a 要创建,其所在的目录要加到 libs-y 列表中。还可参考\ 递归下向时要访问的目录列表\ 使用一般限制在 lib/ 和 arch/*/lib 中。
2.2.5递归向下访问目录
一个Makefile只对编译所在目录的对象负责。在子目录中的文件的编译要由其所在的子目录的Makefile来管理。只要你让Kbuild知道它应该递归操作,那么该系统就会在其子目录中自动的调用 make 递归操作。
这就是 obj-y 和 obj-m 的作用。
ext2 被放的一个单独的目录下,在fs目录下的Makefile会告诉Kbuild使用下面的赋值进行向下递归操作。
#fs/Makefile
9
KBUILD系统原理分析 obj-$(CONFIG_EXT2_FS) += ext2/ 如果 CONFIG_EXT2_FS 被设置为 'y'(编译进内核)或是'm'(编译成模块),相应的 obj- 变量就会被设置,并且Kbuild就会递归向下访问 ext2 目录。Kbuild只是用这些信息来决定它是否需要访问该目录,而具体怎么编译由该目录中的Makefile来决定。
将 CONFIG_ 变量设置成目录名是一个好的编程习惯。这让Kbuild在完全忽略那些相应的 CONFIG_ 值不是'y'和'm'的目录。
2.2.6 编辑标志
EXTRA_CFLAGS, EXTRA_AFLAGS, EXTRA_LDFLAGS, EXTRA_ARFLAGS
所有的 EXTRA_ 变量只在所定义的Kbuild Makefile中起作用。EXTRA_ 变量可以在Kbuild Makefile中所有命令中使用。
$(EXTRA_CFLAGS) 是用 $(CC) 编译C源文件时的选项。
# drivers/sound/emu10kl/Makefile EXTRA_CFLAGS += -I$(obj) ifdef DEBUG
EXTRA_CFLAGS += -DEMU10KL_DEBUG endif
该变量是必须的,因为顶层Makefile拥有变量 $(CFLAGS) 并用来作为整个源代码树的编译选项。
$(EXTRA_AFLAGS) 也是一个针对每个目录的选项,只不过它是用来编译汇编源代码的。
#arch/x86_64/kernel/Makefile EXTRA_AFLAGS := -traditional
$(EXTRA_LDFLAGS) 和 $(EXTRA_ARFLAGS)分别与 $(LD)和 $(AR)类似,只不过,他们是针对每个目录的。
#arch/m68k/fpsp040/Makefile EXTRA_LDFLAGS := -x
CFLAGS_$@, AFLSGA_$@
CFLAGS_$@ 和 AFLAGS_$@ 只能在当前Kbuild Makefile中的命令中使用。 $(CFLAGS_$@) 是 $(CC) 针对每个文件的选项。$@ 表明了具体操作的文件。 # drivers/scsi/Makefile
CFLAGS_aha152x.o = -DAHA152X_STAT -DAUTOCONF
CFLAGS_gdth.o = # -DDEBUG_GDTH=2 -D__SERIAL__ -D__COM2__ \\ -DGDTH_STATISTICS
CFLAGS_seagate.o = -DARBITRATE -DPARITY -DSEAGATE_USE_ASM 以上三行分别设置了aha152x.o,gdth.o 和 seagate.o的编辑选项。
10