Linux内核加载全流程

简介: 作者:gfree.wind@gmail.com 博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net  本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
======================================================================================================
无论是Linux还是Windows,在加电后的第一步都是先运行BIOS(Basic Input/Output System)程序——不知道是不是所以的电脑系统都是如此。BIOS保存在主板上的一个non-volatile(即非易失)存储器,如PROM,EPROM,Flash等。——以前的BIOS一般都是只读的,现代的系统中,允许刷新BIOS程序。它的任务就是简单的初始化和识别系统硬件设备,如CPU,内存,输入/输出设备,外部存储设备等。然后找到bootloader的位置,并加载bootloader,将PC的控制权交给bootloader,完成后面的复杂的系统初始化任务。

但是在系统启动之前,系统如何启动BIOS呢?所以系统启动的过程,也被称为自举。虽然没有“先有鸡还是先有蛋”那么复杂,但是这里也有一个矛盾。PC是这样解决这个问题的。将CPU设计成加电以后,就从一个特殊的固定的地址开始执行指令,那么BIOS的位置就放在这里,也就是存储BIOS的ROM的起始地址就是这个固定的地址,用以保证BIOS程序可以在加电时被直接执行。

这里有两个问题:
1. BIOS的存储器地址如何决定的?
2. 现在多处理器的情况下,BIOS是如何执行的?

下面以Intel CPU为例,简单说明一下流程:
Intel在初始化的时候将CPU分为两类,即BSP(Bootstrap Processor)与APs(Application Processors)。从名字上既可看出两类CPU的作用。在启动的时候,首先由硬件动态选择一个总线上的CPU为BSP,那么剩下的CPU则都为AP。由BSP执行BIOS程序,初始化环境以及APs,然后还是有BSP执行操作系统的初始化代码。 Intel CPU的第一条语句的固定地址为0xFFFF FFF0,然后BIOS的存储器被hard-map到这个内存地址。这样当CPU开始执行时,实际上执行的就是BIOS程序。

由于BIOS的存储器不会太大,所以程序一般不会太复杂,那么不大可能实现加载操作系统的操作,只能完成简单的初始化工作。这时,只能借助于外部存储器了。可是外部存储器的读取是依赖于文件系统的。而BIOS程序既然比较简单,那么是不可能去支持文件系统的,更何况有各种各样的文件系统,不可能去一一支持。这时,还是只能依赖于硬编码,必须定义一个固定的外部存储器的地址——硬盘的第一个扇区的512字节——被称为MBR(Master Boot Record)——为什么是512字节呢?按照我的理解,一般情况下一个扇区都被设置为512字节,而硬盘操作的最小单元即为一个扇区。虽然可以设置更大的扇区,但是作为一个统一的程序来说,使用惯例512是一个不错的选择。

BIOS的最后一项任务就是将MBR读入到内存中,且起始地址固定为0x7c00,然后对MBR的最后两个字节进行验证,必须为0x55和0xAA,以保证这512字节为MBR。验证通过后,则跳转到0x7c00处开始执行。这样MBR就开始执行。——这里,我有两个问题,为什么是7c00和0x55和0xAA呢?目前没有找到当初选择这两个值的解释。我依稀记得选择0x55,0xaa是因为这个值比较特殊,利于校验,但是为什么利于校验却不记得了。

MBR保存了分区表(MBR并不存在于任何一个分区中,而是处于分区之上),以及一个用于装载操作系统启动程序的小程序。MBR首先会确定活动分区,然后使用BIOS将这个活动分区的启动扇区——仍然是第一个扇区512字节,最后跳转到加载该启动扇区的内存地址处。这样就将PC的控制器转移到这个启动扇区的程序手中(即真正的bootloader)。一般来说,这个启动程序也要求被加载到0x7c00这个地址。可是这个地址之前已经加载了MBR,如果再加载这个启动程序,那么必然冲突。所以MBR实际上在开始的时候,先对自己做了relocate,将自己拷贝到另外一个地址,然后从那个地址开始执行,这样就避免了冲突。

