linux kernel make构建分析
前言
之前对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构建分析的更多相关文章
- Linux kernel workqueue机制分析
Linux kernel workqueue机制分析 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.10.108的workqueue文档分析其基 ...
- Linux Kernel Oops异常分析
1.PowerPC小系统内核异常分析 1.1 异常打印 Unable to handle kernel paging request for data at address 0x36fef31eFa ...
- Linux kernel workqueue机制分析【转】
转自:http://www.linuxsir.org/linuxjcjs/15346.html 在内核编程中,workqueue机制是最常用的异步处理方式.本文主要基于linux kernel 3.1 ...
- Linux Kernel CMPXCHG函数分析
原文地址:http://blog.csdn.net/penngrove/article/details/44175387 最近看到Linux Kernel cmpxchg的代码,对实现很不理解.上网查 ...
- linux kernel input 子系统分析
Linux 内核为了处理各种不同类型的的输入设备 , 比如说鼠标 , 键盘 , 操纵杆 , 触摸屏 , 设计并实现了一个对上层应用统一的试图的抽象层 , 即是Linux 输入子系统 . 输入子系统的层 ...
- Linux Kernel ---- PCI Driver 分析
自己笔记使用. Kernel 版本 4.15.0 (ubuntu 18.04,intel skylake) 最近想学习VGA驱动去了解 DDCCP / EDID 等协议,然后顺便了解下驱动是如何工作的 ...
- linux kernel 字符设备详解
有关Linux kernel 字符设备分析: 参考:http://blog.jobbole.com/86531/ 一.linux kernel 将设备分为3大类,字符设备,块设备,网络设备. 字符设备 ...
- uboot makefile构建分析
前言 几年前分析过uboot的构建及启动过程,做了笔记,但最终没有转为文章.这次又有机会开发嵌入式产品了(之前一年多都是在搞x86 linux),看了下uboot的构建过程,觉得有必要写下整个分析过程 ...
- arm linux kernel 从入口到start_kernel 的代码分析
参考资料: <ARM体系结构与编程> <嵌入式Linux应用开发完全手册> Linux_Memory_Address_Mapping http://www.chinaunix. ...
随机推荐
- 使用JMeter代理录制app测试脚本
准备条件:JMeter.手机app 上一篇介绍过录制Web测试脚本的方式有两种,使用代理和使用第三方工具.本篇录制app测试脚本只讨论使用代理的方式,其他方式以后有机会再补充.其实Web和app使用代 ...
- UVA12583_Memory Overow
题目是很简单的队列维护的题目. 每次加入之前判断该字母是否在队列以及队列的容量是否超过k即可. #include <iostream> #include <cstdio> #i ...
- 【bzoj5147】casino 区间dp
题目描述 赌城拉斯维加斯的米高梅大赌场最近推出了一种新式赌法.它的玩法是由庄家设局(所用赌具是一批五颜六色的筹码),赌徒只要交付一定数额的赌资即可入局.开赌前庄家将手中的筹码依次排开铺成一排构成一局, ...
- day06 小数据池和编码
一. 上次课内容回顾字典:由{}括起来. 每个元素用逗号隔开, key:value的形式存储数据key: 不可变的. 可哈希的.增删改查:1. 增加: 直接用新key来赋值. dict[key] = ...
- Docker学习笔记六:Docker搭建企业级私有仓库
前言 Docker不仅是一个强大的服务器部署工具,而且它还有一个官方的Docker Hub registry用于储存Docker镜像.上传镜像到Docker Hub是免费的,上传的镜像文件同时也对公共 ...
- 基于 HTML5 的人脸识别技术
基于 HTML5 的人脸识别技术 https://github.com/auduno/headtrackr/
- HDU.2612 Find a way (BFS)
HDU.2612 Find a way (BFS) 题意分析 圣诞节要到了,坤神和瑞瑞这对基佬想一起去召唤师大峡谷开开车.百度地图一下,发现周围的召唤师大峡谷还不少,这对基佬纠结着,该去哪一个...坤 ...
- bzoj2213: [Poi2011]Difference(思维题)
今天颓了一天T T 这题有两种写法... ①预处理出每种字符在原字符串中的位置,枚举两种字符作为最大值和最小值,把这两种字符的坐标归并排序,把最大值设为1,最小值设为-1,求最大子段和.注意因为 ...
- re正则模块
1.正则表达式的常用符号 '.' 默认匹配除\n之外的任意一个字符,若指定flag DOTALL,则匹配任意字符,包括换行 '^' 匹配字符开头,若指定flags MULTILINE,这种也可以匹配上 ...
- ASP.NET Core的身份认证框架IdentityServer4--(5)自定义用户登录(通过接口登录,无UI版本)
官网接口详解文档地址:文档地址 (PS:可通过接口名称搜索相应接口信息.) 源码地址:https://github.com/YANGKANG01/IdentityServer4-IdentityAut ...