从cpu加电到加载OS内核的详细过程(清华大学ucore-lab1总结一)
结合最近学习清华的OS课,先用“人话”来高度抽象的描述一下我自己的理解。CPU在系统加电也就是我们按下电源开关后,开始初始化他的寄存器,主要是cs和eip(本文基于x86架构),然后在ROM中找到一个叫BIOS(Basic Input Output System),加载到RAM中然后开始执行他,他在进行完设备的自检和初始化之后,就根据他自己内部的“我该去哪个设备启动加载程序”表,将其中第一个设备的主引导扇区加载到内存中来,也就是将系统的控制权转交给了这个程序,然后他在正确的引用了bootloader,也就是我们常说的“引导程序”,这个引导程序就会在内存的I/O区域中读入硬盘(os的位置)扇区的信息,得到操作系统内核的ELF文件,加载到内存中来,然后将控制权转交给OS,就大功告成了。
(一)关于BIOS的那些事
1、我们的第一条指令的地址从何而来?
cpu在加电之后首先会初始化寄存器,主要是CF寄存器和EIP寄存器,因为他们合起来组成了第一条指令的线性地址。
The address FFFFFFF0H is beyond the 1-MByte addressable range of the processor while in real-address mode. The processor is initialized to this starting address as follows. The CS register has two parts: the visible segment selector part and the hidden base address part. In real-address mode, the base address is normally formed by shifting the 16-bit segment selector value 4 bits to the left to produce a 20-bit base address. However, during a hardware reset, the segment selector in the CS register is loaded with F000H and the base address is loaded with FFFF0000H. The starting address is thus formed by adding the base address to the value in the EIP register (that is, FFFF0000 + FFF0H = FFFFFFF0H).
【参考IA-32 Intel Architecture Software Developer’s Manual Volume 3: System Programming Guide Section 9.1.4】
由于386的段base address不是像8086/8088那样直接存储在段寄存器中然后左移四位形成,而是通过在段寄存器中存入段选择子(也叫段选择符、段选择器),然后再查找GDT或LDT获取段的基地址和段界限以及属性等信息——为了避免每次都存储器访问时都进行上述的过程,在处理器的内部特地为每个段配置了一个76位的高速缓冲寄存器,称其为段描述符高速缓冲寄存器,这些寄存器对程序员是透明的(不可见的)。然后最开始的CS寄存器提供的基地址就是从这儿得来的。

2、BIOS都做了啥?
第一条指令是一个跳转执行,跳转到BIOS的首地址去执行,系统将控制权转交给BIOS。BIOS首先进行硬件的自检和初始初始化之后,会选择一个启动设备(例如软盘、硬盘、光盘等),并且读取该设备的第一扇区(即主引导扇区或启动扇区)到内存一个特定的地址0x7c00处,然后CPU控 制权会转移到那个地址继续执行。至此BIOS的初始化工作做完了,进一步的工作交给了bootloader。
加载bootloader:
bootloader的中文译名叫“引导程序”,他所在硬件的扇区(512B)叫“引导扇区(boot sector)”,这里简单的介绍下硬盘的扇区结构。
硬盘是有很多个512bytes的扇区组成的,这些扇区又进一步划分为多个“分区”,每个分区拥有的扇区在物理上必须是连续的,每个分区的第一个扇区成为起始扇区或者引导扇区。主引导扇区(boot sector)位于硬盘的0磁头0柱面1扇区,由主引导记录(Master Boot Record)和分区表(Disk Partition Table)组成。其中主引导记录的作用就是根据分区表检查分区是否正确以及确定哪个分区为引导分区,并在程序结束后将引导分区的起始扇区加载到内存中。

