blog.csdn.net/u013554213/article/details/79012612
make menuconfig/.config/Kconfig解析 -- 这篇文章对kconfig分析的很透彻
blog.chinaunix.net/uid-24227137-id-3277449.html
linux内核的配置机制及其编译过程
---------------------------------
一、Linux内核配置系统的基本结构
Linux内核的配置系统由三个部分组成,分别是:
1) Makefile:分布在Linux内核源代码根目录及各层目录中,定义Linux内核的编译规则;
2) 配置文件:(config.in(2.4内核),Kconfig(2.6及以后的内核)),给用户提供配置选择的功能;
3) 配置工具:包括配置命令解释器(对配置脚本中使用的配置命令进行解释)和配置用户界面(提供基于字符界面、基于 Ncurses 图形界面以及基于 Xwindows 图形界面的用户配置界面,各自对应于 make config、make menuconfig 和 make xconfig)。
这些配置工具都是使用脚本语言,如 Tcl/TK、Perl 编写的(也包含一些用 C 编写的代码)。本文并不是对配置系统本身进行分析,而是介绍如何使用配置系统。所以,除非是配置系统的维护者,一般的内核开发者无须了解它们的原理,只需要知道如何编写 Makefile 和配置文件就可以。
二、makefile menuconfig过程讲解
当我们在执行make menuconfig这个命令时,系统到底帮我们做了哪些工作呢?这里面一共涉及到以下几个文件:
1) Linux内核根目录下的scripts文件夹
2) arch/$(ARCH)/Kconfig文件、各层目录下的Kconfig文件
3) Linux内核根目录下的Makefile文件、各层目录下的Makefile文件
4) Linux内核根目录下的.config文件、arm/$(ARCH)/下的config文件
5) Linux内核根目录下的include/generated/autoconf.h文件
下面来一一讲解:
1) Linux内核根目录下的scripts文件夹
scripts文件夹存放的是跟make menuconfig配置界面的图形绘制相关的文件,我们作为使用者无需关心这个文件夹的内容
2) arch/$(ARCH)/Kconfig文件、各层目录下的Kconfig文件
当我们执行make menuconfig命令出现上述蓝色配置界面以前,系统帮我们做了以下工作:
首先系统会读取arch/$(ARCH)/目录下的Kconfig文件生成整个配置界面选项(Kconfig是整个linux配置机制的核心),那么ARCH环境变量的值等于多少呢?
它是由linux内核根目录下的Makefile文件决定的,在Makefile下有此环境变量的定义,例如:
ARCH ?= arm
CROSS_COMPILE ?= arm-eabi-
或者通过make ARCH=arm menuconfig命令来生成配置界面。
3) Linux内核根目录下的.config文件、arm/$(ARCH)/下的config文件
执行make menuconfig命令之后,内核默认读取linux内核根目录下.config文件作为内核配置项的默认值。如果内核根目录下没有.config文件,我们一般会根据开发板的类型从arch/$(ARCH)/configs目录中选取一个与我们开发板最接近的配置文件到Linux内核根目录下,例如:
cp arch/arm/configs/s3c2410_defconfig .config
如何更改内核配置选项呢?我们可以选择直接修改.config文件然后执行make menuconfig命令读取新的选项。但是一般我们不采取这个方案,我们选择在配置界面中通过空格、esc、回车选择某些选项选中或者不选中,最后保存退出的时候,Linux内核会把新的选项更新到.config中。
4)经过以上几步,我们可以正确的读取、配置我们需要的界面了。那么他们是如何与Makefile文件建立编译关系呢?当你保存make menuconfig选项时,系统会除了会自动更新.config外,还会将所有的选项以宏的形式保存在Linux内核根目录下的include/generated/autoconf.h文件下,内核中的源代码就都会包含这个.h文件,跟宏的定义情况进行条件编译。
至此,我们就完成了整个linux内核的编译过程。最后我们会发现,整个linux内核配置过程中,留给用户的接口其实只有各层Kconfig、Makefile文件以及对应的源文件。比如想要给内核增加一个功能,并且通过make menuconfig控制其生成过程,需要做的工作:
首先,修改/增加对应目录下的Kconfig文件,按照Kconfig语法增加对应的选项;
其次,make menuconfig选择编译进内核或者不编译进内核,或者编译为模块。保存之后自动生成.config和autoconf.h文件;
然后,修改/增加对应目录下的Makefile文件完成编译选项的添加;
最后,执行make zImage命令进行编译。
三、总结
跟make menuconfig这个命令相关的文件,包括三类,包括.config,Kconfig,Makefile。为什么不说三个,而说三类呢?因为 Kconfig和Makefile是配合使用的,在很多的子目录都存在,而.config只存在于根目录中。这三个文件的作用分别是:
Kconfig: 定义了配置项
.config: 对配置项进行赋值
Makefile: 建立配置项的生成法则
Kconfig语法
-------------
Kconfig的基本构成包括五种,menu/endmenu,menuconfig,config,choice/endchoice,source。下面就对每种详细介绍:
(1) menu/endmenu
menu的作用,可以理解成一个目录,menu可以把其中一部分配置项包含到一个menu中,这样有利于配置的分类显示。menu与endmenu是一组指令,必须同时出现。menu和endmenu中包含的部分就是子目录中的配置项。
(2) menuconfig
menuconfig有点类似menu,它是可以配置的,在内核配置界面下通过空格可以修改这个配置项的选中状态。而且从格式上来看,也是有区别的。如init/Kconfig文件中:
menuconfig MODULES
bool "Enable loadable module support"config
if MODULES
...
...
endif
如果选中了MODULE,那么if和endif中的内容可以显示。如果没有定义,就只能进入一个空目录。
(3) config
config是构成Kconfig的最基本单元,其中定义了配置项的详细信息。如:
config ARM
bool
default y
****************
config的类型有5种:
bool(y/n)
tristate(y/m/n)
string(字符串)
hex(十六进 制)
integer(整数)
其中,bool只能表示选中和不选,而tristate还可以配置成模块 (m),特别对于驱动程序的开发非常有用。
config的其他语法如下:
1) prompt:提示,显示在make menuconfig中的名称,一般省略。下面两种写法相同。
a. bool “Networking Support”
b. bool
prompt “Networking Support”
2) default:默认值
一个配置项可以有多个默认值,但是只有第一个被定义的值是有效的。
3)depends on/requires:依赖关系
如果依赖的配置项没有选中,那么就当前项也无法选中。
4) select:反向依赖
如果当前项选中,那么也选中select后的选项。
5) range:范围,用于hex和integer
range A B表示当前值不小于A,不大于B
6) comment:注释
(4) choice/endchoice
choice的作用,多选一,有点像MFC中的Radio控件。如:
choice
prompt "Memory split"
default VMSPLIT_3G
help
Select the desired split between kernel and user memory.
If you are not absolutely sure what you are doing, leave this
option alone!
config VMSPLIT_3G
bool "3G/1G user/kernel split"
config VMSPLIT_2G
bool "2G/2G user/kernel split"
config VMSPLIT_1G
bool "1G/3G user/kernel split"
endchoice
(5) source
source只是将另外一个Kconfig文件直接复制到当前位置而已。但它的作用也是明显的,可以将这个系统贯穿在一起。从开始位置arch/arm/Kconfig,来将整个系统都作为配置型
总结:
当我们进入了linux源码的根目录时,输入make menuconfig。假设,此时根目录已经存在.config,如果不存在,会自动生成。显然,在执行make menuconfig时,会自动调用scripts/Kconfig/mconf arch/arm/Kconfig开始系统的配置,那么arch/arm/Kconfig就是配置的起点。这个文件会通过source指令来调用其他目录 下的Kconfig文件,从而完成整体配置。这样,arch/arm/Kconfig就可以理解成main函数,而source指令就有点类似于 include。可以按照上面的语法,来分析Kconfig文件。
KBuild Makefile
---------------------
从Linux内核2.6开始,Linux内核的编译采用Kbuild系统,这同过去的编译系统有很大的不同,尤其对于Linux 内核模块的编译。在新的系统下,Linux编译系统会两次扫描Linux的Makefile:首先编译系统会读取Linux内核顶层的Makefile ,然后根据读到的内容第二次读取Kbuild的Makefile来编译Linux内核。
Linux内核Makefile分类:
1. Kernel Makefile
Kernel Makefile 位于Linux 内核源代码的顶层目录,也叫 Top Makefile 。它主要用于指定编译Linux Kernel 目标文件(vmlinux )和模块(module )。这编译内核或模块是,这个文件会被首先读取,并根据读到的内容配置编译环境变量。对于内核或驱动开发人员来说,这个文件几乎不用任何修改。截取Kernel Makefiel部分内容如下:
# The all: target is the default when no target is given on the
# command line.
# This allow a user to issue only 'make' to build a kernel including modules
# Defaults to vmlinux, but the arch makefile usually adds further targets
all: vmlinux
ifdef CONFIG_CC_OPTIMIZE_FOR_SIZE
KBUILD_CFLAGS += -Os $(call cc-disable-warning,maybe-uninitialized,)
else
KBUILD_CFLAGS += -O2
endif
include $(srctree)/arch/$(SRCARCH)/Makefile
...
# ---------------------------------------------------------------------------
# Modules
ifdef CONFIG_MODULES
# By default, build modules as well
all: modules
# Build modules
#
# A module can be listed more than once in obj-m resulting in
# duplicate lines in modules.order files. Those are removed
# using awk while concatenating to the final file.
PHONY += modules
modules: $(vmlinux-dirs) $(if $(KBUILD_BUILTIN),vmlinux) modules.builtin
$(Q)$(AWK) '!x[$$0]++' $(vmlinux-dirs:%=$(objtree)/%/modules.order) > $(objtree)/modules.order
@$(kecho) ' Building modules, stage 2.';
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.modpost
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.fwinst obj=firmware __fw_modbuild
modules.builtin: $(vmlinux-dirs:%=%/modules.builtin)
$(Q)$(AWK) '!x[$$0]++' $^ > $(objtree)/modules.builtin
%/modules.builtin: include/config/auto.conf
$(Q)$(MAKE) $(modbuiltin)=$*
...
# Where to locate arch specific headers
hdr-arch := $(SRCARCH)
KCONFIG_CONFIG ?= .config
export KCONFIG_CONFIG
# SHELL used by kbuild
CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
else if [ -x /bin/bash ]; then echo /bin/bash; \
else echo sh; fi ; fi)
HOSTCC = gcc
HOSTCXX = g++
HOSTCFLAGS = -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer
HOSTCXXFLAGS = -O2
...
# Make variables (CC, etc...)
AS = $(CROSS_COMPILE)as
LD = $(CROSS_COMPILE)ld
CC = $(CROSS_COMPILE)gcc
CPP = $(CC) -E
AR = $(CROSS_COMPILE)ar
NM = $(CROSS_COMPILE)nm
STRIP = $(CROSS_COMPILE)strip
OBJCOPY = $(CROSS_COMPILE)objcopy
OBJDUMP = $(CROSS_COMPILE)objdump
AWK = awk
GENKSYMS = scripts/genksyms/genksyms
INSTALLKERNEL := installkernel
DEPMOD = /sbin/depmod
PERL = perl
CHECK = sparse
CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise -Wno-return-void $(CF)
CFLAGS_MODULE =
AFLAGS_MODULE =
LDFLAGS_MODULE =
CFLAGS_KERNEL =
AFLAGS_KERNEL =
CFLAGS_GCOV = -fprofile-arcs -ftest-coverage
...
kernelrelease:
@echo "$(KERNELVERSION)$$($(CONFIG_SHELL) $(srctree)/scripts/setlocalversion $(srctree))"
kernelversion:
@echo $(KERNELVERSION)
2. ARCH Makefile
ARCH Makefile 位于ARCH/$(ARCH)/Makefile,是系统对应平台的Makefile。Kernel Top Makefile 会包含这个文件来指定平台相关信息,如下所示:
include $(srctree)/arch/$(SRCARCH)/Makefile
只有平台开发人员会关心这个文件。
3. Kbuild Makefile
Kbuild 系统使用Kbuild Makefile 来编译内核或模块。当Kernel Makefile 被解析完成后,Kbuild 会读取相关的Kbuild Makefile 进行内核或模块的编译。Kbuild Makefile 有特定的语法指定哪些编译进内核中、哪些编译为模块、及对应的源文件是什么等。内核及驱动开发人员需要编写这个Kbuild Makefile 文件。
Kbuild Makefile 的文件名不一定是 Makefile ,尽管推荐使用Makefile 这个名字。大多的Kbuild 文件的名字都是Makefile 。为了与其他Makefile 文件相区别,你也可以指定Kbuild Makefile 的名字为 Kbuild 。而且如果“Makefile ”和“Kbuild ”文件同时存在,则Kbuild 系统会使用“Kbuild ”文件。
1) 目标定义
Kbuild Makefile 的一个最主要功能就是指定编译什么,这个功能是通过下面两个对象指定的obj-? 和xxx-objs :
· obj-?
obj-?指定编译什么,怎么编译,其中的“?”可能是“y”或“m”,“y”指定把对象编译进内核中,“m”指定把对象编译为模块。语法如下:
obj-? = $(target).o
target为编译对象的名字。如果没有指定xxx-objs ,这编译这个对象需要的源文件就是$(target).c 或$(target).s 。如果指定了$(target)-objs ,则编译这个对象需要的源文件由$(target)-objs 指定,并且不能有$(target).c 或$(target).s 文件。
· xxx-objs
xxx-objs 指定了编译对象需要的文件,一般只有在源文件是多个时才需要它。
只要包含了这两行,Kbuild Makefile 就应该可以工作了。
2) 嵌套编译
有时一个对象可能嵌入到另一个对象的目录下,那个如何编译子目录下的对象呢? 其实很简单,只要指定obj_? 的对象为子目录的名字就可以了:
obj-? = $(sub_target)/
其中“? ”可以是“y”或“m”,$(sub_target) 是子目录名字。
3) 编译器选项
尽管在大多数情况下不需要指定编译器选项,有时我们还是需要指定一些编译选项的。
· ccflags-y, asflags-y and ldflags-y
这些编译选项用于指定cc 、as 和ld 的编译选项