Linux Kernel 编译流程 (一)

1 .config 文件产生

  • 研究Linux Kernel .config文件的产生, 添加自己的配置
  • Linux Kernel 4.18.20
  • Source Insight 3.5
  • Ubuntu 18.04
  • arm-linux-gnueabi-xxx

1.1 find %config

当我们执行sudo make ARCH=arm vexpress_defconfig命令行后,make会进入顶层Makefile文件,执行下面的语句. 而%config依赖scripts_basicouputmakefile, 所以我问首先去看scripts_basic

#SRCARCH := arm
SRCARCH 	:= $(ARCH)
#include arch/arm/Makefile
include arch/$(SRCARCH)/Makefile
%config: scripts_basic outputmakefile FORCE	
	$(Q)$(MAKE) $(build)=scripts/kconfig $@

1.2 find scripts_basic

在顶层Makefile中会发现如下代码段

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

make 直接执行命令行$(Q)$(MAKE) $(build)=scripts/basic, 展开后得到下面的片段,默认目标是all

#build := -f $(srctree)/scripts/Makefile.build obj
$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/basic
1.2.1 进入Makefile.build 执行
  • 包含 ./tools/build/Build
  • 包含scripts/Makefile.host

Makefile.build, 展开后得到下面的语句.其中dir=.中的".",是make进入Makefile.build的目录./tools/build/

PHONY := __build
__build:
src := scripts/basic
include scripts/Kbuild.include

# For backward compatibility check that these variables do not change
save-cflags := $(CFLAGS)

#kbuild-dir := ./scripts/basic
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
#kbuild-file := ./scripts/basic/Makefile
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include ./scripts/basic/Makefile
include $(kbuild-file)

ifneq ($(hostprogs-y)$(hostprogs-m)$(hostlibs-y)$(hostlibs-m)$(hostcxxlibs-y)$(hostcxxlibs-m),)
include scripts/Makefile.host
endif

__build: $(if $(KBUILD_BUILTIN),$(builtin-target) $(lib-target) $(extra-y)) \
	 $(if $(KBUILD_MODULES),$(obj-m) $(modorder-target)) \
	 $(subdir-ym) $(always)
	@:

我们查看下./scripts/basic/Makefile内容

hostprogs-y	:= fixdep
hostprogs-$(CONFIG_BUILD_BIN2C)     += bin2c
always		:= $(hostprogs-y)

#scripts/basic/bin2c: scripts/basic/fixdep
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

从上面我们得知hostprogs-y := fixdep, 不为空,接着继续执行命令行.include scripts/Makefile.host

ifneq ($(hostprogs-y)$(hostprogs-m)$(hostlibs-y)$(hostlibs-m)$(hostcxxlibs-y)$(hostcxxlibs-m),)
include scripts/Makefile.host
endif
1.2.2产生fixdep

当Make将Makefile.hostMakefile包含进来后,就会执行默认的目标__build, 而__build 依赖 $(always), 即fixdep,
而Makefile.host就是去产生fixdep

__hostprogs := $(sort $(hostprogs-y) $(hostprogs-m))								
# C code
# host-csingle := fixdep
host-csingle	:= $(foreach m,$(__hostprogs), \
			$(if $($(m)-objs)$($(m)-cxxobjs)$($(m)-sharedobjs),,$(m)))							
#host-csingle := scripts/basic/fixdep
host-csingle	:= $(addprefix $(obj)/,$(host-csingle))

hostc_flags    = -Wp,-MD,$(depfile) $(__hostc_flags)

#####
# Compile programs on the host

# Create executable from a single .c file
# host-csingle -> Executable
quiet_cmd_host-csingle 	= HOSTCC  $@
      cmd_host-csingle	= $(HOSTCC) $(hostc_flags) -o $@ $< \
	  	$(HOST_LOADLIBES) $(HOSTLOADLIBES_$(@F))
#产生fixdep	  	
$(host-csingle): $(obj)/%: $(src)/%.c FORCE											
	$(call if_changed_dep,host-csingle)

1.3 find outputmakefile

当fixdep完了后,make就返回执行第二个依赖outputmakefile ,目前没有对KBUILD_SRC赋值,所以没有执行任何语句

outputmakefile:
ifneq ($(KBUILD_SRC),)
	$(Q)ln -fsn $(srctree) source
	$(Q)$(CONFIG_SHELL) $(srctree)/scripts/mkmakefile \
	    $(srctree) $(objtree) $(VERSION) $(PATCHLEVEL)
endif

1.4 执行命令行

当make将所有的依赖scripts_basicoutputmakefile都产生了,就去执行命令行$(Q)$(MAKE) $(build)=scripts/kconfig $@,展开后得到, 目标是vexpress_defconfig

