计算机启动过程

前言

这篇文章是在看了很多前辈写的博客下完成的,一些表述和图来自参考资料,非原创 ,该篇先从ROM RAM 这些硬件介绍,然后从启动的过程最主要的主角:BIOS 开始,介绍整个过程。

前期知识

ROM 和 RAM 是什么, 有什么区别

    参考: [什么是ROM 什么是RAM【详细介绍】](https://zhuanlan.zhihu.com/p/71383426) 和  https://baijiahao.baidu.com/s?id=1669115625475225711&wfr=spider&for=pc

ROM

ROM (Read-Only Memory) , 如下图 , 他的作用
把固件(系统软件)烧录进Flash,然后就不能修改了。不管用户怎么重新启动,怎么运用,都不会影响到固件。也就是说, 固件是不可被写入的 ,因此叫做“只读储存器”。
1297993-20211029152853878-1068744821.jpg

RAM

RAM (Random-Access Memory) , 随机储存器 ,就是电脑的内存条。用于存放动态数据。(也叫运行内存)系统运行的时候,需要把操作系统从ROM中读取出来,放在RAM中运行。

1297993-20211029155436359-1794960879.jpg

1297993-20211110105527327-895182707.jpg

可以看出 ROM 和 RAM 同属于硬件, 不同的是 ROM 经常被嵌入到元器件当中 ,而RAM 是我们熟悉的内存 ,通常可拔插 .

BIOS

"基本輸出輸入系統"(Basic Input/Output System),简称为BIOS。它是一组固化到计算机内主板上一个ROM芯片上的程序,它保存着计算机最重要的基本输入输出的程序、开机后自检程序和系统自启动程序,它可从CMOS中读写系统设置的具体信息。 其主要功能是为计算机提供最底层的、最直接的硬件设置和控制。

加载 BIOS

    原文链接:https://blog.csdn.net/weixin_46350177/article/details/116796559

计算机刚开机的时候只有1M的内存可以使用。 此时内存会被各种外设瓜分,映射在内存的相应区域。同理,BIOS里的信息会被映射到内存的一段连续的区域中(0xC00000xFFFFF),其中最为关键的系统BIOS被映射到了0xF00000xFFFFF位置,CPU开机就是执行了系统BIOS这块内存区域中的代码,注意BIOS中的程序还会占用内存开头的一些区域,如把中断向量表写在了内存开始的位置。

在开机的一瞬间,CPU中的PC寄存器被强制初始化为0xFFFF0。BIOS程序的入口地址就是0xFFFF0,然后CPU就开始跑起来,执行BIOS中的程序。

BIOS 做了什么

	这里摘自来自 https://www.jeanleo.com/2018/08/28/linux%E6%9C%BA%E5%99%A8%E5%90%AF%E5%8A%A8%E8%BF%87%E7%A8%8B/ 的描述, 非原创 

机器启动到linux初始化是一个比较复杂并且细节性较强的过程,大部分的实现都是由于历史遗留问题以及各种协议约定而来的,衔接性比较强,而且比较难跟踪,毕竟不是C语言那么简单,一个函数调用一个函数,整个脉络比较清晰明了。

这里主要是以linux-3.2.55版本内核为基础,将整个系统的初始化流程梳理了一下。

进入正文:

A、 自摁下电源开关后,是由主板自动初始化处理器信息的,将CS、DS、ES、SS置为0xffff,而PC置为0x0000(物理地址),由于初始化时,处理器处于实模式,那么也就是计算机执行的第一条指令在0xffff0(物理地址)这个位置。计算机执行的第一条指令是来自BIOS的,存储在ROM里面,通过ROM芯片译码以读取出来执行,该指令通常都是一条跳转指令,这是由于0xffff0距离可访问的内存结尾已经不远了,该指令可以用来跳转到具体的BIOS操作代码中;

B、 BIOS开始执行后,做了不少事情,比如Power On Self Test(POST,开机自检),检查CPU寄存器、周边芯片的状态,以及针对动态内存、主板芯片组、显卡以及相关外围的寄存器做初始化设置,并检查能够正常工作,同时记录系统的设置值,最重要的就是将常驻程序库(可以理解为BIOS的库)放置在某段内存中,提供给操作系统或者应用程序调用,比如int 0x13等

C、 BIOS毕竟不是系统,完成了分内工作后,执行int 0x19(前面提到的BIOS的常驻程序库)将存储在磁盘0头0道0扇区的MBR读入到内存0x7c00中,然后BIOS通过跳转指令去到0x7c00去执行引导程序代码;

D、 MBR的引导程序对不同的linux版本而言各有不同,最初0.11版本中,linux的/boot/bootsect.s自己实现了这个MBR引导程序,而如今3.2.55的linux内核版本自身已经不再实现MBR引导程序了,都是由GRUB的/stage1/stage1.S实现的,具体的引导程序历史可以参考linux内核中的/Documentation/x86/boot.txt;

E、 Linux 0.11的引导过程就不谈了,关于这块的资料数不胜数,那么GRUB实现的引导,主要是给用户提供了系统引导选择和引导编辑等功能,核心的是它通过调用BIOS的常驻程序库去将linux内核映像加载到内存当中

F、 GRUB加载完了内核映像,将会跳转到内核的/ arch/x86/boot/header.S里面的_start开始执行,其实_start也没什么好执行的,就跳转到start_of_setup去设置准备给实模式下main函数运行的环境;

G、 开始进入main函数执行,该函数的实现在/arch/x86/boot/main.c文件里面,具体可以进入去分析里面的代码,其主要的莫过于一些参数的准备、堆的初始化、CPU的检测、BIOS的设置以及内存检测等工作,然后开始转入保护模式 (到这个地方才进入了保护模式);

H、 进入保护模式后,将会跳转去执行arch/x86/boot/compressed/head_32.S里面的startup_32,其主要设置一个基本的环境,如堆栈等;

I、 再往下就是调用/arch/x86/boot/compressed/misc.c里面的decompress_kernel,用来解压内核,当内核被解压到内存中之后,就可以调用它了;

J、 解压完内核,还要调用一个startup_32,这个不同于前面的那个,这个是位于/arch/x86/kernel/head_32.S里面的,主要工作是对页表进行初始化,并启动内存分页功能,初始化0号进程;

K、 startup_32 执行完了之后,最后进入内核的主题函数start_kernel,该函数位于/init/main.c,自此完成linux的最后初始化。

以上就是机器启动linux的整个过程,主要是梳理了初始化实现的一个线索,暂时不深入分析。

我们总结一下, 大概的流程就是 :

  1. BIOS 运行自己的开机程序
  2. 硬件自检
  3. 加载内核镜像 , (交给操作系统了)
  4. 进入实模式ing...
  5. 保护模式 (页表还没初始化)
  6. 解压内核到内存当中(开始操作系统的运行啦)
  7. 页表初始化
  8. 初始化 0 号进程
  9. main 函数

其中加载MBR的过程如下 :

    所谓的加载: 就是把某设备(如硬盘)中的程序复制到内存中的过程。
    加载启动区就是: BIOS程序把启动区的内容复制到了内存的某区域

    什么是启动区?
    启动区是符合某种特征的一块区域,人们把它叫做启动区。

    BIOS的查找启动盘的顺序是可以设置的,如U盘启动,硬盘启动,软盘启动,光盘启动等。

    所谓的符合某种特征指的是: BIOS会按照设置的启动盘顺序,轮流去读取这些启动盘中位于0盘0道1扇区的内容。这0盘0道1扇区一共有512字节,如果末尾的两个字节分别是0x55和0xaa,那么BIOS就会认为它是个启动区。如果不是,就下一个启动盘接着寻找,都找不到的话会报出无启动区的错误。

    BIOS把启动区的512字节复制到内存的0x7c00位置,并用一条跳转指令把pc寄存器的值指向0x7c00。

可以看这篇文章,可以知道在0盘0道1扇区有个MBR的区域。

图例 :

1297993-20211111215340619-399966103.png

GRUB

上面我们讲的第D个步骤 , 这个 GRUB 是什么东西呢 ? GRUB 是个引导操作系统的程序 ,下面来自 GRUB 官网的介绍
1.1 Overview
Briefly, a boot loader is the first software program that runs when a computer starts. It is responsible for loading and transferring control to an operating system kernel software (such as Linux or GNU Mach). The kernel, in turn, initializes the rest of the operating system (e.g. a GNU system).

	GNU GRUB is a very powerful boot loader, which can load a wide variety of free operating systems, as well as proprietary operating systems with chain-loading1. GRUB is designed to address the complexity of booting a personal computer; both the program and this manual are tightly bound to that computer platform, although porting to other platforms may be addressed in the future.

	One of the important features in GRUB is flexibility; GRUB understands filesystems and kernel executable formats, so you can load an arbitrary operating system the way you like, without recording the physical position of your kernel on the disk. Thus you can load the kernel just by specifying its file name and the drive and partition where the kernel resides.

	简而言之,引导加载程序是计算机启动时运行的第一个软件程序。它负责加载和传输控制到操作系统内核软件(如 Linux 或 GNU Mach)。内核反过来初始化操作系统的其余部分(例如 GNU 系统)。

	GNU GRUB 是一个非常强大的引导加载程序,它可以加载各种各样的免费操作系统,以及具有链加载1 的专有操作系统。GRUB 旨在解决启动个人计算机的复杂性;该程序和本手册都与该计算机平台紧密相关,尽管将来可能会移植到其他平台。

	GRUB 的重要特性之一是灵活性;GRUB 了解文件系统和内核可执行格式,因此您可以按照自己喜欢的方式加载任意操作系统,而无需记录内核在磁盘上的物理位置。因此,您只需指定内核的文件名以及内核所在的驱动器和分区即可加载内核。

下面是来自:

	Windows也有类似的工具NTLOADER;比如我们在机器中安装了Windows 98后,我们再安装一个Windows XP,在机器启动的会有一个菜单让我们选择进入是进入Windows 98还是进入WindowsXP。NTLOADER就是一个多系统启动引导管理器,NTLOADER同样也能引导Linux,只是极为麻烦罢了;

	在Powerpc架构的机器中,如果安装了Linux的Powerpc版本,大多是用yaboot多重引导管理器,比如Apple机目前用的是IBM Powerpc处理器,所以在如果想在Apple机上,安装Macos和LinuxPowerpc版本,大多是用yaboot来引导多个操作系统;

	因为目前X86架构的机器仍是主流,所以目前GRUB和LILO仍然是我们最常用的多重操作系统引导管理器;

语摘

	lvzongting 说:

	对于这个话题很感兴趣,根据自己了解的补充几点内存地址的东西:
	1.计算机开机时,CPU默认执行0ffffh:0000h处的指令(8086是这样,386应该类似),而此内存地址应该存放的就是bios rom
	2.bios执行玩post等后,将引导设备的mbr复制到内存地址07c00h处,跳转执行此处指令,这个地址应该是规定的
	3.现在一般引导设备中的mbr是grub或lilo这样的引导程序,这样的程序首先将自己复制到06c00h处执行,在主分区表中搜索活动分区,将用户选择的活动分区的第一个扇区读入到07c00h处,调转到07c00h处执行
	注:以上均在实模式中执行,还未进入保护模式
	4.为加载内核做准备,并将控制权交给kernel,系统启动成功
	注:这一步进入保护模式
	当然这是现在一般的启动流程,当然如果自己写os,可以直接在引导设备的mbr中写直接加载内核的代码,将编译的代码dd入引导设备的mbr中即可。





	成仔不说话 说:

	bios不一定存在,bios只是提供了一个固定的方式,去访问其他输入输出设备,存在的目的是可以将硬件的变化简化,这样内核就可以通过和固定的bios通信来获取基本的硬件地址信息,然后再获得更充分的硬件地址信息。没有bios的计算机,就没有这么一个固定的方式,随着硬件的改变,就要静态的改变内核内部设备的入口地址,然后重新编译。可以这么说bios是使得现在操作系统变得通用的一个利器。比如现在不同的arm手机的内核不能随便换着用,而有bios的计算机内核即使硬件不那么一样,却可以用同样的内核。还比如现在很多时候都会将硬件地址的map独立于内核写到一个单独的文件中,内核载入的时候会从这些文件中读取硬件的入口地址.  
	
	从上电开始,处理器的指令计数器会被初始化成一个值(这个值不一定是0x0000H,因为一般还会在前面放中断向量表),然后从这个地址开始执行程序。这些程序有可能放到片内的flash,有可能放到片外的flash,或是RAM里面。由于RAM是掉电易失的,所以可能需要先从其他存储设备中载入内存然后再从内存中调入片内进行运算。

参考资料

原文地址:https://www.cnblogs.com/Benjious/p/15551233.html