如何编译⼀个嵌⼊式的linux内核.config
1. 配置系统的基本结构
Linux内核的配置系统由三个部分组成,分别是:
Makefile:分布在 Linux 内核源代码中的 Makefile,定义 Linux 内核的编译规则;
配置⽂件(config.in):给⽤户提供配置选择的功能;
配置⼯具:包括配置命令解释器(对配置脚本中使⽤的配置命令进⾏解释)和配置⽤户界⾯(提供基于字符界⾯、基于 Ncurses 图形界⾯以及基于 Xwindows 图形界⾯的⽤户配置界⾯,各⾃对应于 Make config、Make menuconfig 和 make xconfig)。
这些配置⼯具都是使⽤脚本语⾔,如 Tcl/TK、Perl 编写的(也包含⼀些⽤ C 编写的代码)。本⽂并不是对配置系统本⾝进⾏分析,⽽是介绍如何使⽤配置系统。所以,除⾮是配置系统的维护者,⼀般的内核开发者⽆须了解它们的原理,只需要知道如何编写 Makefile 和配置⽂件就可以。
2. Makefile
2.1 Makefile 概述
Makefile 的作⽤是根据配置的情况,构造出需要编译的源⽂件列表,然后分别编译,并把⽬标代码链接到⼀起,最终形成 Linux 内核⼆进制⽂件。
由于Linux 内核源代码是按照树形结构组织的,所以 Makefile 也被分布在⽬录树中。Linux 内核中的 Makefile 以及与 Makefile 直接相关的⽂件有:
Makefile:顶层 Makefile,是整个内核配置、编译的总体控制⽂件。
.config:内核配置⽂件,包含由⽤户选择的配置选项,⽤来存放内核配置后的结果(如 make config)。
arch/*/Makefile:位于各种 CPU 体系⽬录下的 Makefile,如 arch/arm/Makefile,是针对特定平台的 Makefile。
各个⼦⽬录下的 Makefile:⽐如 drivers/Makefile,负责所在⼦⽬录下源代码的管理。
Rules.make:规则⽂件,被所有的 Makefile 使⽤。
⽤户通过 make **_defconfig,(针对SMDK2410的默认设置的配置⽂件位于:arch/arm/configs/s3c2410_defconfig,将这个⽂件拷贝到根⽬录下成为.config⽂件。在make menuconfig中加载这个默认的config⽂件;针对plugD的:
arch/arm/configs/plugD_defconfig) 配置后,产⽣了 .config。顶层 Makefile 读⼊ .config 中的配置选择。顶层 Makefile 有两个主要的任务:产⽣ vmlinux ⽂件和内核模块(module)。为了达到此⽬的,顶层 Makefile 递归的进⼊到内核的各个⼦⽬录中,分别调⽤位于这些⼦⽬录中的 Makefile。⾄于到底进⼊哪些⼦⽬录,取决于内核的配置。
  在顶层 Makefile 中,有⼀句:include arch/$(ARCH)/Makefile,包含了特定 CPU 体系结构下的 Makefile,这个 Makefile 中包含了平台相关的信息。位于各个⼦⽬录下的 Makefile 同样也根据 .config 给出的配置信息,构造出当前配置下需要的源⽂件列表,并在⽂件的最后有 include $(TOPDIR)/Rules.make。Rules.make ⽂件起着⾮常重要的作⽤,它定义了所有 Makefile 共⽤的编译规则。⽐如,如果需要将本⽬录下所有的 c 程序编译成汇编代码,需要在 Makefile 中有以下的编译规则:
%.s: %.c
$(CC) $(CFLAGS) -S $< -o $@
有很多⼦⽬录下都有同样的要求,就需要在各⾃的 Makefile 中包含此编译规则,这会⽐较⿇烦。⽽ Linux 内核中则把此类的编译规则统⼀放置到 Rules.make 中,并在各⾃的 Makefile 中包含进了 Rules.make(include Rules.make),这样就避免了在多个 Makefile 中重复同样的规则。对于上⾯的
例⼦,在 Rules.make 中对应的规则为:
%.s: %.c
$(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(CFLAGS_$(*F)) $(CFLAGS_$@) -S $< -o $@
2.2 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
6) 配置变量CONFIG_*
.config ⽂件中有许多的配置变量等式,⽤来说明⽤户配置的结果。例如 CONFIG_MODULES=y 表明⽤户选择了 Linux 内核的模块功能。
.config 被顶层 Makefile 包含后,就形成许多的配置变量,每个配置变量具有确定的值:y 表⽰本编译选项对应的内核代码被静态编译进Linux 内核;m 表⽰本编译选项对应的内核代码被编译成模块;n 表⽰不选择此编译选项;如果根本就没有选择,那么配置变量的值为空。
2.3 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 结尾。
2.4 ⼦⽬录 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-$(CONFIG_TC) += 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
3. 配置⽂件
3.1 配置功能概述
除了 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.oldlinux内核文件放在哪
3.2 配置语⾔
1) 顶层菜单
mainmenu_name /prompt/ /prompt/ 是⽤'或"包围的字符串,'与"的区别是'…'中可使⽤$引⽤变量的值。mainmenu_name 设置最⾼层菜单的名字,它只在 make xconfig 时才会显⽰。
2) 询问语句
bool /prompt/ /symbol/
hex /prompt/ /symbol/ /word/
int /prompt/ /symbol/ /word/
string /prompt/ /symbol/ /word/
tristate /prompt/ /symbol/
询问语句⾸先显⽰⼀串提⽰符 /prompt/,等待⽤户输⼊,并把输⼊的结果赋给 /symbol/ 所代表的配置变量。不同的询问语句的区别在于它们接受的输⼊数据类型不同,⽐如 bool 接受布尔类型( y 或 n ),hex 接受 16 进制数据。有些询问语句还有第三个参数 /word/,⽤来给出缺省值。
3) 定义语句
define_bool /symbol/ /word/
define_hex /symbol/ /word/
define_int /symbol/ /word/
define_string /symbol/ /word/
define_tristate /symbol/ /word/
不同于询问语句等待⽤户输⼊,定义语句显式的给配置变量 /symbol/ 赋值 /word/。
4) 依赖语句
dep_bool /prompt/ /symbol/ /dep/ ...
dep_mbool /prompt/ /symbol/ /dep/ ...
dep_hex /prompt/ /symbol/ /word/ /dep/ ...
dep_int /prompt/ /symbol/ /word/ /dep/ ...
dep_string /prompt/ /symbol/ /word/ /dep/ ...
dep_tristate /prompt/ /symbol/ /dep/ ...
与询问语句类似,依赖语句也是定义新的配置变量。不同的是,配置变量/symbol/的取值范围将依赖于配置变量列表/dep/ …。这就意味着:被定义的配置变量所对应功能的取舍取决于依赖列表所对应功能的选择。以dep_bool为例,如果/dep/ …列表的所有配置变量都取值y,则显⽰/prompt/,⽤户可输⼊任意的值给配置变量/symbol/,但是只要有⼀个配置变量的取值为n,则/symbol/被强制成n。
不同依赖语句的区别在于它们由依赖条件所产⽣的取值范围不同。
5) 选择语句
choice /prompt/ /word/ /word/
choice 语句⾸先给出⼀串选择列表,供⽤户选择其中⼀种。⽐如 Linux for ARM ⽀持多种基于 ARM core 的 CPU,Linux 使⽤ choice 语句提供⼀个 CPU 列表,供⽤户选择:
choice 'ARM system type' \
"Anakin CONFIG_ARCH_ANAKIN \
Archimedes/A5000 CONFIG_ARCH_ARCA5K \
Cirrus-CL-PS7500FE CONFIG_ARCH_CLPS7500 \
……
SA1100-based CONFIG_ARCH_SA1100 \
Shark CONFIG_ARCH_SHARK" RiscPC
Choice ⾸先显⽰ /prompt/,然后将 /word/ 分解成前后两个部分,前部分为对应选择的提⽰符,后部分是对应选择的配置变量。⽤户选择的配置变量为 y,其余的都为 n。
6) if语句

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。