2.0 Linux编译及启动流程

本博客主要对学习嵌入式Linux进行总结,学习视频参考正点原子。开发板使用Imx6ull芯片。

1.、Uboot启动Linux系统

1.0 从网络启动linux

1.使用tftp命令将zImage下载到DDR的0x800000地址处
		tftp 80800000 zImage
2.将设备树imx6ull_mynand.dtb下载到DDR的0x83000000
		tftp 83000000 imx6ull_mynand.dtb
3.bootz启动linux内核
		bootz 80800000 - 83000000

2.0 EMMC中启动linux

1.使用fatload命令将zImage下载到DDR的80800000
		fatload mmc 1:1 80800000 zImage
2.使用fatload命令将设备树下载到DDR的83000000
		fatload mmc 1:1 83000000 imx6ull_mynand.dtb
3.bootz启动linux内核
		bootz 80800000 - 83000000

3.0 设置bootcmd环境变量进行网络启动linux

1.设置环境变量
	setenv bootcmd 'tftp 80800000 zImage; tftp 83000000 imx6ull_mynand.dtb; bootz 80800000 - 83000000'
2.保存环境变量
	saveenv
3.启动linux
	boot

2、Linux内核编译过程

对于linux编译需要安装lzop软件,使用sudo apt-get install lzop进行安装

2.1 首先创建编译脚本文件

1.将arch/arm/configs/目录下的imx_v7_mfg_defconfig文件拷贝并命名为imx_mynand_defconfig,这是linux默认配置文件
2.创建imx6ull_mynand.sh文件,给该文件赋予权限,使用sudo chmod 777 ./imx6ull_mynand.sh,在该文件中添加如下指令,并执行./mx6ull_alientek_emmc.sh 
	#!/bin/sh 
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- distclean # 清除工程中的一些文件
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- imx_mynand_defconfig # 该名称与之前创建的默认配置文件名称相同
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig # 图形配置界面
	make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- all -j16 # 编译
3.当弹出图行界面配置时不用理会,直接两下esc退出即可
4.当编译完成后,最终会生成/arch/arm/boot/zImage镜像文件镜像和arch/arm/boot/dts/imx6ull-mynand.dtb设备树文件。
总结:Linux 的编译过程基本和 uboot 一样,都要先执行“make xxx_defconfig”来配置一下,然后再执行“make”进行编译。如果需要使用图形界面配置的话就执行“make menuconfig”。

2.2 顶层Makefile分析

Linux 的顶层Makefile 和 uboot 的顶层 Makefile 非常相似,因为 uboot 参考了Linux,前602行几乎一样
在第258和259行定义ARM架构和编译器

2.2.1 make xxx_defconfig执行过程

当执行该指令时,与顶层Makefile中的第548行目标“%config”匹配相匹配,“%config”依赖scripts_basic、outputmakefile 和 FORCE,“%config”真正有意义的依赖就只有scripts_basic。
%config: scripts_basic outputmakefile FORCE 
	$(Q)$(MAKE) $(build)=scripts/kconfig $@ # 执行该程序
	
 其中scripts_basic规则为: 
	$(Q)$(MAKE) $(build)=scripts/basic 
	$(Q)rm -f .tmp_quiet_recordmcount 
  build 定义在文件scripts/Kbuild.include 中,值为 build := -f $(srctree)/scripts/Makefile.build obj
  ---> 命令展开就是:@make -f ./scripts/Makefile.build obj=scripts/basic
  
  其中$(Q)$(MAKE) $(build)=scripts/kconfig $@
  --->命令展开就是: @make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig 
  
  总结:执行make xxx_defconfig指令会对应执行如下命令
  		@make -f ./scripts/Makefile.build obj=scripts/basic 
		@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig 

在这里插入图片描述

1.分析@make -f ./scripts/Makefile.build obj=scripts/basic指令

打开Makefile.build文件分析
将 kbuild-dir 展开后为: kbuild-dir=./scripts/basic 
将 kbuild-file 展开后为: kbuild-file= ./scripts/basic/Makefile 
include $(kbuild-file)展开后为: include ./scripts/basic/Makefile

