Linux内核配置系统浅析(zz)
时间:2006-07-05 来源:legion8169
Linux内核配置系统浅析 |
随着 Linux 操作系统的广泛应用,特别是 Linux 在嵌入式领域的发展,越来越多的人开始投身到 Linux 内核级的开发中。面对日益庞大的 Linux 内核源代码,开发者在完成自己的内核代码后,都将面临着同样的问题,即如何将源代码融入到 Linux 内核中,增加相应的 Linux 配置选项,并最终被编译进 Linux 内核。这就需要了解 Linux 的内核配置系统。
众所周知,Linux 内核是由分布在全球的 Linux 爱好者共同开发的,Linux 内核每天都面临着许多新的变化。但是,Linux 内核的组织并没有出现混乱的现象,反而显得非常的简洁,而且具有很好的扩展性,开发人员可以很方便的向 Linux 内核中增加新的内容。原因之一就是 Linux 采用了模块化的内核配置系统,从而保证了内核的扩展性。 本文首先分析了 Linux 内核中的配置系统结构,然后,解释了 Makefile 和配置文件的格式以及配置语句的含义,最后,通过一个简单的例子--TEST Driver,具体说明如何将自行开发的代码加入到 Linux 内核中。在下面的文章中,不可能解释所有的功能和命令,只对那些常用的进行解释,至于那些没有讨论到的,请读者参考后面的参考文献。
1. 配置系统的基本结构
Makefile:分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则; 2. Makefile
由于 Linux 内核源代码是按照树形结构组织的,所以 Makefile 也被分布在目录树中。Linux 内核中的 Makefile 以及与 Makefile 直接相关的文件有:
Makefile:顶层 Makefile,是整个内核配置、编译的总体控制文件。 位于各个子目录下的 Makefile 同样也根据 .config 给出的配置信息,构造出当前配置下需要的源文件列表,并在文件的最后有 include $(TOPDIR)/Rules.make。 Rules.make 文件起着非常重要的作用,它定义了所有 Makefile 共用的编译规则。比如,如果需要将本目录下所有的 c 程序编译成汇编代码,需要在 Makefile 中有以下的编译规则:
%.s: %.c 有很多子目录下都有同样的要求,就需要在各自的 Makefile 中包含此编译规则,这会比较麻烦。而 Linux 内核中则把此类的编译规则统一放置到 Rules.make 中,并在各自的 Makefile 中包含进了 Rules.make(include Rules.make),这样就避免了在多个 Makefile 中重复同样的规则。对于上面的例子,在 Rules.make 中对应的规则为:
%.s: %.c
2.2 Makefile 中的变量 常用的变量有以下几类:
1) 版本信息
2) CPU 体系结构:ARCH
3) 路径信息:TOPDIR, SUBDIRS
4) 内核组成信息:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs 可以看出,vmlinux 是由 HEAD、main.o、version.o、CORE_FILES、DRIVERS、NETWORKS 和 LIBS 组成的。这些变量(如 HEAD)都是用来定义连接生成 vmlinux 的目标文件和库文件列表。其中,HEAD在arch/*/Makefile 中定义,用来确定被最先链接进 vmlinux 的文件列表。比如,对于 ARM 系列的 CPU,HEAD 定义为:
HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ 表明 head-$(PROCESSOR).o 和 init_task.o 需要最先被链接到 vmlinux 中。PROCESSOR 为 armv 或 armo,取决于目标 CPU。 CORE_FILES,NETWORK,DRIVERS 和 LIBS 在顶层 Makefile 中定义,并且由 arch/*/Makefile 根据需要进行扩充。 CORE_FILES 对应着内核的核心文件,有 kernel/kernel.o,mm/mm.o,fs/fs.o,ipc/ipc.o,可以看出,这些是组成内核最为重要的文件。同时,arch/arm/Makefile 对 CORE_FILES 进行了扩充: # arch/arm/Makefile
# If we have a machine-specific directory, then include it in the build.
HEAD := arch/arm/kernel/head-$(PROCESSOR).o \
5) 编译信息:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
CROSS_COMPILE = arm-linux-
CROSS_COMPILE 定义了交叉编译器前缀 arm-linux-,表明所有的交叉编译工具都是以 arm-linux- 开头的,所以在各个交叉编译器工具之前,都加入了 $(CROSS_COMPILE),以组成一个完整的交叉编译工具文件名,比如 arm-linux-gcc。 # arch/arm/Makefile LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds
6) 配置变量CONFIG_*
2.3 Rules.make 变量 O_OBJS,L_OBJS,OX_OBJS,LX_OBJS:本目录下需要编译进 Linux 内核 vmlinux 的目标文件列表,其中 OX_OBJS 和 LX_OBJS 中的 "X" 表明目标文件使用了 EXPORT_SYMBOL 输出符号。 M_OBJS,MX_OBJS:本目录下需要被编译成可装载模块的目标文件列表。同样,MX_OBJS 中的 "X" 表明目标文件使用了 EXPORT_SYMBOL 输出符号。 O_TARGET,L_TARGET:每个子目录下都有一个 O_TARGET 或 L_TARGET,Rules.make 首先从源代码编译生成 O_OBJS 和 OX_OBJS 中所有的目标文件,然后使用 $(LD) -r 把它们链接成一个 O_TARGET 或 L_TARGET。O_TARGET 以 .o 结尾,而 L_TARGET 以 .a 结尾。
2.4 子目录 Makefile
# export-objs := tc.o # Object file lists.
obj-y :=
obj-$(CONFIG_TC) += tc.o # Files that are both resident and modular: remove from modular. obj-m := $(filter-out $(obj-y), $(obj-m)) # Translate to Rules.make lists. L_TARGET := tc.a
L_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) include $(TOPDIR)/Rules.make
a) 注释
b) 编译目标定义
c) 适配段 d) include $(TOPDIR)/Rules.make 3. 配置文件
配置命令 解释脚本 以字符界面配置(make config)为例,顶层 Makefile 调用 scripts/Configure, 按照 arch/arm/config.in 来进行配置。命令执行完后产生文件 .config,其中保存着配置信息。下一次再做 make config 将产生新的 .config 文件,原 .config 被改名为 .config.old
3.2 配置语言 2) 询问语句
bool /prompt/ /symbol/ 询问语句首先显示一串提示符 /prompt/,等待用户输入,并把输入的结果赋给 /symbol/ 所代表的配置变量。不同的询问语句的区别在于它们接受的输入数据类型不同,比如 bool 接受布尔类型( y 或 n ),hex 接受 16 进制数据。有些询问语句还有第三个参数 /word/,用来给出缺省值。 3) 定义语句
define_bool /symbol/ /word/ 不同于询问语句等待用户输入,定义语句显式的给配置变量 /symbol/ 赋值 /word/。 4) 依赖语句
与询问语句类似,依赖语句也是定义新的配置变量。不同的是,配置变量/symbol/的取值范围将依赖于配置变量列表/dep/ …。这就意味着:被定义的配置变量所对应功能的取舍取决于依赖列表所对应功能的选择。以dep_bool为例,如果/dep/ …列表的所有配置变量都取值y,则显示/prompt/,用户可输入任意的值给配置变量/symbol/,但是只要有一个配置变量的取值为n,则/symbol/被强制成n。 5) 选择语句 choice /prompt/ /word/ /word/
Choice 首先显示 /prompt/,然后将 /word/ 分解成前后两个部分,前部分为对应选择的提示符,后部分是对应选择的配置变量。用户选择的配置变量为 y,其余的都为 n。 6) if语句
if [ /expr/ ] ; then if 语句对配置变量(或配置变量的组合)进行判断,并作出不同的处理。判断条件 /expr/ 可以是单个配置变量或字符串,也可以是带操作符的表达式。操作符有:=,!=,-o,-a 等。 7) 菜单块(menu block)语句
引入新的菜单。在向内核增加新的功能后,需要相应的增加新的菜单,并在新菜单下给出此项功能的配置选项。Comment 后带的注释就是新菜单的名称。所有归属于此菜单的配置选项语句都写在 comment 和 endmenu 之间。
8) Source 语句
3.3 缺省配置 Linux 内核中针对每个 ARCH 都会有一个缺省配置。在向内核代码增加了新的功能后,如果新功能对于这个 ARCH 是必需的,就要修改此 ARCH 的缺省配置。修改方法如下(在 Linux 内核根目录下):
备份 .config 文件
3.4 help file 所有配置选项的帮助信息都在 Documentation/Configure.help 中,它的格式为:
<description> 给出本配置选项的名称,<variable name> 对应配置变量,<help file> 对应配置帮助信息。在帮助信息中,首先简单描述此功能,其次说明选择了此功能后会有什么效果,不选择又有什么效果,最后,不要忘了写上"如果不清楚,选择 N(或者)Y",给不知所措的用户以提示。
4. 实例
4.1 目录结构
$cd drivers/test
4.2 配置文件
bool 'TEST support' CONFIG_TEST endmenu 由于 test driver 对于内核来说是新的功能,所以首先创建一个菜单 TEST Driver。然后,显示 "TEST support",等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y),则进一步显示子功能:用户接口与 CPU 功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了 tristate(因为 tristate 的取值范围包括 y、n 和 m,m 就是对应着模块)。
2) arch/arm/config.in
4.3 Makefile
SUB_DIRS :=
L_TARGET := test.a
obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o subdir-$(CONFIG_TEST_CPU) += cpu include $(TOPDIR)/Rules.make
clean: drivers/test 目录下最终生成的目标文件是 test.a。在 test.c 和 test-client.c 中使用了 EXPORT_SYMBOL 输出符号,所以 test.o 和 test-client.o 位于 export-objs 列表中。然后,根据用户的选择(具体来说,就是配置变量的取值),构建各自对应的 obj-* 列表。由于 TEST Driver 中包一个子目录 cpu,当 CONFIG_TEST_CPU=y(即用户选择了此功能)时,需要将 cpu 目录加入到 subdir-y 列表中。 2)drivers/test/cpu/Makefile
SUB_DIRS := L_TARGET := test_cpu.a obj-$(CONFIG_test_CPU) += cpu.o
clean: 3)drivers/Makefile
在 drivers/Makefile 中加入 subdir-$(CONFIG_TEST)+= test,使得在用户选择 TEST Driver 功能后,内核编译时能够进入 test 目录。 4)Makefile
DRIVERS := $(DRIVERS-y) 在顶层 Makefile 中加入 DRIVERS-$(CONFIG_TEST) += drivers/test/test.a 和 DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a。如何用户选择了 TEST Driver,那么 CONFIG_TEST 和 CONFIG_TEST_CPU 都是 y,test.a 和 test_cpu.a 就都位于 DRIVERS-y 列表中,然后又被放置在 DRIVERS 列表中。在前面曾经提到过,Linux 内核文件 vmlinux 的组成中包括 DRIVERS,所以 test.a 和 test_cpu.a 最终可被链接到 vmlinux 中。 |