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

来源:互联网 发布:sketch软件 编辑:程序博客网 时间:2024/05/21 22:45


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

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

clip_image002

   当我们在内核的根目录下运行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) (Q)(MAKE)$(build)=scripts/kconfig $@

上面的规则等价于:

menuconfig: scripts_basic outputmakefile FORCE

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

“menuconfig”这个目标有三个依赖:scripts_basicoutputmakefileFORCE。先来分析下“menuconfig”这个目标下的命令:(Q) (Q)(MAKE)$(build)=scripts/kconfig $@

   1$(Q)

看下变量QMakefile的定义:

# 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)/ (RELDIR)/@

#        cmd_cc_o_c       = (CC) (CC)(c_flags) -c -o @ @<

#

# 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 (Q)ln@ :<

#

# 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这个变量的值为1V=1)时,就会使能quietQ变量的值为空,make在执行Makefile命令时就会向屏幕输出所执行的命令;当在命令行不传入V这个变量或者V的值为0V=0)时,就会使能quiet=quiet_Q= @make在执行Makefile命令时就不会向屏幕输出所执行的命令。

   2$(MAKE)

MAKE是内嵌变量,其值为make

   3$(build)

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

###

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

# Usage:

#(Q) (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$@

   $@make的自动环变量,表示当前目标,即menuconfig

   

下面来分析下scripts_basicoutputmakefileFORCE这三个依赖:

1FORCE

   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伪目标的规则命令为空,故FORCEKbuild体系中,就是相当于是一个关键字,如果我们想要某个目标每次make的时候都一定会被重新生成,就把FORCE写为该目标的依赖。

2scripts_basic

   scripts_basic的定义为:

# Basic helpers built in scripts/

PHONY += scripts_basic

scripts_basic:

   (Q) (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.buildsrc (scripts/basic)目录下的Makefile包含进来(如果有Kbuild则包含Kbuild

# The filename Kbuild has precedence over Makefile

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

kbuild-file:= (if (if(wildcard(kbuilddir)/Kbuild), (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)")

               (errorCFLAGSwaschangedin" (errorCFLAGSwaschangedin"(kbuild-file)". Fix it to use ccflags-y)

       endif

endif

 

include scripts/Makefile.lib

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

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

    (if (if(KBUILD_MODULES),(objm) (obj−m)(modorder-target)) \

    $(subdir-ym)$(always)

    @:

KBUILD_BUILTINKBUILD_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 (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 ((filterall a llmodules, (filterallallmodules,(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_BUILTINKBUILD_MODULES在顶层makefile文件中定义赋值后,就没有被改变过。所以此处__build目标的依赖就是(builtintarget) (builtin−target)(lib-target)(extray) (extra−y)(subdir-ym) $(always)

__build规则展开为:

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

    @:

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

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

 

3outputmakefile

   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) (Q)(CONFIG_SHELL)$(srctree)/scripts/mkmakefile \

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

endif

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

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

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

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

   $(srctree)/scripts/Makefile.buildsrc变量的定义为:

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 (if(filter /%,(src)), (src)),(src),(srctree)/ (srctree)/(src))

kbuild-file:= (if (if(wildcard(kbuilddir)/Kbuild), (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/ (filter/(src))就是scripts/kconfig,故kbuild-dir就被赋值为$(src),即kbuild-dirscripts/kconfig。由于scripts/kconfig目录下并没有Kbuild文件,因此函数(wildcard (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 (obj)/mconf(Kconfig), obj的值为scripts/kconfig,因为没有定义KBUILD_KCONFIG,而且SRCARCH之前已被赋值为$(ARCH),即SRCARCHarm,因此Kconfig的值为arch/arm/Kconfig。故menuconfig目标的规则的命令为scripts/kconfig/mconf arch/arm/Kconfigmconf在这里实际上是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 (addprefix(obj)/,$(lxdialog)):$(obj)/dochecklxdialog

$(obj)/dochecklxdialog:

   (Q) (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文件,该文件保存了所做的内核配置信息。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 常州医保卡丢了怎么办 驾驶证违章罚单丢了怎么办 身份证被别人办了信用卡怎么办 被别人办了信用卡怎么办 考驾照体检忘带身份证怎么办 c证扣12分怎么办新规 c照12分不够扣怎么办 扣了18分怎么办一次性 c照累计扣12分怎么办 车辆超速扣12分怎么办 一次超速扣12分怎么办 分扣了罚款未交怎么办 c照一次扣12分怎么办 人在外地身份证到期了怎么办 手机进水了屏幕不亮怎么办 北京一证通过期怎么办 小米6音量键进水怎么办 考驾照怕过不了怎么办 学车对车没感觉怎么办 居住证到期2个月怎么办 生育险差一个月怎么办 驾照扣了38分怎么办 新疆转入山东上学怎么办手续 驾照过日期换证怎么办 机动车被扣24分怎么办 车辆被扣24分怎么办 现在深圳牌十年老车怎么办? 护士证过期4年了怎么办 护士资格证延续注册过期了怎么办 护士资格证过期没注册怎么办 护士资格证注册时间过期怎么办 辅警体检视力不行怎么办 护士延续注册体检怀孕怎么办 护士资格证没有延续注册怎么办 申请信用卡没有座机号码怎么办 网上申请信用卡没有座机号码怎么办 我叫上门服务被骗了怎么办 上门服务被骗了3000多怎么办 微信被骗9000元怎么办 奥迪a8气囊灯亮怎么办 驾考站岗迟到了怎么办