@make -f ./scripts/Makefile.build obj=scripts/basic该命令没有指定目标,所以会使用到默认目标__build
则_build:$(builtin-target) $(lib-target) $(extra-y)) $(subdir-ym) $(always)
目标__build 有 5 个依赖,其中只有 always 有效
故__build: scripts/basic/fixdep scripts/basic/bin2c 
scripts_basic 目标的作用就是编译出 scripts/basic/fixdep 和 scripts/basic/bin2c 这两个软件

在这里插入图片描述
在这里插入图片描述

2.分析@make -f ./scripts/Makefile.build obj=scripts/kconfig xxx_defconfig指令

此命令会使用到的各个变量值
	src= scripts/kconfig 
	kbuild-dir = ./scripts/kconfig 
	kbuild-file = ./scripts/kconfig/Makefile 
	include ./scripts/kconfig/Makefile
	
由此可得知Makefile.build 会读取 scripts/kconfig/Makefile 中的内容,该文件中的代码段为
%_defconfig: $(obj)/conf 
	$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig) 

目标%_defconfig 与 xxx_defconfig 匹配,会执行这条规则,将该代码段展开为
%_defconfig: scripts/kconfig/conf 
 	@ scripts/kconfig/conf --defconfig=arch/arm/configs/%_defconfig Kconfig
 	
%_defconfig 依赖scripts/kconfig/conf,所以会编译scripts/kconfig/conf.c 生成conf 这个软件。
此conf 软件就会将%_defconfig 中的配置输出到.config 文件中,最终生成 Linux kernel 根目录下的.config 文件

2.2.2 make命令执行过程

使用命令“make xxx_defconfig”配置好Linux内核以后就可以使用“make”或者“make all”命令进行编译
_all:…… # _all是默认目标,如果使用命令“make”编译 Linux 的话此目标就会被匹配
_all: all # 默认目标_all依赖all。 
all: vmlinux # 目标all依赖vmlinux
 
vmlinux: scripts/link-vmlinux.sh $(vmlinux-deps) FORCE 
vmlinux-deps := $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN) 
 
KBUILD_LDS          = arch/arm/kernel/vmlinux.lds
KBUILD_VMLINUX_INIT = $(head-y) $(init-y)
KBUILD_VMLINUX_MAIN = $(core-y) $(libs-y) $(drivers-y) $(net-y)
vmlinux 的依赖为:scripts/link-vmlinux.sh、$(head-y) 、$(init-y)、$(core-y) 、$(libs-y) 、$(drivers-y) 、$(net-y)、arch/arm/kernel/vmlinux.lds 和 FORCE

head-y    = arch/arm/kernel/head.o 
init-y    = init/built-in.o 
drivers-y = drivers/built-in.o sound/built-in.o firmware/built-in.o 
net-y     = net/built-in.o
libs-y    = arch/arm/lib/lib.a lib/lib.a arch/arm/lib/built-in.o lib/built-in.o 
core-y    = usr/built-in.o 					arch/arm/vfp/built-in.o \ 
			arch/arm/vdso/built-in.o 		arch/arm/kernel/built-in.o \ 
			arch/arm/mm/built-in.o 			arch/arm/common/built-in.o \ 
			arch/arm/probes/built-in.o 		arch/arm/net/built-in.o \ 
			arch/arm/crypto/built-in.o 		arch/arm/firmware/built-in.o \ 
			arch/arm/mach-imx/built-in.o 	kernel/built-in.o\ 
			mm/built-in.o 					fs/built-in.o \ 
			ipc/built-in.o 					security/built-in.o \ 
			crypto/built-in.o 				block/built-in.o 
head-y 、init-y、core-y 、libs-y 、drivers-y 和 net-y 这 6 个变量都是一些 built-in.o 或.a 等文件,这个和 uboot 一样,都是将相应目录中的源码文件进行编译,然后在各自目录下生成 built-in.o 文件,有些生成了.a 库文件。
最终将这些 built-in.o 和.a 文件进行链接(使用vmlinux.lds链接脚本,其中vmlinux-deps依赖KBUILD_LDS且KBUILD_LDS等于vmlinux.lds)即可形成ELF格式的可执行文件,也就是vmlinux

