配置Linux Kernel时make menuconfig执行流程分析

   在编译内核前,一般是根据已有的配置文件(一般在内核根目录下的arch/arm/configs/文件夹下,把该目录下的xxx_defconfig文件拷贝到内核根目录下,并重命名为.config)来进行编译; 或者需要先配置裁剪内核。

假设我们要基于一块ARM芯片的开发板配置裁剪内核时,在内核的根目录下运行:make ARCH=arm menuconfig命令后,会弹出如下配置界面:

当我们在内核的根目录下运行make ARM=arm menuconfig这条命令时,内核根目录下的Makefile是怎样被执行的呢?

回答这个问题之前,我们要先了解make这个工具的执行过程。GNU make找寻默认的Makefile规则是在当前目录下按顺序依次找三个文件 —“GNUmakefile”、“makefile”和“Makefile”,一旦找到就开始读取这个文件并执行。make menuconfig命令没有指定makefile文件,因此默认执行的是 make –f Makefile menuconfig,即执行$(srctree)/Makefile文件中目标menuconfig的相关规则。一般来说,make的最终目标是makefile中的第一个目标,而其它目标一般是由这个目标连带出来的。这是make的默认行为。

如果你的makefile中的第一个目标是由许多个目标组成,你可以指示make,让其完成你所指定的目标。要达到这一目的很简单,需在make命令后直接跟目标的名字就可以完成(如make clean)。任何在makefile中的目标都可以被指定成终极目标,但是 除了以“-”打头,或是包含了“=”的目标,因为有这些字符的目标,会被解析成命令行参数或是变量。甚至没有被我们明确写出来的目标也可以成为make的终极目标,也就是说,只要make可以找到其隐含规则推导规则,那么这个隐含目标同样可以被指定成终极目标。

当在Linux内核(内核版本为3.18.42)顶层目录执行”make  ARCH=arm  menuconfig”时,命令行对内核根目录下Makefile文件的ARCH这个变量赋值为arm ,并且指定了make的目标是menuconfig。“menuconfig”这个目标在根目录下的Makefile中找到的匹配的目标是“%config”,因此会执行如下的规则:


%config: scripts_basic outputmakefile FORCE

$(Q)$(MAKE) $(build)=scripts/kconfig [email protected]

上面的规则等价于:


menuconfig: scripts_basic outputmakefile FORCE

$(Q)$(MAKE) $(build)=scripts/kconfig menuconfig

“menuconfig”这个目标有三个依赖:scripts_basic、outputmakefile、FORCE。先来分析下“menuconfig”这个目标下的命令:$(Q) $(MAKE) $(build)=scripts/kconfig [email protected]

1、$(Q)

看下变量Q在Makefile的定义:


# Beautify output

# ---------------------------------------------------------------------------

#

# Normally, we echo the whole command before executing it. By making

# that echo $($(quiet)$(cmd)), we now have the possibility to set

# $(quiet) to choose other forms of output instead, e.g.

#

#         quiet_cmd_cc_o_c = Compiling $(RELDIR)/[email protected]

#         cmd_cc_o_c       = $(CC) $(c_flags) -c -o [email protected] $<

#

# If $(quiet) is empty, the whole command will be printed.

# If it is set to "quiet_", only the short version will be printed.

# If it is set to "silent_", nothing will be printed at all, since

# the variable $(silent_cmd_cc_o_c) doesn‘t exist.

#

# A simple variant is to prefix commands with $(Q) - that‘s useful

# for commands that shall be hidden in non-verbose mode.

#

#   $(Q)ln [email protected] :<

#

# If KBUILD_VERBOSE equals 0 then the above command will be hidden.

# If KBUILD_VERBOSE equals 1 then the above command is displayed.

#

# To put more focus on warnings, be less verbose as default

# Use ‘make V=1‘ to see the full commands

ifeq ("$(origin V)", "command line")

KBUILD_VERBOSE = $(V)

endif

ifndef KBUILD_VERBOSE

KBUILD_VERBOSE = 0

endif

ifeq ($(KBUILD_VERBOSE),1)

quiet =

Q =

else

quiet=quiet_

Q = @

endif

从上面的注释和Makefile语句可以看到,当在命令行传人V这个变量的值为1(V=1)时,就会使能quiet、Q变量的值为空,make在执行Makefile命令时就会向屏幕输出所执行的命令;当在命令行不传入V这个变量或者V的值为0(V=0)时,就会使能quiet=quiet_、Q= @,make在执行Makefile命令时就不会向屏幕输出所执行的命令。

