前言

之前对uboot的构建进行了分析,现在再对linux kernel的构建进行分析。几年前的确也分析过,但是只是停留在笔记层面,没有转为文章,这次下定决定来完善它。

环境

同样,采用的还是zynq平台的linux,从Makefile可以看到版本:

VERSION = 3
PATCHLEVEL = 15
SUBLEVEL = 0
EXTRAVERSION =
NAME = Shuffling Zombie Juror

linux Makefile支持的选项(最常用到的)

选项V,用于开启或者关闭执行make时编译信息的打印

    @echo  '  make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
@echo ' make V=2 [targets] 2 => give reason for rebuild of target' ifeq ("$(origin V)", "command line")
KBUILD_VERBOSE = $(V)
endif
ifndef KBUILD_VERBOSE
KBUILD_VERBOSE = 0
endif

选项C,用于开启或者关闭静态代码检查

    @echo  '  make C=1   [targets] Check all c source with $$CHECK (sparse by default)'
@echo ' make C=2 [targets] Force check of all c source with $$CHECK' ifeq ("$(origin C)", "command line")
KBUILD_CHECKSRC = $(C)
endif
ifndef KBUILD_CHECKSRC
KBUILD_CHECKSRC = 0
endif

选项M/SUBDIRS,源码外模块编译时会用到

# Use make M=dir to specify directory of external module to build
# Old syntax make ... SUBDIRS=$PWD is still supported
# Setting the environment variable KBUILD_EXTMOD take precedence
ifdef SUBDIRS
KBUILD_EXTMOD ?= $(SUBDIRS)
endif
ifeq ("$(origin M)", "command line")
KBUILD_EXTMOD := $(M)
endif

选项O/KBUILD_OUTPUT,指定out-of-build时的输出目录

    @echo  '  make O=dir [targets] Locate all output files in "dir", including .config'

ifeq ("$(origin O)", "command line")
KBUILD_OUTPUT := $(O)
endif

选项W,也是代码检查用的,很少用到

    @echo  '  make W=n   [targets] Enable extra gcc checks, n=1,2,3 where'
@echo ' 1: warnings which may be relevant and do not occur too often'
@echo ' 2: warnings which occur quite often but may still be relevant'
@echo ' 3: more obscure warnings, can most likely be ignored'
@echo ' Multiple levels can be combined with W=12 or W=123' ifeq ("$(origin W)", "command line")
export KBUILD_ENABLE_EXTRA_GCC_CHECKS := $(W)
endif

构建分析

内核的构建一般包含两步(如果算上distclean的话,就是三步,如果再算上dtbs构建的话,就是四步,这里忽略那两个步骤的分析),下面会对每一个步骤进行跟踪分析

第一步,配置,对应的命令make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zturn_defconfig

对应的规则是(顶层Makefile里):

%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@

这里先解释两个地方

第一个,关于Q,它只是在KBUILD_VERBOSE为1的时候为空,不为1的时候,为@。在make中,一条命令前加@表示执行该命令的时候,不打印出执行的命令。而KBUILD_VERBOSE的设置,请参考关于"支持的选项"那节里的V选项的描述。

# A simple variant is to prefix commands with $(Q) - that's useful
# for commands that shall be hidden in non-verbose mode.
#
# $(Q)ln $@ :<
ifeq ($(KBUILD_VERBOSE),1)
quiet =
Q =
else
quiet=quiet_
Q = @
endif

也顺便说说quiet

# 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)/$@
# cmd_cc_o_c = $(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.
# If the user is running make -s (silent mode), suppress echoing of
# commands ifneq ($(filter 4.%,$(MAKE_VERSION)),) # make-4
ifneq ($(filter %s ,$(firstword x$(MAKEFLAGS))),)
quiet=silent_
endif
else # make-3.8x
ifneq ($(filter s% -s%,$(MAKEFLAGS)),)
quiet=silent_
endif
endif

第二个,关于build(在scripts/Kbuild.include定义,它是由顶层Makefile所包含进来的):

# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj

现在重点关注这条规则的依赖,scripts_basic和outputmakefile

依赖1 scripts_basic(顶层Makefile中)

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

也就是说,会先执行

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

以及

$(Q)rm -f .tmp_quiet_recordmcount(这个就不用说了,删除临时文件)

经过上面对Q和build的说明,我们可以知道

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

等价于