1 但是链接是需要链接脚本的,vmlinux 的依赖arch/arm/kernel/vmlinux.lds 就是整个Linux 的链接脚本arch/arm/kernel/vmlinux.lds。
2 顶层Makefile文件中的+$(call if_changed,link-vmlinux) 表示将$(call if_changed,link-vmlinux)的结果作为最终生成 vmlinux 的命令,前面的“+”表示该命令结果不可忽略
其中link-vmlinux 是函数 if_changed 的参数,函数 if_changed 定义在文件 scripts/Kbuild.include 中,该文件中有命令$(echo-cmd) $(cmd_$(1)); 
3 $(echo-cmd)用于打印命令执行过程,$(cmd_$(1))中的$(1)表示参数,也就是 link-vmlinux,$(cmd_$(1))表示执行 cmd_link-vmlinux 的内容
4 cmd_link-vmlinux 会调用scripts/link-vmlinux.sh这个脚本来链接出vmlinux
至此我们基本理清了 make 的过程,重点就是将各个子目录下的 built-in.o、.a 等文件链接在一起,最终生成vmlinux 这个ELF 格式的可执行文件

各个目录下的built-in.o 文件编译生成过程

在每个目录下均会生成一个built-in.o文件,这些文件通过vmlinux.lds脚本进行链接,最终调用link-vmlinux.sh脚本输出vmlinux镜像文件,最后被压缩成zImage文件,了解built-in-o如何编译。
1.vmliux 依赖 vmlinux-deps,而 vmlinux-deps= $(KBUILD_LDS) $(KBUILD_VMLINUX_INIT) $(KBUILD_VMLINUX_MAIN)
其中KBUILD_LDS是链接脚本,这里不考虑,剩下的KBUILD_VMLINUX_INIT 和KBUILD_VMLINUX_MAIN 就是各个子目录下的 built-in.o、.a 等文件。
最终 vmlinux-deps 的值如下
vmlinux-deps = 	arch/arm/kernel/vmlinux.lds 		arch/arm/kernel/head.o \ 
				init/built-in.o 					usr/built-in.o \ 
				arch/arm/vfp/built-in.o 			arch/arm/vdso/built-in.o \ 
				arch/arm/kernel/built-in.o 			arch/arm/mm/built-in.o \ 
				arch/arm/common/built-in.o 			arch/arm/probes/built-in.o \ 
				arch/arm/net/built-in.o 			arch/arm/crypto/built-in.o \ 
				arch/arm/firmware/built-in.o 		arch/arm/mach-imx/built-in.o \ 
				kernel/built-in.o 					mm/built-in.o \ 
				fs/built-in.o 						ipc/built-in.o \ 
				security/built-in.o 				crypto/built-in.o\ 
				block/built-in.o 					arch/arm/lib/lib.a\ 
				lib/lib.a 							arch/arm/lib/built-in.o\ 
				lib/built-in.o 						drivers/built-in.o \ 
				sound/built-in.o 					firmware/built-in.o \ 
				net/built-in.o 

在顶层Makefile文件中有如下命令可以编译链接
$(sort $(vmlinux-deps)): $(vmlinux-dirs);

vmlinux-dirs主要和目录有关,此变量保存着生成 vmlinux 所需源码文件的目录,,vmlinux-dirs的值为
vmlinux-dirs = 	init 			usr 				arch/arm/vfp \ 
 				arch/arm/vdso 	arch/arm/kernel 	arch/arm/mm \ 
 				arch/arm/common arch/arm/probes 	arch/arm/net \ 
 				arch/arm/crypto arch/arm/firmware 	arch/arm/mach-imx\ 
				kernel 			mm 					fs \ 
 				ipc 			security 			crypto \ 
 				block 			drivers 			sound \ 
 				firmware 		net 				arch/arm/lib \ 
				lib 

$(vmlinux-dirs): prepare scripts 	目标 vmlinux-dirs 依赖 prepare 和 scripts
	$(Q)$(MAKE) $(build)=$@     	展开为make -f ./scripts/Makefile.build obj=$@