2、$(MAKE)

MAKE是内嵌变量,其值为make。

3、$(build)

build这个变量是一个通用的变量,它定义在$(srctree)/scripts/Kbuild.include文件中:


###

# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=

# Usage:

# $(Q)$(MAKE) $(build)=dir

build := -f $(srctree)/scripts/Makefile.build obj

在内核的根目录下的Makefile包含了$(srctree)/scripts/Kbuild.include这个文件:


# We need some generic definitions (do not try to remake the file).

$(srctree)/scripts/Kbuild.include: ;

include $(srctree)/scripts/Kbuild.include

分析$(srctree)/scripts/Kbuild.include: ; 这条语句前我们先了解下make书写规则。规则的命令部分有两种书写方式:

a、目标、依赖描述和命令放在同一行,目标和依赖描述使用冒号(:)分隔开,在依赖文件列表后使用分号(;)把依赖文件列表和命令分开。

b、目标和依赖描述放在同一行,目标和依赖描述使用冒号(:)分隔开;命令行在目标、依赖描述的下一行。当作为独立的命令行时此行必须以[Tab]字符开始。在Makefile中,在第一个规则之后出现的所有以[Tab]字符开始的行都会被当作命令来处理。

$(srctree)/scripts/Kbuild.include: ; 这条语句使用的是第一种make书写规则,这条规则只有目标,没有依赖和命令。???因此make在执行这条规则的时候怎么执行???

include $(srctree)/scripts/Kbuild.include这条规则把$(srctree)/scripts/Kbuild.include这个文件包含到了内核根目录下的Makefile文件中。

从上面的分析可以知道build这个变量的值为-f $(srctree)/scripts/Makefile.build obj。

4、[email protected]

[email protected]是make的自动环变量,表示当前目标,即menuconfig。

下面来分析下scripts_basic、outputmakefile、FORCE这三个依赖:

1、FORCE

FORCE的定义为:


PHONY += FORCE

FORCE:

# Declare the contents of the .PHONY variable as phony.  We keep that

# information in a variable so we can use it in if_changed and friends.

.PHONY: $(PHONY)

从上面看到,FORCE 既没有依赖的规则,其底下也没有可执行的命令。如果一个规则没有命令或者依赖,并且它的目标不是一个存在的文件名。在执行此规则时,目标总会被认为是最新的。就是说:这个规则一旦被执行,make就认为它的目标已经被更新过。这样的目标在作为一个规则的依赖时,因为依赖总被认为被更新过,因此作为依赖所在的规则中定义的命令总会被执行。FORCE所在规则为空,也是什么都不做。FORCE被定义为一个伪目标,所以它作为依赖时总是被认为是最新的(比目标新),故有FORCE作为依赖的目标每次make时必然会重新生成,在这里FORCE伪目标的规则命令为空,故FORCE在Kbuild体系中,就是相当于是一个关键字,如果我们想要某个目标每次make的时候都一定会被重新生成,就把FORCE写为该目标的依赖。

2、scripts_basic

scripts_basic的定义为:


# Basic helpers built in scripts/

PHONY += scripts_basic

scripts_basic:

$(Q)$(MAKE) $(build)=scripts/basic

$(Q)rm -f .tmp_quiet_recordmcount

scripts_basic这个目标没有依赖,且scripts_basic也不是一个存在的文件,因此scripts_basic所定义的命令总会被执行。上述scripts_basic的定义等价为:


# Basic helpers built in scripts/

PHONY += scripts_basic

scripts_basic:

