欢迎您访问365答案网,请分享给你的朋友!
生活常识 学习资料

linux内核makemenuconfig执行过程

时间:2023-06-28

  近两年一直有着写linux内核相关的想法,比如从Makefile执行 make menuconfig、make,到x86架构的引导启动过程。由于网上有较多相关的文章,便有些羞涩于自己动手写。现在想着无论自己水平如何,用自己的思路去追溯代码并记录代码,应该强于融入别人的思想。
  本次采用5.9版本内核,通过代码分析、strace跟踪等形式按照自己的理解来写,写的不对的地方请跳过。

make menuconfig

  示例在x86_64架构的Centos8.2虚拟机中进行,现在开始进入正题。
  打开Makefile文件:

# SPDX-License-Identifier: GPL-2.0VERSION = 5PATCHLEVEL = 9SUBLEVEL = 0EXTRAVERSION =NAME = Kleptomaniac Octopus

  在开始的地方定义了linux版本信息,这些将在后面多次使用。

  …

$(if $(filter __%, $(MAKECMDGOALS)), $(error targets prefixed with '__' are only for internal use))

  MAKECMDGOALS属执行make时传入的参数(暂存到这个变量),比如输入make test,将会匹配到,执行 $(error targets prefixed with ‘__’ are only for internal use)),导致make停止执行

  …

PHONY := __all __all:

  当make时没有传入参数规则,将默认执行__all流程

  …

ifneq ($(sub_make_done),1)

  判断子级make配置是否完成,如果还没有完成执行条件内的语句

  …

unexport LC_ALLLC_COLLATE=CLC_NUMERIC=Cexport LC_COLLATE LC_NUMERIC

  不让LC_ALL变量传入子级make(使用),传入LC_COLLATE LC_NUMERIC

  …

ifeq ("$(origin V)", "command line") KBUILD_VERBOSE = $(V)endififndef KBUILD_VERBOSE KBUILD_VERBOSE = 0endif

  如果KBUILD_VERBOSE等于0,则上述命令将被隐藏;如果KBUILD_VERBOSE等于1,则显示上述命令;如果KBUILD_VERBOSE等于2,请给出重建每个目标的原因。
  这里默认执行KBUILD_VERBOSE = 0(quiet=quiet_,Q = @,只打印执行结果)

  …

export quiet Q KBUILD_VERBOSE

  quiet Q KBUILD_VERBOSE变量传入子级make(使用)

  …

ifeq ("$(origin O)", "command line") KBUILD_OUTPUT := $(O)endififneq ($(KBUILD_OUTPUT),)# Make's built-in functions such as $(abspath ...), $(realpath ...) cannot# expand a shell special character '~'、We use a somewhat tedious way here.abs_objtree := $(shell mkdir -p $(KBUILD_OUTPUT) && cd $(KBUILD_OUTPUT) && pwd)$(if $(abs_objtree),, $(error failed to create output directory "$(KBUILD_OUTPUT)"))

  通过(make) O=指定输出目录或者通过KBUILD_OUTPUT变量,O=优先级高级KBUILD_OUTPUT变量,默认值为当前目录

  …

abs_srctree := $(realpath $(dir $(lastword $(MAKEFILE_LIST))))

  abs_srctree得到Makefile绝对路径(由MAKEFILE_LIST变量经过 lastword、dir、realpath执行得到)

  …

ifneq ($(abs_srctree),$(abs_objtree))MAKEFLAGS += --include-dir=$(abs_srctree)need-sub-make := 1endif

  在make menuconfig中abs_srctree与abs_objtree相同,未设置need-sub-make,need-sub-make=0

  …

this-makefile := $(lastword $(MAKEFILE_LIST))

  this-makefile=Makefile

  …

export sub_make_done := 1

  子级make配置完成

  …

ifeq ($(need-sub-make),1)PHONY += $(MAKECMDGOALS) __sub-make$(filter-out $(this-makefile), $(MAKECMDGOALS)) __all: __sub-make @:__sub-make: $(Q)$(MAKE) -C $(abs_objtree) -f $(abs_srctree)/Makefile $(MAKECMDGOALS)...

  上述说过need-sub-make=0,这里的分支将不执行(不执行子级Makefile)

  …

ifeq ($(need-sub-make),)MAKEFLAGS += --no-print-directory

  进入分支,设置输出信息

  …

ifeq ("$(origin C)", "command line") KBUILD_CHECKSRC = $(C)endififndef KBUILD_CHECKSRC KBUILD_CHECKSRC = 0endif

  使用“make C=1”只允许检查重新编译的文件,使用“make C=2”来启用对所有源文件的检查;这里为0,执行KBUILD_CHECKSRC = 0

  …

ifeq ("$(origin M)", "command line") KBUILD_EXTMOD := $(M)endif$(if $(word 2, $(KBUILD_EXTMOD)), $(error building multiple external modules is not supported))export KBUILD_CHECKSRC KBUILD_EXTMOD

  设置外部模块(.ko)目录

  …

extmod-prefix = $(if $(KBUILD_EXTMOD),$(KBUILD_EXTMOD)/)

  extmod-prefix默认为空

  …