其中$@表示目标文件,也就是vmlinux-dirs的值,将vmlinux-dirs中的这些目录全部带入到命令中如下:
			@ make -f ./scripts/Makefile.build obj=init 
			@ make -f ./scripts/Makefile.build obj=usr 
			@ make -f ./scripts/Makefile.build obj=arch/arm/vfp 
			@ make -f ./scripts/Makefile.build obj=arch/arm/vdso 
			@ make -f ./scripts/Makefile.build obj=arch/arm/kernel 
			@ make -f ./scripts/Makefile.build obj=arch/arm/mm 
			@ make -f ./scripts/Makefile.build obj=arch/arm/common 
			@ make -f ./scripts/Makefile.build obj=arch/arm/probes 
			@ make -f ./scripts/Makefile.build obj=arch/arm/net 
			@ make -f ./scripts/Makefile.build obj=arch/arm/crypto 
			@ make -f ./scripts/Makefile.build obj=arch/arm/firmware 
			@ make -f ./scripts/Makefile.build obj=arch/arm/mach-imx 
			@ make -f ./scripts/Makefile.build obj=kernel 
			@ make -f ./scripts/Makefile.build obj=mm 
			@ make -f ./scripts/Makefile.build obj=fs 
			@ make -f ./scripts/Makefile.build obj=ipc 
			@ make -f ./scripts/Makefile.build obj=security 
			@ make -f ./scripts/Makefile.build obj=crypto 
			@ make -f ./scripts/Makefile.build obj=block 
			@ make -f ./scripts/Makefile.build obj=drivers 
			@ make -f ./scripts/Makefile.build obj=sound 
			@ make -f ./scripts/Makefile.build obj=firmware 
			@ make -f ./scripts/Makefile.build obj=net 
			@ make -f ./scripts/Makefile.build obj=arch/arm/lib 
			@ make -f ./scripts/Makefile.build obj=lib	
具体每个命令的生成不再介绍,最终在每个目录下的所有.o 文件链接在一起,最终形成每个目录下的built-in.o,再由vmlinux.lds脚本进行链接,最终调用link-vmlinux.sh脚本输出vmlinux镜像文件,最后被压缩成zImage文件。					

Linux编译基本和 uboot 的顶层 Makefile 一样,重点在于vmlinux 的生成。最后将 vmlinux 压缩成我们最常用的 zImage 或 uImage 等文件

3、linux启动流程

分析linux启动过程,首先对linux源码进行编译(见上一节)
1. linux内核链接脚本文件为arch/arm/kernel/vmlinux.lds,在该文件中ENTRY(stext) 为整个linux内核入口,入口为stext
2. 分析stext,定义在文件arch/arm/kernel/head.S中88行
	safe_svcmode_maskall r9 # 第100行,确保CPU 处于 SVC 模式,并且关闭了所有的中断。safe_svcmode_maskall 定义在文件arch/arm/include/asm/assembler.h 中
	mrc	p15, 0, r9, c0, c0 # 第102行,读处理器ID,ID 值保存在r9 寄存器中
	bl	__lookup_processor_type	 # 第103行,调用函数__lookup_processor_type 检查当前系统是否支持此 CPU,如果支持就获取 procinfo 信息
	bl	__vet_atags # 第130行,调用函数__vet_atags 验证 atags 或设备树(dtb)的合法性。函数__vet_atags 定义在文件arch/arm/kernel/head-common.S 中。 
	bl	__create_page_tables # 第139行,调用函数__create_page_tables 创建页表
	ldr	r13, =__mmap_switched # 第149行,将函数__mmap_switched 的地址保存到r13 寄存器中。__mmap_switched 定义在文件 arch/arm/kernel/head-common.S,__mmap_switched 最终会调用 start_kernel 函数。
	1:	b	__enable_mmu # 第159行,调 用 __enable_mmu 函 数 使 能 MMU , __enable_mmu 定 义 在 文 件arch/arm/kernel/head.S 中。__enable_mmu 最终会通过调用__turn_mmu_on 来打开 MMU,__turn_mmu_on 最后会执行 r13 里面保存的__mmap_switched 函数。
3.执行stext程序,最终会执行第149行__mmap_switched函数(定义在文件 arch/arm/kernel/head-common.S),通过分析该函数,最终会执行b start_kernel程序来启动linux内核(start_kernel 函数定义在文件 init/main.c中)

在这里插入图片描述

  • 8
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值