下面就进入了真正的bootloader了,对于Linux来说,一般就是LILO和GRUB,下面以最常用的GRUB为例。

GRUB的启动分为三个阶段stage1,stage1.5和stage2,这三个阶段也被分为三个文件(在某些情况下,可以没有stage1和stage1.5)。其中stage1可以嵌入到MBR中,即MBR的头446个字节(后面为分区表64字节,0x55和0xAA两个校验字节),也可以存储在活动分区的第一个扇区512字节, 然后由MBR来加载。所以stage1最多为512字节,如果存储在MBR中,则只能最大为446字节。stage1中保存了stage1.5的地址,并负责加载stage1.5的前512字节。之所以stage1只能加载512字节,是为了遵循MBR的规则。

进入stage1.5,由于只加载了前512字节,所以stage1.5首先要负责把剩余部分代码,由自己加载到内存中。对于stage1.5来说,它可以识别和支持文件系统。可以查看/boot/grub目录下,有多个后缀为stage1.5文件,其前缀即为支持的文件系统,也就是说要支持一个文件系统,就有一个对应的stage1.5文件。至于加载哪个文件,已经硬编码在stage1中。这个文件系统为stage2所在的文件系统。stage2文件是真正保存在文件系统中的。这样通过对应的stage1.5文件,就可以正确加载stage2文件。为什么会有stage1.5这个阶段呢?主要是当stage2不连续或者需要在stage2前,对文件系统做些特殊处理。如果没有这样的需求,完全可以避免stage1.5。

stage2文件为最主要的加载代码,这时由于已经stage1.5已经支持文件系统了,所以stage2可以比较大。stage2来实现GRUB的各种功能,这里就不列举了,感兴趣的同学可以自己查看GRUB的手册。stage2首先需要找到GRUB的配置文件,来决定如何加载操作系统。对于GRUB的配置与本文的主题联系并不紧密,我个人也对其兴趣不大。

GRUB不仅要复杂加载kernel,还要负责加载Initial Ram Disk,又被成为initrd。其目的主要是为了保证一个小体积的内核。initrd为一个简单的文件系统,它包含了一些内核必要的文件和模块。这样,首先将initrd挂载为一个根系统,然后kernel利用这个基本的系统,来检测环境,加载更多的必要的模块。在完成所有的加载后,这时kernel已经完全准备就绪。那么initrd对于kernel来说,已经不需要了。这时,kernel会将initrd从根/上卸载,并挂载上真正的根系统,并执行正常的启动程序。




参考:
1.  Wiki
2. 《Linux内核分析及编程》——倪继利
3. 《Linux内核完全剖析》——赵炯
4. 《Linux内核源代码情景分析》——毛德操  胡希明
5. 《Intel 64 and IA32 Architectures Software Developer‘s Manual》 Volume 3A
6. 《Understanding The Linux Kernel》 Denial P. Bovet & Macro Cesati
7. GUN GRUB Manual

