哈喽,我是子牙,一个很卷的硬核男人。深入研究计算机底层、Windows内核、Linux内核、Hotspot源码……聚焦做那些大家想学没地方学的课程
今天这篇文章给大家分享一下Linux内核的启动流程。为什么要分享这个话题呢?所谓万丈高楼平地起,如果我们想玩转Linux内核,我们既要了解它的前世,又要了解它的今生,还要了解它的未来。启动流程就是Linux内核的前世,对于理解Linux内核,里面包含了太多太多关键信息
其实也是在为后面出书做准备…Linux内核的重要性,我不必言说了。但是目前市面上关于Linux内核的书籍,要么是基于老版本的Linux2.6内核讲的,要么就是ARM架构的、MIPS架构的,Intel架构的几乎没有!凭什么歧视我大Intel!我准备填补这个空缺
授人以鱼不如授人以渔,在分享Linux内核的启动流程过程中,我会把研究明白流程中的每一步所需要的基础也顺便分享出来。这样大家看完这篇文章,不仅知道了Linux内核是如何启动起来的,更知道了研究Linux内核需要的每一层基础
以下,enjoy(对应Linux x86_64 5.0内核)
如果我们要研究Linux内核,我们需要什么呢?单步调试Linux内核的环境(这部分内容我好像还没写过文章,已加入todo list,抽空写。如果你需要的话,可以关注下我的公众号,才能获得文章推送
现在假设我们有了单步调试Linux内核的环境,我们第一步要做什么呢?下断点。那在哪下断点最合适呢?
这个你随便搜资料都能找到答案:start_kernel
好,我们下个断点。顺便让大家看下,单步调试Linux内核是一种怎样的赶脚
注意看我用红框框出来的。我想表达什么呢?启动流程不是从start_kernel开始的,前面还要很多。求知,自己还是得有探索的能力,或者得有基本的判断,不然只能人云亦云
其实这个框出来的,也只是64位内核部分。如果你写过操作系统,或者对CPU有一定的了解,你就知道:电脑刚启动的时候,CPU是出于实模式下(real mode)的,我们图中看到的,已经是CPU的长模式(long mode)了,这中间还要经历保护模式,IA32-e兼容模式。这部分内容,我之前写过文章,感兴趣的可以去看看 传送门
接下来我们就来看看,Linux内核是如何完成CPU的模式切换并启动起来的
说到启动内核,大家应该都或多或少听过这些词:MBR、Bootloader、Grub、UEFI、BIOS、GPT…它们分别代表什么呢?我来解释一下
BIOS+MBR是一组搭档,负责加载BootLoader及Linux内核。当你按下开机键,BIOS程序会从MBR区域将引导程序BootLoader读入内存。MBR区域就是你听过的磁盘的0柱面0磁道1扇区,这种磁盘分区结构,一个扇区是固定的512B,而功能强大的引导程序通常超过512B,所以Linux内核的做法是将MBR区域当作一个跳板,由MBR区域的代码将BootLoader主体载入内存,然后移交控制权,最后由BootLoader将Linux内核读入内存
没get到?看我写的操作系统中的MBR区域代码
注意上图中我用红圈圈出来的部分,这部分代码就是BootLoader主体,在早期的Linux内核中,这部分代码是自己写的,程序名叫setup,现在都用功能强大齐全的Grub
我发现Linux5.0内核已经不支持BIOS+MBR组合了,我运行了它的MBR区域的代码,得到的结果是这样的
我特地研究了一下这个问题,得到的答案是:这部分代码主要目的是防止旧的不识别GPT的工具误操作。这种"Protective MBR"通常不包含有效的引导代码或分区信息
这就引出来了GPT,UEFI+GPT是一组搭档,用来取代BIOS+MBR。因为UEFI比BIOS强大,GPT比MBR强大,强大具体体现在哪些方面我就不展开了,感兴趣的小伙伴自行chatgpt
那UEFI+GPT+Grub是如何启动Linux内核的呢?UEFI固件会对硬件进行初始化,并读取在NVRAM中的启动条目,然后读取GPT磁盘上的ESP区域,加载Grub程序,Grub程序会读取配置文件,然后启动Linux内核
Grub配置文件在哪呢?
Grub文件中重要的内容
最终执行的Linux内核的代码位置在哪呢?
到这里,这趴就分享完了,得到最终的结论就是Linux5.0内核已经不支持BIOS+MBR启动,仅支持UEFI+GPT启动,Linux5.0内核最初的代码入口是一个叫_start的函数,这个函数的前面两个字节是一个短跳转,最终跳到Linux内核的主要代码start_of_setup程序
通过前面的分享你应该能看出来,如果你想玩明白Linux内核中的这部分内容,你需要具备的基础:AT&T汇编、要了解CPU的运行模式、OS的通用加载机制、Linux内核的加载机制、BIOS+MBR的工作机制、UEFI+GPT的工作机制、Grub的工作机制…
是不是感觉Linux内核这玩意,看别人玩感觉好容易,自己一玩,就emo了:大家玩的是同样的内核吗?
这个环节最重要的就是完成CPU由实模式进入保护模式的切换。那如何实现CPU由实模式进入保护模式呢?这个代码要怎么写?翻下Intel手册就知道要干三件事。关中断是我加的,我觉得这时候处理发生的中断没有意义,发生错误让机器直接down掉得了
但是Linux5.0内核的代码远远没有这么简单。所以我觉得:自己动手写一个64位多核操作系统,是玩转Linux内核的唯一方式。因为你不写,你会迷失在Linux内核庞大的代码中,找不到重点,没有一根主线牵引你的思维去理解Linux内核源码
Linux内核是如何实现的呢?执行start_of_setup程序,进入C语言的世界,这时候C语言的世界是16位的,功能很弱。Linux内核在这个环节,除了做了主要的事情,还做了很多边缘的事情
这部分代码我就不展开讲了。这篇文章的主题是帮助大家梳理出Linux内核的启动流程主线及玩转这些需要的基础,感兴趣的小伙伴可以自行去研究,也可以选择加入我的实战Linux内核小班跟我学习
接下来就是进入64位CPU独有的运行模式,但是CPU设计的时候,不能直接由保护模式进入64位长模式,而是要先进入兼容模式
你肯定有这样的疑惑:为什么不能直接进入64位长模式呢?64位CPU中的"兼容模式"(也被称为“32位模式”或“保护模式”)是为了向后兼容而设计的。主要的理由是当新的64位CPU和操作系统被引入时,大量的已存在的软件仍然是32位的。为了让这些软件能够在新的硬件和操作系统上运行,而不需要重新编写,64位CPU支持了这种模式
那如何进入IA32-e兼容模式呢?看核心代码
这部分代码在Linux内核中还真不太好找。我们真正运行的内核是压缩过的,所以这部分代码在这里
这部分代码我是怎么找到的呢?分析Linux内核的编译脚本makefile分析出来的
所以如果你想玩转Linux内核,makefile语法你要非常非常熟悉
终于可以进入64位CPU的最终运行模式了。如果你看Linux内核源码,还真不好理解
你看我写的代码,就很容易理解了
如果不采用这样的方式,就得用一种长跳转的方式实现,看起来没这个优雅
仔细阅读Linux内核源码,真的能感受到写Linux内核的这些工程师对技术的热爱,他们对代码优雅的追求、代码技巧的追求、对技术的综合应用,真的让人拍案叫绝!Linux内核,无愧是集结了全球顶级程序员智慧的结晶!
当CPU执行完这段代码,就进入了我们熟悉的世界,就是本篇文章的第一张图
如果你的CPU是多核的,到目前为止,只有一个核完成了由实模式进入长模式的切换,这个核称为BP,一般就是0号核。其他的核暂时都还是出于原始状态,即实模式下,还不能用
那Linux内核是如何让这些核,即所有的AP核,进入长模式呢?下篇文章细讲。关注公众号【硬核子牙】,第一时间获得最新文章推送
对了,关注公众号【硬核子牙】回复【Linux内核启动流程】可以获取我梳理流程时画的图
enjoy·题外话
我的实战Linux内核小班还在招生,文章中讲到的所有知识点都会讲到,并且会提供实战环境,告诉你做实验的思路,让你彻底掌握。
为了保证课程质量,所有课程内容由子牙老师亲自授课!整个课程大概三个月左右,50+课时,每节课都超硬核,干货满满
对课程感兴趣的,可以看看我之前写的文章:
手写64位多核OS,玩转Linux内核