$(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build obj=scripts/basic

这里假设KBUILD_SRC为空(一般都是为空),那么上面的命令最终变成了

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

也就是说会执行scripts/Makefile.build这个Makefile,且设置输入变量obj为 scripts/basic

经过分析scripts/Makefile.build,最终执行的规则为:

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

其中KBUILD_BUILTIN := 1在顶层Makefile有定义,如果执行”make modules”,会在214行开始对其进行一些处理

ifeq ($(MAKECMDGOALS),modules)
KBUILD_BUILTIN := $(if $(CONFIG_MODVERSIONS),1)
endif

所以我们这里 KBUILD_BUILTIN :=1

如果执行”make all”、”make _all”、”make modules”、”make”中任一个命令,则会对这个变量进行处理

ifneq ($(filter all _all modules,$(MAKECMDGOALS)),)
KBUILD_MODULES := 1
endif ifeq ($(MAKECMDGOALS),)
KBUILD_MODULES := 1
endif

因此,我们这里KBUILD_MODULES :=

分析了这两个变量后,上面的规则可重新写为

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

规则的命令是一个冒号命令”:”,冒号(:)命令是bash的内建命令,通常把它看作true命令。

下面来一个个分析在执行make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zturn_defconfig的情况下,$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)这些变量的值。

ifneq ($(strip $(obj-y) $(obj-m) $(obj-n) $(obj-) $(subdir-m) $(lib-target)),)
builtin-target := $(obj)/built-in.o
endif ifneq ($(strip $(lib-y) $(lib-m) $(lib-n) $(lib-)),)
lib-target := $(obj)/lib.a
endif

这里的变量除了always,其他都为空,always来自于scripts/Makefile.build中,有

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

由于我们输入的obj为scripts/basic,而该Makefile开头就有src := $(obj),于是我们可以知道,它最终包含了头文件scripts/basic/Makefile,该Makefile内容如下:

hostprogs-y := fixdep
always := $(hostprogs-y) # fixdep is needed to compile other host programs
$(addprefix $(obj)/,$(filter-out fixdep,$(always))): $(obj)/fixdep

由此可知,always的内容为fixdep。也就是说scripts_basic在执行make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zturn_defconfig的结果就是构建fixdep

依赖2,outputmakefile

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

这个规则的命令运行一个shell脚本scripts/makefile,并传递四个参数。这个脚本主要是在$(objtree)参数指定的目录中生成一个Makefile文件。由于这里KBUILD_SRC为空,所以这个脚本并不会被执行

分析完两个依赖后,再来看

%config: scripts_basic outputmakefile FORCE
$(Q)mkdir -p include/linux include/config
$(Q)$(MAKE) $(build)=scripts/kconfig $@

在他的依赖被处理完后,开始执行规则的命令。第一个命令创建了两个目录,第二个命令扩展后为

$(Q)$(MAKE) -f $(if $(KBUILD_SRC),$(srctree)/)scripts/Makefile.build  obj=scripts/kconfig zynq_zturn_defconfig

这个命令依然是执行scripts/Makefile.build这个makefile文件。并执行它里面zynq_zturn_defconfig的规则。根据上面的分析,在Makefile.build会包含scripts/kconfig/Makefile文件。然后执行以zynq_zturn_defconfig为目标的规则,在scripts/kconfig/Makefile中有规则:

%_defconfig: $(obj)/conf
$(Q)$< --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)

其中Kconfig来自:

# Read arch specific Makefile to set KBUILD_DEFCONFIG as needed.
# KBUILD_DEFCONFIG may point out an alternative default configuration
# used for 'make defconfig'
include $(srctree)/arch/$(SRCARCH)/Makefile
export KBUILD_DEFCONFIG KBUILD_KCONFIG ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif

arch/arm/Makefile中没有定义KBUILD_KCONFIG,因此Kconfig := Kconfig

等价于

scripts/kconfig/conf --defconfig=arch/$(SRCARCH)/configs/zynq_zturn_defconfig Kconfig

继续分析%_defconfig: $(obj)/conf,它会先构建conf(请参考scripts/Makefile.host),然后就是调用conf处理--defconfig=arch/$(SRCARCH)/configs/zynq_zturn_defconfig Kconfig。这就是我们实际配置的过程。

这里可以总结下,执行make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zturn_defconfig,make第一步会先编译fixdep这个程序,它在内核的编译过程会用到,然后会构建conf,它用来解析Kconfig,最后会根据命令行情况生成.config