目录
相关文章
|
6天前
|
算法 Linux
深入探索Linux内核的内存管理机制
本文旨在为读者提供对Linux操作系统内核中内存管理机制的深入理解。通过探讨Linux内核如何高效地分配、回收和优化内存资源,我们揭示了这一复杂系统背后的原理及其对系统性能的影响。不同于常规的摘要,本文将直接进入主题,不包含背景信息或研究目的等标准部分,而是专注于技术细节和实际操作。
|
6天前
|
存储 缓存 网络协议
Linux操作系统的内核优化与性能调优####
本文深入探讨了Linux操作系统内核的优化策略与性能调优方法,旨在为系统管理员和高级用户提供一套实用的指南。通过分析内核参数调整、文件系统选择、内存管理及网络配置等关键方面,本文揭示了如何有效提升Linux系统的稳定性和运行效率。不同于常规摘要仅概述内容的做法,本摘要直接指出文章的核心价值——提供具体可行的优化措施,助力读者实现系统性能的飞跃。 ####
|
7天前
|
监控 算法 Linux
Linux内核锁机制深度剖析与实践优化####
本文作为一篇技术性文章,深入探讨了Linux操作系统内核中锁机制的工作原理、类型及其在并发控制中的应用,旨在为开发者提供关于如何有效利用这些工具来提升系统性能和稳定性的见解。不同于常规摘要的概述性质,本文将直接通过具体案例分析,展示在不同场景下选择合适的锁策略对于解决竞争条件、死锁问题的重要性,以及如何根据实际需求调整锁的粒度以达到最佳效果,为读者呈现一份实用性强的实践指南。 ####
|
7天前
|
缓存 监控 网络协议
Linux操作系统的内核优化与实践####
本文旨在探讨Linux操作系统内核的优化策略与实际应用案例,深入分析内核参数调优、编译选项配置及实时性能监控的方法。通过具体实例讲解如何根据不同应用场景调整内核设置,以提升系统性能和稳定性,为系统管理员和技术爱好者提供实用的优化指南。 ####
|
9天前
|
负载均衡 算法 Linux
深入探索Linux内核调度机制:公平与效率的平衡####
本文旨在剖析Linux操作系统内核中的进程调度机制,特别是其如何通过CFS(完全公平调度器)算法实现多任务环境下资源分配的公平性与系统响应速度之间的微妙平衡。不同于传统摘要的概览性质,本文摘要将直接聚焦于CFS的核心原理、设计目标及面临的挑战,为读者揭开Linux高效调度的秘密。 ####
26 3
|
12天前
|
负载均衡 算法 Linux
深入探索Linux内核调度器:公平与效率的平衡####
本文通过剖析Linux内核调度器的工作机制,揭示了其在多任务处理环境中如何实现时间片轮转、优先级调整及完全公平调度算法(CFS),以达到既公平又高效地分配CPU资源的目标。通过对比FIFO和RR等传统调度策略,本文展示了Linux调度器如何在复杂的计算场景下优化性能,为系统设计师和开发者提供了宝贵的设计思路。 ####
27 6
|
11天前
|
消息中间件 安全 Linux
深入探索Linux操作系统的内核机制
本文旨在为读者提供一个关于Linux操作系统内核机制的全面解析。通过探讨Linux内核的设计哲学、核心组件、以及其如何高效地管理硬件资源和系统操作,本文揭示了Linux之所以成为众多开发者和组织首选操作系统的原因。不同于常规摘要,此处我们不涉及具体代码或技术细节,而是从宏观的角度审视Linux内核的架构和功能,为对Linux感兴趣的读者提供一个高层次的理解框架。
|
13天前
|
缓存 并行计算 Linux
深入解析Linux操作系统的内核优化策略
本文旨在探讨Linux操作系统内核的优化策略,包括内核参数调整、内存管理、CPU调度以及文件系统性能提升等方面。通过对这些关键领域的分析,我们可以理解如何有效地提高Linux系统的性能和稳定性,从而为用户提供更加流畅和高效的计算体验。
25 2
|
13天前
|
缓存 网络协议 Linux
深入探索Linux操作系统的内核优化策略####
本文旨在探讨Linux操作系统内核的优化方法,通过分析当前主流的几种内核优化技术,结合具体案例,阐述如何有效提升系统性能与稳定性。文章首先概述了Linux内核的基本结构,随后详细解析了内核优化的必要性及常用手段,包括编译优化、内核参数调整、内存管理优化等,最后通过实例展示了这些优化技巧在实际场景中的应用效果,为读者提供了一套实用的Linux内核优化指南。 ####
39 1
|
6天前
|
存储 Oracle 安全
服务器数据恢复—LINUX系统删除/格式化的数据恢复流程
Linux操作系统是世界上流行的操作系统之一,被广泛用于服务器、个人电脑、移动设备和嵌入式系统。Linux系统下数据被误删除或者误格式化的问题非常普遍。下面北亚企安数据恢复工程师简单聊一下基于linux的文件系统(EXT2/EXT3/EXT4/Reiserfs/Xfs) 下删除或者格式化的数据恢复流程和可行性。