$(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic

$(Q) rm -f .tmp_quiet_recordmcount

$(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/basic这条命令指定了执行的是$(srctree)/scripts/Makefile.build这个Makefile,传递的参数是obj=scripts/basic。

接下来我们来分析下$(srctree)/scripts/Makefile.build这个Makefile文件。

obj这个变量传递进$(srctree)/scripts/Makefile.build中的src这个变量:


src := $(obj)

即src := scripts/basic。

$(srctree)/scripts/Makefile.build把src (即scripts/basic)目录下的Makefile包含进来(如果有Kbuild则包含Kbuild)


# The filename Kbuild has precedence over Makefile

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

include $(kbuild-file)

$(srctree)/scripts/Makefile.build也包含了scripts/Makefile.lib这个文件:


# If the save-* variables changed error out

ifeq ($(KBUILD_NOPEDANTIC),)

ifneq ("$(save-cflags)","$(CFLAGS)")

$(error CFLAGS was changed in "$(kbuild-file)". Fix it to use ccflags-y)

endif

endif

include scripts/Makefile.lib

在$(srctree)/scripts/Makefile.build这个Makefile文件中的第一个目标是:


__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \

$(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \

$(subdir-ym) $(always)

@:

KBUILD_BUILTIN、KBUILD_MODULES在顶层Makefile中定义:


# Decide whether to build built-in, modular, or both.

# Normally, just do built-in.

KBUILD_MODULES :=

KBUILD_BUILTIN := 1

# If we have only "make modules", don‘t compile built-in objects.

# When we‘re building modules with modversions, we need to consider

# the built-in objects during the descend as well, in order to

# make sure the checksums are up to date before we record them.

ifeq ($(MAKECMDGOALS),modules)

KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)

endif

# If we have "make <whatever> modules", compile modules

# in addition to whatever we do anyway.

# Just "make" or "make all" shall build modules as well

ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)

KBUILD_MODULES := 1

endif

ifeq ($(MAKECMDGOALS),)

KBUILD_MODULES := 1

endif

export KBUILD_MODULES KBUILD_BUILTIN

export KBUILD_CHECKSRC KBUILD_SRC KBUILD_EXTMOD

通过export关键字定义,使在makefile递归进行时,这两个变量被传递进子makefile。

这里:

KBUILD_MODULES :=

KBUILD_BUILTIN := 1

KBUILD_BUILTIN和KBUILD_MODULES在顶层makefile文件中定义赋值后,就没有被改变过。所以此处__build目标的依赖就是$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)。

__build规则展开为:


__build: $(builtin-target) $(lib-target) $(extra-y) $(subdir-ym) $(always)

@:

规则的命令是一个冒号命令”:”,冒号(:)命令是bash的内建命令,通常把它看作true命令。bash的help解释(help :)为:No effect; the command does nothing. A zero exit code is returned.(没有效果,该命令是空操作,退出状态总是0)。

__build的依赖除了$(always),$(builtin-target) $(lib-target) $(extra-y) $(subdir-ym)这些变量在$(srctree)/scripts/basic/Makefile中没有定义,因此builtin-target、lib-target、extra-y、subdir-ym都为空串,只有always有值。always在scripts/kconfig/Makefile中定义为dochecklxdialog,而dochecklxdialog目标所在规则的注释写着# Check that we have the required ncurses stuff installed for lxdialog (menuconfig)。也就是说,__build目标的依赖dochecklxdialog是用来检查生成配置对话框所需的ncurses库是不是已经安装在本机了,如果没有安装,make过程会报错退出。因此在make menuconfig前,我们要保证该库已经被安装在本地。

3、outputmakefile

outputmakefile在内核根目录下的Makefile中的定义为:


PHONY += outputmakefile

# outputmakefile generates a Makefile in the output directory, if using a

# separate output directory. This allows convenient use of make in the

# output directory.

outputmakefile:

ifneq ($(KBUILD_SRC),)

$(Q)ln -fsn $(srctree) source

$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \

$(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)

endif

由于这里KBUILD_SRC为空,所以这个脚本并不会被执行。

到这里我们分析完了menuconfig的依赖,在处理完这些依赖后就开始执行规则的命令:把$(Q)$(MAKE) $(build)=scripts/kconfig [email protected]这条命令展开:

$(Q) make -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig menuconfig

这条命令指定要执行scripts/Makefile.build这个makefile文件。

在$(srctree)/scripts/Makefile.build中src变量的定义为:


src := $(obj)

PHONY := __build

__build:

# Init all relevant variables used in kbuild files so

# 1) they have correct type

# 2) they do not inherit any value from the environment

obj-y :=

obj-m :=

lib-y :=

lib-m :=

always :=

targets :=

subdir-y :=

subdir-m :=

EXTRA_AFLAGS   :=

EXTRA_CFLAGS   :=

EXTRA_CPPFLAGS :=

EXTRA_LDFLAGS  :=

asflags-y  :=

ccflags-y  :=

cppflags-y :=

ldflags-y  :=

subdir-asflags-y :=

subdir-ccflags-y :=

# Read auto.conf if it exists, otherwise ignore

-include include/config/auto.conf