我们可以通过执行命令来验证下上面的分析(记得先distclean下):

$ make V=1 ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zynq_zturn_defconfig
make -f scripts/Makefile.build obj=scripts/basic
gcc -Wp,-MD,scripts/basic/.fixdep.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -o scripts/basic/fixdep scripts/basic/fixdep.c
rm -f .tmp_quiet_recordmcount
mkdir -p include/linux include/config
make -f scripts/Makefile.build obj=scripts/kconfig zynq_zturn_defconfig
gcc -Wp,-MD,scripts/kconfig/.conf.o.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DCURSES_LOC="<ncurses.h>" -DLOCALE -c -o scripts/kconfig/conf.o scripts/kconfig/conf.c
cat scripts/kconfig/zconf.tab.c_shipped > scripts/kconfig/zconf.tab.c
cat scripts/kconfig/zconf.lex.c_shipped > scripts/kconfig/zconf.lex.c
cat scripts/kconfig/zconf.hash.c_shipped > scripts/kconfig/zconf.hash.c
gcc -Wp,-MD,scripts/kconfig/.zconf.tab.o.d -Wall -Wmissing-prototypes -Wstrict-prototypes -O2 -fomit-frame-pointer -DCURSES_LOC="<ncurses.h>" -DLOCALE -Iscripts/kconfig -c -o scripts/kconfig/zconf.tab.o scripts/kconfig/zconf.tab.c
In file included from scripts/kconfig/zconf.tab.c:2537:0:
scripts/kconfig/menu.c: In function ‘get_symbol_str’:
scripts/kconfig/menu.c:590:18: warning: ‘jump’ may be used uninitialized in this function [-Wmaybe-uninitialized]
jump->offset = strlen(r->s);
^
scripts/kconfig/menu.c:551:19: note: ‘jump’ was declared here
struct jump_key *jump;
^
gcc -o scripts/kconfig/conf scripts/kconfig/conf.o scripts/kconfig/zconf.tab.o
scripts/kconfig/conf --defconfig=arch/arm/configs/zynq_zturn_defconfig Kconfig
#
# configuration written to .config
#

第二步,构建,对应的命令make ARCH=arm CROSS_COMPILE=arm-xilinx-linux-gnueabi- zImage

BOOT_TARGETS    = zImage Image xipImage bootpImage uImage

$(BOOT_TARGETS): vmlinux
$(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@

由此可以看出依赖vmlinux,下面先看vmlinux

# Include targets which we want to
# execute if the rest of the kernel build went well.
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE
ifdef CONFIG_HEADERS_CHECK
$(Q)$(MAKE) -f $(srctree)/Makefile headers_check
endif
ifdef CONFIG_SAMPLES
$(Q)$(MAKE) $(build)=samples
endif
ifdef CONFIG_BUILD_DOCSRC
$(Q)$(MAKE) $(build)=Documentation
endif
+$(call if_changed,link-vmlinux)

关于命令前加号说明

makefile中以+开头的命令的执行不受到 make的-n,-t,-q三个参数的影响。比方说你的 makefile 是这样写的

all:

echo hello

+echo world

正常你 make,两个echo命令都会执行

hello

world

然后你 make -n,就只有第二个echo命令会被执行了

接着看依赖$(vmlinux-deps):

export KBUILD_VMLINUX_INIT := $(head-y) $(init-y)
export KBUILD_VMLINUX_MAIN := $(core-y) $(libs-y) $(drivers-y) $(net-y)
export KBUILD_LDS := arch/$(SRCARCH)/kernel/vmlinux.lds vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)

由此可以看出主要是依赖$(head-y) $(init-y) $(core-y) $(libs-y) $(drivers-y) $(net-y)

其中

head-y      := arch/arm/kernel/head$(MMUEXT).o  (arch/arm/Makefile)

其他的都是在顶层的Makefile里定义

init-y      := $(patsubst %/, %/built-in.o, $(init-y))
core-y := $(patsubst %/, %/built-in.o, $(core-y))
drivers-y := $(patsubst %/, %/built-in.o, $(drivers-y))
net-y := $(patsubst %/, %/built-in.o, $(net-y))
libs-y1 := $(patsubst %/, %/lib.a, $(libs-y))
libs-y2 := $(patsubst %/, %/built-in.o, $(libs-y))
libs-y := $(libs-y1) $(libs-y2)