$(Q)$(MAKE) -f $(srctree)/scripts/Makefile.build obj=scripts/kconfig smdk5250_defconfig
1.4.1 进入Makefile.build
  • 包含scripts/kconfig/Makefile
  • 包含scripts/Makefile.host
    进入Makefile.build后,展开后得到,下面的语句
PHONY := __build
__build:
src := scripts/kconfig
include scripts/Kbuild.include

# For backward compatibility check that these variables do not change
save-cflags := $(CFLAGS)

#kbuild-dir := ./scripts/kconfig
kbuild-dir := $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
#kbuild-file := ./scripts/kconfig/Makefile
kbuild-file := $(if $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/Kbuild,$(kbuild-dir)/Makefile)
#include ./scripts/kconfig/Makefile
include $(kbuild-file)

ifneq ($(hostprogs-y)$(hostprogs-m),)
include scripts/Makefile.host
endif

从上面可以看出需要scripts/kconfig/Makefile, 其内容如下, 这里的SRCARCH:=arm

ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif

%_defconfig: $(obj)/conf
	@echo "eric_debug----------------start "$@
	#scripts/kconfig/conf --defconfig=arch/arm/configs/vexpress_defconfig Kconfig
	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
	@echo "eric_debug----------------end "$@
	
conf-objs	:= conf.o  zconf.tab.o
hostprogs-y := conf nconf mconf kxgettext qconf gconf

可以看出%_defconfig依赖 conf,所以应该首先产生conf, conf的产生会促使nconf mconf kxgettext qconf gconf的产生,其原理和fixdep产生一样,都需要借助Makefile.host,不做解释了

1.4.2 执行scripts/kconfig/Makefile中命令行

当conf产生后就需要执行命令行$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig), 展开后得到

scripts/kconfig/conf  --defconfig=arch/arm/configs/vexpress_defconfig Kconfig

我们分析下此时此刻的conf.c


NOTE: getopt_long method

int main(int ac, char **av)
{
	while ((opt = getopt_long(ac, av, "s", long_opts, NULL)) != -1) {
		input_mode = (enum input_mode)opt;
		switch (opt) {
		case defconfig:
			//defconfig_file = smdk5250_defconfig
			defconfig_file = optarg;
			break;
		}
	}
	switch (input_mode) {
	case defconfig:
		//将smdk5250_defconfig中内容读入
		if (conf_read(defconfig_file)) {...}
		break;
	}
	switch (input_mode) {
	case defconfig:
		//设置新的symbols
		conf_set_all_new_symbols(def_default);
		break;
	}
	//写入---
	if (conf_write(NULL)) {...}

}

最终通过conf_write将信息写入.config中

int conf_write(const char *name)
{
	FILE *out;
	struct symbol *sym;
	struct menu *menu;
	const char *basename;
	const char *str;
	char dirname[PATH_MAX+1], tmpname[PATH_MAX+1], newname[PATH_MAX+1];
	char *env;
	/*
	const char *conf_get_configname(void)
	{
		char *name = getenv("KCONFIG_CONFIG");
		return name ? name : ".config";
	}
	*/
	//文件.config的由来
	basename = conf_get_configname();
	sprintf(newname, "%s%s", dirname, basename);

	conf_write_heading(out, &kconfig_printer_cb, NULL);

	menu = rootmenu.list;
	while (menu) {...}
	fclose(out);
	//最终看见的信息
	conf_message(_("configuration written to %s"), newname);
}

1.5 如何添加自己的CONFIG

当make生成.config后,整个sudo make ARCH=arm vexpress_defconfig, 就执行完了,但是如何添加自己的编译选项和文件到Makefile中呢?
以drivers/usb/host为例
(1) 首先我们找到该目录下的Kconfig, 然后添加下面的语句

config USB_ERIC_DEBUG
	bool "eric_debug usb support"
	#默认是开启的,没有任何依赖
	default y

Linux Kernel 编译流程 (一)_第1张图片
(2) 然后在drivers/usb/host/ 目录下添加eric_debug_usb.c 并且在 drivers/usb/host/Makefile中添加下面的语句,

obj-$(CONFIG_USB_ERIC_DEBUG) += eric_debug_usb.o

NoteMakefileCONFIG_USB_ERIC_DEBUGCONF_后的字符串(USB_ERIC_DEBUG)一定要和 KconfigUSB_ERIC_DEBUG一样,

我们直接看看编译的部分结果(vmlinux), 如下所示
Linux Kernel 编译流程 (一)_第2张图片
.config文件的产生以及怎样添加自己的文件,已经讲完了.请移步Linux Kernel编译流程(二)—vmlinux的产生

你可能感兴趣的:(linux内核,linux)