3.2_内核初始化(盘古开天地)

王者杯·14天创作挑战营·第5期 10w+人浏览 542人参与

3.2 内核初始化(盘古开天地)

3.2.1 内核初始化的神话

Linux内核的初始化流程,很多文章都有分析,自己也学习了很多次,总是感觉记忆不深刻。突发奇想,觉得整个初始化流程与中国神话传说盘古开天地特别的契合,所以尝试着更生动形象的分析一下内核初始化化流程。

天和地还没有分开的时候,宇宙混沌一片,从外表看像一个大鸡蛋。这个蛋形混沌宇宙可以用“0”来非常形象的表示。Linux内核的初始化是从0开始的,Documentation/arm64/booting.txt里面写明了,此时中断是完全关闭的,一切似乎都是静止的。

宇宙混沌中,开始孕育代号为0的巨人盘古,孕育的顺序是从头(head.S)开始,然后是身体(stext)。什么是宇宙?宇者,上下四方,空间为宇;宙者,古往今来,时间为宙。盘古为了开天辟地,必须分开空间和时间。在空间维度,要对内存空间进行切分,即基于MMU的空分复用。在时间维度,要对CPU的运行时间进行切分,即基于进程调度的时分复用。盘古作为0号进程,在head.S汇编代码中,沿着_head(头)->stext(身)->__primary_switch(MMU)->__primary_switched(进程)完成了上述4个步骤,之后就可以跳转到C语言函数start_kernel开始开天辟地。

盘古开天辟地不仅仅是简单的分开天地,还要创造世间万物,就像start_kernel要进行各个子系统的初始化一样。当所有的子系统都初始化完毕,开天辟地的最后一步,就是分出天和地,start_kernel的最后一步是rest_init,也有异曲同工之妙。在rest_init中,创建”地”进程即1号进程init,它将孕育所有用户进程,是所有的用户进程的祖先;创建”天”进程为2号线程kthreadd,它将孕育所有内核线程,是所有的内核线程的祖先。

盘古(0号进程)在完成开天辟地之后,就事了拂衣去,闲云野鹤地idle了,也被称为idle进程。只有在天地之间没有任何进程运行的时候,才被调度运行。

3.2.2 从头 (Head)开始

Linux内核代码的执行真的是从头(Head)开始!为啥这么说?

内核入口所在的文件名是head.S,启动代码链接时的代码段名字叫做__HEAD(即.head.text头代码),启动代码的标签是_head,全部都是用单词Head(头),司马昭之心路人皆知哈。接下来分析具体是怎么做到的。

根据Linux内核源码目录下的Makefile,vmlinux的链接文件是vmlinux.lds。

export KBUILD_LDS          := arch/$(SRCARCH)/kernel/vmlinux.lds

链接文件vmlinux.lds是由vmlinux.lds.S文件生成的。以ARM64为例,vmlinux.lds.S位于目录arch/arm64/kernel/。vmlinux.lds生成的过程记录在.vmlinux.lds.cmd文件中。

在vmlinux.lds.S文件中,与Linux内核运行入口相关的主体内容如下:

1	ENTRY(_text)	
2				
3	SECTIONS	
4	{			
5		    ……	
6		. = KIMAGE_VADDR + TEXT_OFFSET;
7				
8		.head.text : {	
9			_text = .;	
10			HEAD_TEXT
11		}		
12		    ……	
13		.text : {	/* Real text segment*/
14			_stext = .;	/* Text and read-only data*/
15		    ……	
16		}		
17	}			

第1行,使用ENTRY命令将符号_text的值设置为Linux运行时入口地址。

_text在哪里定义?

看第9行,此处定义了_text符号。咬文嚼字一下哈,为啥叫这个名字呢?追溯到commit e2f81844efa2 ARM: vmlinux.lds: use _text and _stext the same way as x86。x86使用_text来标记.head.text段的起始地址,同时也是内核镜像的运行起始地址;用_stext来标记.text段的起始地址。_text之后第10行就是HEAD_TEXT。HEAD_TEXT定义在include/asm-generic/vmlinux.lds.h:

#define HEAD_TEXT  KEEP(*(.head.text))

所以,只要在源码中定义了.head.text段,就会链接到_text。 这就必须说到定义在include/linux/init.h中的宏定义__HEAD。通过宏定义__HEAD就可以在源码中定义.head.text段。其中“ax”后缀的定义参考Section (Using as) (sourceware.org):a代表allocatable,x代表executable。

#define __HEAD		.section	".head.text","ax"

__HEAD宏在哪里使用?

在head.S(arch/arm64/kernel)的起始位置,使用了__HEAD宏。所以,Linux内核运行的入口位置就是head.S中的标号_head。

	__HEAD
_head:
	/*
	 * DO NOT MODIFY. Image header expected by Linux boot-loaders.
	 */
#ifdef CONFIG_EFI
	/*
	 * This add instruction has no meaningful effect ex
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值