写到这的时候,我发现网上有一个blog已经详细分析了整个构建过程,而且写的非常好(之前没看到它!!!),因此我就打算停止继续写这篇了,不重复造轮子,请大家直接参考 这里

完!

2016年5月

linux kernel make构建分析的更多相关文章

  1. Linux kernel workqueue机制分析

    Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...

  2. Linux Kernel Oops异常分析

    1.PowerPC小系统内核异常分析 1.1  异常打印 Unable to handle kernel paging request for data at address 0x36fef31eFa ...

  3. Linux kernel workqueue机制分析【转】

    转自:http://www.linuxsir.org/linuxjcjs/15346.html 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.1 ...

  4. Linux Kernel CMPXCHG函数分析

    原文地址:http://blog.csdn.net/penngrove/article/details/44175387 最近看到Linux Kernel cmpxchg的代码,对实现很不理解.上网查 ...

  5. linux kernel input 子系统分析

    Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层 ...

  6. Linux Kernel ---- PCI Driver 分析

    自己笔记使用. Kernel 版本 4.15.0 (ubuntu 18.04,intel skylake) 最近想学习VGA驱动去了解 DDCCP / EDID 等协议,然后顺便了解下驱动是如何工作的 ...

  7. linux kernel 字符设备详解

    有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备 ...

  8. uboot makefile构建分析

    前言 几年前分析过uboot的构建及启动过程,做了笔记,但最终没有转为文章.这次又有机会开发嵌入式产品了(之前一年多都是在搞x86 linux),看了下uboot的构建过程,觉得有必要写下整个分析过程 ...

  9. arm linux kernel 从入口到start_kernel 的代码分析

    参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...

随机推荐

  1. Guide: Solr performance tuning--转载

    原文地址:http://h3x.no/2011/05/10/guide-solr-performance-tuning Introduction I have for the last year be ...

  2. 从原理上搞定编码(二)-- Web编码

    周末宅在家里睡完觉就吃饭,吃完饭接着睡觉,这日子过的实在是没劲啊.明明还有计划中的事情没有做, 为什么就是不想去做呢,这样的生活持续下去,必然会成为一个彻头彻尾的loser.上一篇写的 初识编码 ,这 ...

  3. BZOJ4887 Tjoi2017可乐(动态规划+矩阵快速幂)

    设f[i][j]为第i天到达j号城市的方案数,转移显然,答案即为每天在每个点的方案数之和.矩乘一发即可. #include<iostream> #include<cstdio> ...

  4. C++解析(7):函数重载分析

    0.目录 1.重载的概念 2.C++中的函数重载 3.函数默认参数遇上函数重载 4.编译器调用重载函数的准则 5.重载与指针 6.C++和C相互调用 7.小结 1.重载的概念 自然语言中的上下文--你 ...

  5. WPF 如何加载图片

    Uri ri = new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources/exp.jpg"); ImageSource ...

  6. [JLOI2012]时间流逝 树上高斯消元 概率期望

    题面 题意:(感觉题面写的题意是错的?)有\(n\)种能量不同的圈,设当前拥有的圈的集合为\(S\),则: 1,每天有\(p\)概率失去一个能量最小的圈.特别的,如果\(S = \varnothing ...

  7. 漫谈ElasticSearch关于ES性能调优几件必须知道的事

    lasticSearch是现在技术前沿的大数据引擎,常见的组合有ES+Logstash+Kibana作为一套成熟的日志系统,其中Logstash是ETL工具,Kibana是数据分析展示平台.ES让人惊 ...

  8. Alpha 冲刺 —— 十分之一

    队名 火箭少男100 组长博客 林燊大哥 作业博客 Alpha 冲鸭! 成员冲刺阶段情况 林燊(组长) 过去两天完成了哪些任务 协调各成员之间的工作,对多个目标检测及文字识别模型进行评估.实验,选取较 ...

  9. MSSQL代理工作服务器远程命令执行

    概述 如果MSSQL数据库中开启了MSSQL Server Agent Job服务的话,攻击者将可以利用MSSQL Server中自带的功能来获取一个shell. SQL Server Agent S ...

  10. (转)搭建本地 8.8 W 乌云漏洞库

    下载地址: 开源地址: https://github.com/m0l1ce/wooyunallbugs 百度网盘: 链接: http://pan.baidu.com/s/1nvkFKox 密码: 94 ...