每个起始扇区(包括主引导扇区)的最后两个字节都是“0X55AA”作为其结束的标志。
(一)关于bootloader的那些事
1、bootloader都干了啥
- 切换到保护模式,启用分段机制
- 读磁盘中ELF执行文件格式的ucore操作系统到内存
- 显示字符串信息
- 把控制权交给ucore操作系统
2、实模式和保护模式
一篇博文搞懂一切:http://www.cnblogs.com/immortal-worm/p/5867418.html
3、关于A20Gate
这里只从一个较高的抽象级给大家介绍一下这个概念。(A20Gate就是的cpu从低到高0计数开始数的第20根地址线)
首先在实模式下,我们知道cpu只有20根地址线可以用来寻址,也就是说理论上我们最大的寻址范围是2^20也就是1111 1111 1111 1111 1111=1M的空间,第21位是1,后面的全是0。但是实际上我们都知道x86是通过【段寄存器*4+EIP寄存器】来得到线性地址的。很明显这样做的最大寻址空间就变成0xffff*4 + 0xffff = 0x10ffef也就是1 0000 1111 1111 1110 1111——这个值是大于理论上的1M的,那么在实模式下,我们对于超出1M的地址,引入了一种叫wrap-around的技术,即对于的超出的部分以1M位模进行取余,这样就不会出现越界的情况。此时我们不难发现在实模式下A20Gate是处于关闭状态的,原因很简单,因为它是第21根地址线,而实模式只打开了20根。
那么现在bootloader要将实模式转换到保护模式下,首先要做的就是打开A20Gate,这里涉及到和键盘的8042芯片交互,在了解具体的打开方式之前,要先储备一点x86的IO端口相关的知识。
基础知识之IO端口:http://www.cnblogs.com/immortal-worm/p/5867690.html
多数PC都使用键盘控制器(8042芯片)来处理A20Gate。
从理论上讲,打开A20Gate的方法是通过设置8042芯片输出端口(64h)的2nd-bit,但事实上,当你向8042芯片输出端口进行写操作的时候,在键盘缓冲区中,或许还有别的数据尚未处理,因此你必须首先处理这些数据。
流程如下:
1. 禁止中断;
2. 等待,直到8042 Inputbuffer为空为止;
3. 发送禁止键盘操作命令到8042Input buffer;
4. 等待,直到8042 Inputbuffer为空为止;
5. 发送读取8042 OutputPort命令;
6. 等待,直到8042 Outputbuffer有数据为止;
7. 读取8042 Outputbuffer,并保存得到的字节;
8. 等待,直到8042 Inputbuffer为空为止;
9. 发送Write 8042Output Port命令到8042 Input buffer;
10. 等待,直到8042 Inputbuffer为空为止;
11. 将从8042 OutputPort得到的字节的第2位置1(OR 2),然后写入8042 Input buffer;
12. 等待,直到8042 Inputbuffer为空为止;
13. 发送允许键盘操作命令到8042Input buffer;
14. 打开中断。
4、配置GDT表
GDT表的详细介绍在前文保护模式中已经给出,现在我们只要用lgdt指令将gdt表的位置加载到dgtr寄存器中即可。
5、使能保护模式
386之后x86架构的cpu设置了控制寄存器,其中CR0的D0位决定了cpu访问内存的模式。在打开了A20Gate、配置好了GDT表之后,将其D0位置1即可。
6、从硬盘中读取ELF格式的操作系统
在使CPU进入到保护模式之后,就要开始从硬盘的相应位置去读取ELF格式的OS内核了,这个环节主要分两步,一个是从硬盘中读出ELF文件,然后从ELF文件的header进入读取整个os的内核。
从cpu加电到加载OS内核的详细过程(清华大学ucore-lab1总结一)的更多相关文章
- 《Linux内核设计的艺术》学习笔记(一)从开机加电到加载三个汇编源码
实验内核版本:0.11 ◆ 从开机到main函数的三步: ① 启动BIOS,准备实模式下的中断向量表和中断服务程序: ② 从启动盘加载OS程序到内存中,加载OS程序的工作就是利用第一步中的中断服务 ...
- 一个页面从输入URL到页面加载显示完成的详细过程
下面以访问baidu页面来做一个过程分析 输入 URL:http://www.baidu.com DNS 域名解析 计算机无法识别域名,计算机与计算机之间要想进行通信,必须通过ip地址用来定位该计算机 ...
- 入侵本地Mac OS X的详细过程 转自https://yq.aliyun.com/articles/22459?spm=5176.100239.blogcont24250.10.CfBYE9
摘要: 本文从提升权限漏洞的一系列巧妙的方法来绕过受保护的Mac OS X.有些已经被处于底层控制,但由于它们存在着更多的认证和修补程序,我们不妨让这些提供出来,以便需要的人学习它们.虽然我不只是要利 ...
- linux内核剖析(零)linux系统启动过程详解-开机加电后发生了什么
本文参考了如下文章 深入理解linux启动过程 mbr (主引导记录(Master Boot Record)) 电脑从开机加电到操作系统main函数之前执行的过程 详解linux系统的启动过程及系统初 ...
- linux系统启动过程具体解释-开机加电后发生了什么 --linux内核剖析(零)
本文參考了例如以下文章 深入理解linux启动过程 mbr (主引导记录(Master Boot Record)) 电脑从开机加电到操作系统main函数之前执行的过程 详细解释linux系统的启动过程 ...
- RISC-V CPU加电执行流程
市面上采用RISC-V架构的CPU很多,且没有如X86那样高度细节的标准,故采用说明文档详细的SiFive Freedom U540-C000芯片来做介绍(下面统一称为FU540). FU540支持多 ...
- 计算机从加电到系统(Linux)启动完成
0x0 背景 在我参加的面试和我面试别人.或者参加别人对别人的面试的事后经常遇到的一个问题就是:请从计算机加电开始描述一下计算机启动到操作系统正式启动起来的全过程.这是一个考验对计算机体系结构和基本知 ...
- centos7启动流程(从加电开始)
图片来自于https://blog.csdn.net/qq_27754983/article/details/75212666 1. UEFI或BIOS启动 服务器加电后,CPU 自动重置成初始状态, ...
- 第1章 从开机加电到main函数之前的过程
主要讲解了80x86cpu在启动的时候时bios如何工作,以及如何最终转换到保护模式. 1.1 启动bios 80x86作为冯诺依曼结构下的cpu,工作模式也是取指执行,即cpu根据cs:ip寄存器的 ...
随机推荐
- 浅淡Webservice、WSDL三种服务访问的方式(附案例)
Webservice Webservice是使应用程序以与平台和编程语言无关的方式进行相互通信技术. eg:站点提供访问的数据接口:新浪微博.淘宝. 官方解释:它是一种构建应用程序的普遍模型,可以在任 ...
- Unity Manual 用户手册
unity3d 文档的中文网址: http://game.ceeger.com/Manual/
- IE6解决固定定位代码
有些朋友在进行网页布局时,会遇到IE6浏览器不支持固定定位的兼容性问题,本博将详细介绍此问题的解决方法,需要了解的朋友可以参考下. ie6 垂直居中固定定位,代码如下: #center {_posit ...
- (JAVA)从零开始之--打印流PrintStream记录日志文件
这里的记录日志是利用打印流来实现的. 文本信息中的内容为String类型.而像文件中写入数据,我们经常用到的还有文件输出流对象FileOutputStream. File file = new Fil ...
- Java反射与代理
Java反射机制与动态代理,使得Java更加强大,Spring核心概念IoC.AOP就是通过反射机制与动态代理实现的. 1 Java反射 示例: User user = new User( ...
- python 数据结构
Python的数据结构主要分为set(),list[],和dict{}.这篇文章主要记载这几种结果在使用过程中的一些技巧或其中一些函数的用法区别. 1.函数get()与setdefault()区别: ...
- Java学习随笔——RMI
RMI(Remote Method Invocation)远程方法注入,用来实现远程方法调用,是实现分布式技术的一种方法.RMI提供了客户辅助对象和服务辅助对象,为客户辅助对象创建了和服务对象相同的方 ...
- iOS图片的伪裁剪(改变图片的像素值)
0x00 原理 利用一张图片事先画好的图片(以下称为蒙板),盖在要被裁剪的的图片上,然后遍历蒙板上的像素点,修改被裁剪图片对应位置的像素的色值即可得到一些我们想要的不规则图片了(比如人脸) 0x01 ...
- 4 - 执行TestNG
TestNG以如下几种方式被调用 命令行 ant Eclipse IntelliJ's IDEA 这部分对如何使用命令行方式调用TestNG进行阐述. 假设TestNG已经在你的classpath中, ...
- PHP 用户注册与登录
网站用户注册与登录是很常用的一个功能,本节教材就以此来演示一下 PHP 中如何开发用户注册与登录模块. 本节需要用到的重点 PHP 基础知识: PHP 中预定义 $_POST 和 $_GET 全局变量 ...