include scripts/Kbuild.include

# For backward compatibility check that these variables do not change

save-cflags := $(CFLAGS)

# The filename Kbuild has precedence over Makefile

kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))

kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)

include $(kbuild-file)

由make -f scripts/Makefile.build obj=scripts/kconfig menuconfig可知,src值为scripts/kconfig,与/%的字串模式相符,因此$(filter /%,$(src))就是scripts/kconfig,故kbuild-dir就被赋值为$(src),即kbuild-dir为scripts/kconfig。由于scripts/kconfig目录下并没有Kbuild文件,因此函数$(wildcard $(kbuild-dir)/Kbuild)查找失败,返回为空,从而kbuild-file值被赋为$(kbuild-dir)/Makefile,也即scripts/kconfig/Makefile。接着,

scripts/Makefile.build包含scripts/kconfig/Makefile文件(include $(kbuild-file))。目标menuconfig定义在scripts/kconfig/Makefile中,找到menuconfig目标后,然后执行以menuconfig为目标的规则:


PHONY += oldconfig xconfig gconfig menuconfig config silentoldconfig update-po-config \

localmodconfig localyesconfig

ifdef KBUILD_KCONFIG

Kconfig := $(KBUILD_KCONFIG)

else

Kconfig := Kconfig

endif

# We need this, in case the user has it in its environment

unexport CONFIG_

... ...

menuconfig: $(obj)/mconf

$< $(Kconfig)

... ...

menuconfig目标的规则的命令是$< $(Kconfig),展开为$(obj)/mconf $(Kconfig), obj的值为scripts/kconfig,因为没有定义KBUILD_KCONFIG,而且SRCARCH之前已被赋值为$(ARCH),即SRCARCH为arm,因此Kconfig的值为arch/arm/Kconfig。故menuconfig目标的规则的命令为scripts/kconfig/mconf  arch/arm/Kconfig。mconf在这里实际上是scripts/kconfig目录下的一个可执行文件,此条命令里arch/arm/Kconfig字符串作为命令行参数传入该可执行文件运行,该可执行文件实际上就是依据arch/arm/Kconfig文件提供的菜单配置,生成配置界面。

NOTE: 这里为什么说scripts/kconfig/mconf就是一个可执行文件呢?继续往下看scripts/kconfig/Makefile中的内容:


lxdialog := lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o

lxdialog += lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o

conf-objs   := conf.o  zconf.tab.o

mconf-objs     := mconf.o zconf.tab.o $(lxdialog)

nconf-objs     := nconf.o zconf.tab.o nconf.gui.o

kxgettext-objs  := kxgettext.o zconf.tab.o

qconf-cxxobjs   := qconf.o

qconf-objs  := zconf.tab.o

gconf-objs  := gconf.o zconf.tab.o

hostprogs-y := conf nconf mconf kxgettext qconf gconf

clean-files := qconf.moc .tmp_qtcheck .tmp_gtkcheck

clean-files += zconf.tab.c zconf.lex.c zconf.hash.c gconf.glade.h

clean-files     += config.pot linux.pot

# Check that we have the required ncurses stuff installed for lxdialog (menuconfig)

PHONY += $(obj)/dochecklxdialog

$(addprefix $(obj)/,$(lxdialog)): $(obj)/dochecklxdialog

$(obj)/dochecklxdialog:

$(Q)$(CONFIG_SHELL) $(check-lxdialog) -check $(HOSTCC) $(HOST_EXTRACFLAGS) $(HOSTLOADLIBES_mconf)

always := dochecklxdialog

如果在编译内核的过程中,需要现编译出一些可执行文件供内核编译阶段使用,就需要借助Kbuild框架的本机程序支持的特性。Kbuild 框架中,专门使用hostprogs-y变量来指示在内核编译阶段需要使用的一些可执行文件,通过hostprogs-y += mconf,就向make程序指明mconf是一个编译阶段需要使用的可执行文件。另外,Kbuild框架使用-objs后缀来指明相应的可执行文件需要通过多个目标文件来链接生成,mconf-objs    := mconf.o zconf.tab.o $(lxdialog)就是向make指明,mconf文件是由mconf.o zconf.tab.o lxdialog/checklist.o lxdialog/util.o lxdialog/inputbox.o lxdialog/textbox.o lxdialog/yesno.o lxdialog/menubox.o链接生成的。再有,未明确写明生成规则时,Kbuild框架默认.o文件是由同名.c或.S文件编译生成的。我们在scripts\kconfig以及scripts\kconfig\lxdialog目录下可以找到前边8个.o文件的同名.c文件。