ifeq ($(abs_srctree),$(abs_objtree)) # building in the source tree srctree := . building_out_of_srctree :=else ifeq ($(abs_srctree)/,$(dir $(abs_objtree))) # building in a subdirectory of the source tree srctree := .. else srctree := $(abs_srctree) endif building_out_of_srctree := 1endif

  abs_srctree与abs_objtree目录相同,srctree为当前目录,building_out_of_srctree为空

  …

objtree := .VPATH := $(srctree)

  objtree为当前目录,VPATH为空

  …

export building_out_of_srctree srctree objtree VPATH

  building_out_of_srctree srctree objtree VPATH变量传入子级make(使用)

  …

version_h := include/generated/uapi/linux/version.hold_version_h := include/linux/version.hclean-targets := %clean mrproper cleandocs...

  定义version_h、old_version_h路径,clean-targets清除规则等配置

  …

ifeq ($(KBUILD_EXTMOD),) ifneq ($(filter %config,$(MAKECMDGOALS)),) config-build := 1 ifneq ($(words $(MAKECMDGOALS)),1) mixed-build := 1 endif endifendif

  默认config-build := 1

  …

include scripts/Kbuild.include# Read KERNELRELEASE from include/config/kernel.release (if it exists)KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)KERNELVERSION = $(VERSION)$(if $(PATCHLEVEL),.$(PATCHLEVEL)$(if $(SUBLEVEL),.$(SUBLEVEL)))$(EXTRAVERSION)export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSIONinclude scripts/subarch.include

  包含Kbuild.include,KERNELRELEASE和KERNELVERSION为5.9.0,包含subarch.include

  …

ARCH ?= $(SUBARCH)

  获取当前系统架构,默认x86

  …

KCONFIG_ConFIG ?= .config

  设置KCONFIG_ConFIG=.config(配置文件保存名称)

  …

export KCONFIG_ConFIGexport KBUILD_DEFConFIG := defconfig

  KCONFIG_ConFIG KBUILD_DEFCONFIG变量传入子级make(使用),KBUILD_DEFCONFIG用于make defconfig

  …

...USERINCLUDE := -I$(srctree)/arch/$(SRCARCH)/include/uapi -I$(objtree)/arch/$(SRCARCH)/include/generated/uapi -I$(srctree)/include/uapi -I$(objtree)/include/generated/uapi -include $(srctree)/include/linux/kconfig.h

  跳过gcc等一些编译相关信息,USERINCLUDE为-I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h

  …

LINUXINCLUDE := -I$(srctree)/arch/$(SRCARCH)/include -I$(objtree)/arch/$(SRCARCH)/include/generated $(if $(building_out_of_srctree),-I$(srctree)/include) -I$(objtree)/include $(USERINCLUDE)

  LINUXINCLUDE为-I./arch/x86/include -I./arch/x86/include/generated -I./include -I./arch/x86/include/uapi -I./arch/x86/include/generated/uapi -I./include/uapi -I./include/generated/uapi -include ./include/linux/kconfig.h

  …

export KBUILD_LDS_MODULE := $(srctree)/scripts/module-common.lds...export KBUILD_AFLAGS_KERNEL KBUILD_CFLAGS_KERNEL

  导入较多变量供子级make使用

  …

PHONY += outputmakefile

  PHONY为__all scripts_basic outputmakefile

  …

PHONY += scripts_basicscripts_basic: $(Q)$(MAKE) $(build)=scripts/basic $(Q)rm -f .tmp_quiet_recordmcount

  执行scripts_basic内分支(__all scripts_basic outputmakefile,这三个为需要执行的分支)

  …

outputmakefile:

  执行outputmakefile内分支,make menuconfig只是经过一下这里

  …

%config: outputmakefile scripts_basic FORCE $(Q)$(MAKE) $(build)=scripts/kconfig $@

  执行scripts/kconfig/Makefile文件

  …

$(foreach f, mconf.o $(lxdialog), $(eval HOSTCFLAGS_$f = $$(shell 、$(obj)/mconf-cfg && echo $$$$cflags)))

  编译mconf.c,lxdialog/checklist.c lxdialog/inputbox.c lxdialog/menubox.c lxdialog/textbox.c lxdialog/util.c lxdialog/yesno.c,生成mconf工具(menuconfig用到的视图工具,参考mconf源码)

  …

$(obj)/%conf-cfg: $(src)/%conf-cfg.sh FORCE $(call filechk,conf_cfg)

  在scripts/kconfig/文件夹内更新mconf-cfg脚本

  …

menuconfig: $(obj)/mconf $< $(silent) $(Kconfig)

  到这里,menuconfig编译过程分析完成了,在根Makefile设置变量及运行条件,然后跳转到scripts/kconfig/生成mconf,通过scripts/kconfig/mconf Kconfig启动视图界面。

Copyright © 2016-2020 www.365daan.com All Rights Reserved. 365答案网 版权所有 备案号:

部分内容来自互联网,版权归原作者所有,如有冒犯请联系我们,我们将在三个工作时内妥善处理。