带你阅读linux内核源码:linux内核启动过程分析

本文探讨了Linux内核的启动过程,从汇编代码的入口点开始,如x86和ARM架构下的head.S文件,直至start_kernel函数的调用。介绍了内核启动的三个阶段:解压、引导和初始化。在引导阶段,汇编代码初始化处理器,创建页表,并启用MMU,最后跳转到start_kernel进行内核初始化。
摘要由CSDN通过智能技术生成

当你在路上遇到一位绝世的丽人,你首先会很好奇她从哪里来,是什么样的成长环境造就了这旷世的美丽。进而你开始搜肠刮肚的想怎么样跟这美女套点近乎,建立起沟通的连廊,走进去她的内心,最终抱得美人归?

同样的道理,当你接触linux内核这位大美女的时候,你也会有上面的小心思,那我们就来看:

  • linux内核从哪儿来? -- 内核是如何运行起来的,它的启动过程是怎么样的?

本文或者后续文章代码均参考自:linux 4.9.230版本。

有接触过的朋友知道,linux内核的启动函数叫 start_kernel, 位置 init/main.c

asmlinkage __visible void __init start_kernel(void)

备注:asmlinkage, __visible和__init都是gcc编译器扩展,我们暂且把它们当透明人,不管他们,我下来会写篇文章统一讲这个部分。

就像人类孜孜不倦的寻找自己的历史一样,内核的起点真的是从C语言的start_kernel开始的吗?

很显然不是。

在start_kernel之前还有漫漫的史前时代,而这个时代的描绘者却是汇编代码。所以不同的体系架构上,这部分代码是不同的:

对于x86 i386架构,这部分代码在arch/x86/kernel/head_32.S

对于x86 x64架构,这部分代码在arch/x86/kernel/head_64.S

带你阅读linux内核源码:linux内核启动过程分析

而对于arm架构,则位于 arch/arm/kernel/head.S

带你阅读linux内核源码:linux内核启动过程分析

但是共同点也是明显的,这些不同架构的汇编代码最终都会调用start_kernel。所以,start_kernel并非linux内核的起点,把start_kernel称为linux内核架构无关的通用入口,才是最合适的描述

那start_kernel以前的这部分代码做了什么呢?处理器相关的硬件初始化。

我们以arm架构为例,arm架构下运行linux内核,执行的第一行代码在哪里?

就在这里了:arch/arm/kernel/head.S (linux 4.9.230)

带你阅读linux内核源码:linux内核启动过程分析

你可以不懂汇编代码,但是不懂英文简直是寸步难行。作为对内核终极起点的描述,我们把上面的注释好好看一下吧,以上注解透漏几个信息:

  1. 这部分代码是被内核解压代码调用的。我们知道内核有压缩的机制,所以看起来内核一解压,就跑到这里来了。所以,如果算上内核的解压程序,是不是内核的解压程序启动比这部分代码还早?是的。所以,内核的终极起点又要提前了,那就是内核自解压程序的入口
  2. 以上汇编代码执行的时候,硬件的状态是这样的:MMU = off, D-cache = off, I-cache = don care, r0 = 0,* r1 = machine nr, r2 = atags或dtb指针* r1 = machine nr, r2 = atags或dtb指针。后续的处理器初始化都是基于这个前提进行的。
  3. 这部分代码是位置无关代码,所以你在编译的时候的链接地址是0xc0008000,那么在执行的时候就可以这样调用:__pa(0xc0008000)。
  4. 查看arch/arm/tools/mach-types获取内核支持的machine types.这个是做什么的呢?如果你要新增你自己的board,那么也要登记到这里。

带你阅读linux内核源码:linux内核启动过程分析

了解以上信息后,我们大概看一下arc/arm/kernel/head.S干了什么事情,其实不一定要能看懂汇编代码,我们看注释就够了,我把head.S里的原文地址代码注释总结如下:

/* 部分源代码分析 */

/* 内核入口点 */

ENTRY(stext)

/* 设置处理器模式 */

ARM_BE8(setend be ) @ ensure we are in BE8 mode

THUMB( badr r9, 1f ) @ Kernel is always entered in ARM.

THUMB( bx r9 ) @ If this is a Thumb-2 kernel,

THUMB( .thumb ) @ switch to Thumb now.

THUMB(1: )

/* 判断CPU类型,查找运行的CPU ID值与Linux编译支持的ID值是否支持 */

bl __lookup_processor_type

/* 处理uboot传递过来的参数,获取memory大小等 */

bl __vet_atags

#ifdef CONFIG_SMP_ON_UP

bl __fixup_smp

#endif

#ifdef CONFIG_ARM_PATCH_PHYS_VIRT

bl __fixup_pv_table

#endif

/* 为内核运行创建页目录表和页表*/

bl __create_page_tables

/* 跳转到start_kernel函数 */

b start_kernel

我们总结一下,内核的启动分为三个阶段:

  1. 内核自解压阶段。解压程序解压内核之后,进入 arch/arm/kernel/head.S
  2. 内核引导阶段。执行head.S汇编代码,初始化处理器并创建页目录表和页表,然后enable mmu,最后进入start_kernel。一定要注意:enable mmu之后,所用的地址就都是虚拟地址了,而非物理地址。
  3. 内核初始化部分,通过start_kernel执行内核各个部件的初始化。这个部分比较长,且需要有一定的基础,我们放到后面再讲。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值