保存配置信息后会在内核根目录下生成一个.config文件,该文件保存了所做的内核配置信息。

时间: 2024-08-06 16:04:17

配置Linux Kernel时make menuconfig执行流程分析的相关文章

决Ubuntu使用`make menuconfig`配置Linux 内核时,出现缺少&#39;ncurses-devel&#39;库支持。

*** Unable to find the ncurses libraries or the *** required header files. *** 'make menuconfig' requires the ncurses libraries. *** *** Install ncurses (ncurses-devel) and try again. ***  1. 问题状况 一般情况下使用系统自带的软件管理器apt-get就可以安装了(`sudo apt-get install 

Linux Kernel文件系统写I/O流程代码分析(一)

Linux Kernel文件系统写I/O流程代码分析(一) 在Linux VFS机制简析(二)这篇博客上介绍了struct address_space_operations里底层文件系统需要实现的操作,实际编码过程中发现不是那么清楚的知道这里面的函数具体是干啥,在什么时候调用.尤其是写IO相关的操作,包括write_begin, write_end, writepage, writepages, direct_IO以及set_page_dirty等函数指针. 要搞清楚这些函数指针,就需要纵观整个

基于linux与busybox的reboot命令流程分析

http://www.xuebuyuan.com/736763.html 基于Linux与Busybox的Reboot命令流程分析 *************************************************************************************************************************** 作者:EasyWave                                                

转 深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇) 最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, 好啦,鄙人不喜欢口水话,还是直接上干活吧: 1. SqlSessionFactory 与 SqlSession. 通过前面的章节对于mybatis 的介绍及使用,大家都能体会到SqlSession的重要性了吧, 没错,从表面上来看,

Hive SQL执行流程分析

转自 http://www.tuicool.com/articles/qyUzQj 最近在研究Impala,还是先回顾下Hive的SQL执行流程吧. Hive有三种用户接口: cli (Command line interface) bin/hive或bin/hive –service cli 命令行方式(默认) hive-server/hive-server2 bin/hive –service hiveserver 或bin/hive –service hiveserver2 通过JDBC/

ThinkPHP 框架执行流程分析

总体来说,应用的流程涉及到几个文件:Index.phpThinkPHP.phpThink.class.phpApp.class.phpDispatcher.class.phpThinkPHP/Mode/common.phpReadHtmlBehavior.class.phpRoute.class.phpHook.class.phpContentReplaceBehavior.class.phpWriteHtmlCacheBehavior.class.php ThinkPHP框架开发的应用的标准执

深入浅出Mybatis系列(十)---SQL执行流程分析(源码篇)

原文地址:http://www.cnblogs.com/dongying/p/4142476.html 最近太忙了,一直没时间继续更新博客,今天忙里偷闲继续我的Mybatis学习之旅.在前九篇中,介绍了mybatis的配置以及使用, 那么本篇将走进mybatis的源码,分析mybatis 的执行流程, 好啦,鄙人不喜欢口水话,还是直接上干活吧: 1. SqlSessionFactory 与 SqlSession. 通过前面的章节对于mybatis 的介绍及使用,大家都能体会到SqlSession

spark-sql执行流程分析

spark-sql 架构 图1 图1是sparksql的执行架构,主要包括逻辑计划和物理计划几个阶段,下面对流程详细分析. sql执行流程 总体流程 parser:基于antlr框架对 sql解析,生成抽象语法树 变量替换,通过正则表达式找出符合规则的字符串,替换成系统缓存环境的变量 SQLConf中的`spark.sql.variable.substitute`,默认是可用的:参考` SparkSqlParser` parser:将antlr的tree转成spark catalyst的Logi

Java Servlet(十二):Servlet、Listener、Filter之间的执行流程分析

时隔几年后,看到本系列文章讲解的内容缺少了不少内容:周末无事分析了Spring Security是如何被集成到Web Servlet(SpringMVC)时,需要重新理清Filter.Listener.Servlet(SpringMVC#DispatcherServlet)之间的执行顺序,于是就有了本篇文章.这个话题是Web Servlet学习中的一个重点,弄清它们之间的执行流程,有助于理解SpringMVC.Spring Security这些框架是否如何与Web Servlet集成到一起. 原