linux核心的設定係統由3部分組成:1、Makefile,分佈在Linux核心原始碼根目錄及各層目錄中,定義Linux核心的編譯規則;2、設定檔(config.in) ,提供使用者配置選擇的能力;3、配置工具,包括配置命令解釋器(對配置腳本中使用的配置命令進行解釋)和配置使用者介面。
本教學操作環境:linux7.3系統、Dell G3電腦。
Linux核心的設定係統由三個部分組成,它們分別是:Makefile、設定檔( config.in )、設定工具。
Makefile:分佈在Linux 核心原始碼根目錄及各層目錄中,定義Linux 核心的編譯規則;
設定檔(config.in):提供使用者設定選擇的能力;
Makefile
有很多子目錄下都有同樣的要求,就需要在各自的Makefile 中包含此編譯規則,這會比較麻煩。而Linux 核心中則把此類的編譯規則統一放置到Rules.make 中,並在各自的Makefile 中包含進了Rules.make(include Rules.make),這樣就避免了在多個Makefile 中重複相同的規則。對於上面的例子,在 Rules.make 中對應的規則為:%.s: %.c
(C#C##) # (CFLAGS) -S # e3f602526e88da917cf786397b7eed9c67859a528e5e9688f6a0ae3be0eab7d3@
%.s: %.c
(CC) (CFLAGS) (EXTRACFLAGS) (CFLAGS_ (∗F)) (CFLAGS_ @)−S < -o $@
Makefile中的变量
顶层 Makefile 定义并向环境中输出了许多变量,为各个子目录下的 Makefile 传递一些信息。有些变量,比如 SUBDIRS,不仅在顶层 Makefile 中定义并且赋初值,而且在 arch/*/Makefile 还作了扩充。
常用的变量有以下几类:
1) 版本信息
版本信息有:VERSION,PATCHLEVEL, SUBLEVEL, EXTRAVERSION,KERNELRELEASE。 版本信息定义了当前内核的版本,比如 VERSION=2,PATCHLEVEL=4,SUBLEVEL=18,EXATAVERSION=-rmk7,它们共同构成内核的发行版本KERNELRELEASE:2.4.18-rmk7
2) CPU 体系结构:ARCH
在顶层 Makefile 的开头,用 ARCH 定义目标 CPU 的体系结构,比如 ARCH:=arm 等。许多子目录的 Makefile 中,要根据 ARCH 的定义选择编译源文件的列表。
3) 路径信息:TOPDIR, SUBDIRS
TOPDIR 定义了 Linux 内核源代码所在的根目录。例如,各个子目录下的 Makefile 通过 $(TOPDIR)/Rules.make 就可以找到 Rules.make 的位置。
SUBDIRS 定义了一个目录列表,在编译内核或模块时,顶层 Makefile 就是根据 SUBDIRS 来决定进入哪些子目录。SUBDIRS 的值取决于内核的配置,在顶层 Makefile 中 SUBDIRS 赋值为 kernel drivers mm fs net ipc lib;根据内核的配置情况,在 arch/*/Makefile 中扩充了 SUBDIRS 的值,参见4)中的例子。
4) 内核组成信息:HEAD, CORE_FILES, NETWORKS, DRIVERS, LIBS
Linux 内核文件 vmlinux 是由以下规则产生的:
vmlinux: $(CONFIGURATION) init/main.o init/version.o linuxsubdirs $(LD) $(LINKFLAGS) $(HEAD) init/main.o init/version.o \ --start-group \ $(CORE_FILES) \ $(DRIVERS) \ $(NETWORKS) \ $(LIBS) \ --end-group \ -o vmlinux
可以看出,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 \ arch/arm/kernel/init_task.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. MACHDIR := arch/arm/mach-$(MACHINE) ifeq ($(MACHDIR),$(wildcard $(MACHDIR))) SUBDIRS += $(MACHDIR) CORE_FILES := $(MACHDIR)/$(MACHINE).o $(CORE_FILES) endif HEAD := arch/arm/kernel/head-$(PROCESSOR).o \ arch/arm/kernel/init_task.o SUBDIRS += arch/arm/kernel arch/arm/mm arch/arm/lib arch/arm/nwfpe CORE_FILES := arch/arm/kernel/kernel.o arch/arm/mm/mm.o $(CORE_FILES) LIBS := arch/arm/lib/lib.a $(LIBS)
5) 编译信息:CPP, CC, AS, LD, AR,CFLAGS,LINKFLAGS
在 Rules.make 中定义的是编译的通用规则,具体到特定的场合,需要明确给出编译环境,编译环境就是在以上的变量中定义的。针对交叉编译的要求,定义了 CROSS_COMPILE。比如:
CROSS_COMPILE = arm-linux- CC = $(CROSS_COMPILE)gcc LD = $(CROSS_COMPILE)ld
CROSS_COMPILE 定义了交叉编译器前缀 arm-linux-,表明所有的交叉编译工具都是以 arm-linux- 开头的,所以在各个交叉编译器工具之前,都加入了 $(CROSS_COMPILE),以组成一个完整的交叉编译工具文件名,比如 arm-linux-gcc。
CFLAGS 定义了传递给 C 编译器的参数。
LINKFLAGS 是链接生成 vmlinux 时,由链接器使用的参数。LINKFLAGS 在 arm/*/Makefile 中定义,比如:
# arch/arm/Makefile LINKFLAGS :=-p -X -T arch/arm/vmlinux.lds
Rules.make变量
前面讲过,Rules.make 是编译规则文件,所有的 Makefile 中都会包括 Rules.make。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 结尾。
子目录Makefile
目录 Makefile 用来控制本级目录以下源代码的编译规则。我们通过一个例子来讲解子目录 Makefile 的组成:
# Makefile for the linux kernel. # # All of the (potential) objects that export symbols. # This list comes from 'grep -l EXPORT_SYMBOL *.[hc]'. export-objs := tc.o # Object file lists. obj-y := obj-m := obj-n := obj- := obj-$(CONFIG_TC) += tc.o obj-$(CONFIG_ZS) += zs.o obj-$(CONFIG_VT) += lk201.o lk201-map.o lk201-remap.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))) LX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) include $(TOPDIR)/Rules.make
a) 注释
对 Makefile 的说明和解释,由#开始。
b) 编译目标定义
类似于 obj-(CONFIGTC)+=tc.o的语句是用来定义编译的目标,是子目录Makefile中最重要的部分。编译目标定义那些在本子目录下,需要编译到Linux内核中的目标文件列表。为了只在用户选择了此功能后才编译,所有的目标定义都融合了对配置变量的判断。前面说过,每个配置变量取值范围是:y,n,m和空,obj−(CONFIG_TC) 分别对应着 obj-y,obj-n,obj-m,obj-。如果 CONFIG_TC 配置为 y,那么 tc.o 就进入了 obj-y 列表。obj-y 为包含到 Linux 内核 vmlinux 中的目标文件列表;obj-m 为编译成模块的目标文件列表;obj-n 和 obj- 中的文件列表被忽略。配置系统就根据这些列表的属性进行编译和链接。
export-objs 中的目标文件都使用了 EXPORT_SYMBOL() 定义了公共的符号,以便可装载模块使用。在 tc.c 文件的最后部分,有 “EXPORT_SYMBOL(search_tc_card);”,表明 tc.o 有符号输出。
这里需要指出的是,对于编译目标的定义,存在着两种格式,分别是老式定义和新式定义。老式定义就是前面 Rules.make 使用的那些变量,新式定义就是 obj-y,obj-m,obj-n 和 obj-。Linux 内核推荐使用新式定义,不过由于 Rules.make 不理解新式定义,需要在 Makefile 中的适配段将其转换成老式定义。
c) 适配段
适配段的作用是将新式定义转换成老式定义。在上面的例子中,适配段就是将 obj-y 和 obj-m 转换成 Rules.make 能够理解的 L_TARGET,L_OBJS,LX_OBJS,M_OBJS,MX_OBJS。
L_OBJS := (sort(filter-out (export−objs),(obj-y))) 定义了 L_OBJS 的生成方式:在 obj-y 的列表中过滤掉 export-objs(tc.o),然后排序并去除重复的文件名。这里使用到了 GNU Make 的一些特殊功能,具体的含义可参考 Make 的文档(info make)。
d) include $(TOPDIR)/Rules.make
配置文件功能概述
除了 Makefile 的编写,另外一个重要的工作就是把新功能加入到 Linux 的配置选项中,提供此项功能的说明,让用户有机会选择此项功能。所有的这些都需要在 config.in 文件中用配置语言来编写配置脚本,
在 Linux 内核中,配置命令有多种方式:
配置命令 | 解释脚本 |
---|---|
Make Config,make oldconfig | scripts/Configure |
Make menuconfig | scripts/Menuconfig |
Make xconfig | scripts/tkparse |
以字符界面配置(make config)为例,顶层 Makefile 调用 scripts/Configure, 按照 arch/arm/config.in 来进行配置。命令执行完后产生文件 .config,其中保存着配置信息。下一次再做 make config 将产生新的 .config 文件,原 .config 被改名为 .config.old
对于一个开发者来说,将自己开发的内核代码加入到 Linux 内核中,需要有三个步骤。首先确定把自己开发代码放入到内核的位置;其次,把自己开发的功能增加到 Linux 内核的配置选项中,使用户能够选择此功能;最后,构建子目录 Makefile,根据用户的选择,将相应的代码编译到最终生成的 Linux 内核中去。下面,我们就通过一个简单的例子–test driver,结合前面学到的知识,来说明如何向 Linux 内核中增加新的功能。
目录结构
test driver 放置在 drivers/test/ 目录下:
cddrivers/testtree
.
|– Config.in
|– Makefile
|– cpu
| |– Makefile
| -- cpu.c <br> |-- test.c <br> |-- test_client.c <br> |-- test_ioctl.c <br> |-- test_proc.c <br> |-- test_queue.c <br>
– test
|– Makefile
配置文件
# TEST driver configuration # mainmenu_option next_comment comment 'TEST Driver' bool 'TEST support' CONFIG_TEST if [ "$CONFIG_TEST" = "y" ]; then tristate 'TEST user-space interface' CONFIG_TEST_USER bool 'TEST CPU ' CONFIG_TEST_CPU fi endmenu
由于 test driver 对于内核来说是新的功能,所以首先创建一个菜单 TEST Driver。然后,显示 “TEST support”,等待用户选择;接下来判断用户是否选择了 TEST Driver,如果是(CONFIG_TEST=y),则进一步显示子功能:用户接口与 CPU 功能支持;由于用户接口功能可以被编译成内核模块,所以这里的询问语句使用了 tristate(因为 tristate 的取值范围包括 y、n 和 m,m 就是对应着模块)。
2) arch/arm/config.in
在文件的最后加入:source drivers/test/Config.in,将 TEST Driver 子功能的配置纳入到 Linux 内核的配置中。
Makefile
1)drivers/test/Makefile
# drivers/test/Makefile # # Makefile for the TEST. # SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) cpu L_TARGET := test.a export-objs := test.o test_client.o obj-$(CONFIG_TEST) += test.o test_queue.o test_client.o obj-$(CONFIG_TEST_USER) += test_ioctl.o obj-$(CONFIG_PROC_FS) += test_proc.o subdir-$(CONFIG_TEST_CPU) += cpu include $(TOPDIR)/Rules.make clean: for dir in $(ALL_SUB_DIRS); do make -C $$dir clean; done rm -f *.[oa] .*.flags
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
# drivers/test/test/Makefile # # Makefile for the TEST CPU # SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) ALL_SUB_DIRS := $(SUB_DIRS) L_TARGET := test_cpu.a obj-$(CONFIG_test_CPU) += cpu.o include $(TOPDIR)/Rules.make clean: rm -f *.[oa] .*.flags
3)drivers/Makefile
…… subdir-$(CONFIG_TEST) += test …… include $(TOPDIR)/Rules.make
在 drivers/Makefile 中加入 subdir-$(CONFIG_TEST)+= test,使得在用户选择 TEST Driver 功能后,内核编译时能够进入 test 目录。
4)Makefile
…… DRIVERS-$(CONFIG_PLD) += drivers/pld/pld.o DRIVERS-$(CONFIG_TEST) += drivers/test/test.a DRIVERS-$(CONFIG_TEST_CPU) += drivers/test/cpu/test_cpu.a DRIVERS := $(DRIVERS-y) ……
在顶层 Makefile 中加入 DRIVERS-(CONFIGTEST)+=drivers/test/test.a和DRIVERS−(CONFIGTEST)+=drivers/test/test.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 中。
相关推荐:《Linux视频教程》
以上是linux核心的配置系統由幾個部分組成的詳細內容。更多資訊請關注PHP中文網其他相關文章!