基于x86的Linux启动流程

1.1启动第一步:

当按下电源键的那一刻,RAM中什么也没有,但是ROM中有生产商烧录的BIOS启动块。此时,cpu由于刚启动,默认为16位实模式,CS:IP默认指向0xFFFF0这个地址位置。这个地址即是BIOS启动块的地址。

在这里插入图片描述

1.2bios中运行流程

1.2.1bios基本流程

显示显卡信息
显示内存信息

建立中断向量表和中断服务程序
在内存最开始的位置0x00000 ~ 0x003FF构建中断向量表[1KB],在紧挨着它的位置用256字节构建bios数据区(0x00400 ~ 0x004FF),在大约57KB之后,加载了8KB左右的中断向量表相应的若干中断服务函数。[这些中断服务函数的作用是把系统内核程序从硬盘加载到内存中]

在这里插入图片描述

1.2.2加载引导程序bootsect

当bios执行自检完成,硬件体系和BIOS联手,使bios触发一个int 0x19中断,该中断服务函数作用是把软盘第一扇区的512字节加载到指定内存位置。这个指定位置为【0x7c00】。

在这里插入图片描述

1.3 bootsect流程

当bios加载完bootsect,就跳到了bootsect中继续执行。
jmp 0x7c00;
其bootsect执行业务如下:

1.3.1复制bootsect

1.将自身bootsect这512字节从0x7c00复制到0x90000处
2.程序切换到cs = 0x90000处继续执行,IP不变
3.将ds es ss 都设置成与CS相同的位置0x9000,[由于ss sp被设置,之后就可以使用栈了]
在这里插入图片描述

为什么要拷贝多此一举?
rom里面的程序是芯片厂商制定的,软件开发和硬件不是一个公司,其实rom只做了一件事,就是将磁盘第一个扇区搬移到内存0x7c00处,这个地址相当于一个默认约定。但是软件厂商有很多,每个软件厂商都有自己的规划,如果各个厂商想将bootsect放到其他地址,需要自己重新规划,重新移动。

1.3.2将setup程序加载到内存

1.触发int 0x13中断,执行中断服务程序,将硬盘第二扇区开始的4个扇区【即是setup.s对应的程序】拷贝到0x90200之后,范围为【0x90200 ~ 0x909FF】
在这里插入图片描述

1.3.3将system模块加载到内存

1.触发int 0x13中断 将从第六扇区开始的240个扇区加载到内存的0x10000处。大小为120KB.
在这里插入图片描述

1.3.4确定根设备号(rootdevice)

目前来看,根设备号就是一个数字,该数字对应的是一个包含硬件信息的软盘,该软盘中含有格式化好的文件系统。

1.3.5 通过jmp跳转到setup代码中

跳转目的地0x90200;
跳转指令 jmpi 0, SETUPSEG

1.4 setup流程

1.4.1 获取系统数据

1.利用BIOS提供中断服务程序从设备上提取内核运行所需的机器系统数据,包括光标位置,显示页面等数据,并分别从中断向量0x41和0x46向量指向的内存地址处获得硬盘参数表1/硬盘参数表2,把它们存放在内存地址0x9000:0x0080和0x9000:0x0090处。

这些机器系统数据加载到内存0x90000-0x901FC处,覆盖了已经完成使命的bootsect代码

内存地址长度名称描述
0x900002光标位置列号 行号
0x900022拓展内存数系统从1MB开始的扩展内存数值【KB】
0x900042显示页面当前显示页面
0x900061显示模式
0x900071字符列数
0x900082??
0x9000A2显示内存显示内存 0x00-64K 0x01-128…
0x9008016硬盘参数表第一个硬盘参数表
0x9009016硬盘参数表第二个硬盘参数表 没有则清零
0x901FC2根设备号根文件系统所在的设备号

在这里插入图片描述

1.4.2开始转变为保护模式
1.4.2.1关中断,并将system移到0x00000

意义:将system从0x10000移到0x00000,将会废除BIOS在这区域的中断向量表和中断服务函数。而为了度过这一段没有中断向量表的时期,我们必须关中断。这样收回完成历史使命的bios向量表和中断服务函数所占用的内存,同时,也让内核代码占据内存物理地址最开始,天然的,有利位置。
在这里插入图片描述

1.4.2.2设置中断描述符表和全局描述符表

GDTR: GDT基地址寄存器,通过这个寄存器可以找到GDT首地址
GDT: 全局描述符表,在系统中唯一存放段寄存器内容(段描述符)的数组。用来在保护模式下管理段描述的数据结构,对操作系统自身的运行以及管理/调度进程有着重大意义。
这里设置了内核代码段/内核数据段

IDT: 保护模式下所有中断服务程序的入口地址,类似于是模式下的中断向量表
IDTR: 保存IDT的起始地址
由于已关中断,无需调用中断服务函数,所以目前IDTR设置为空

1.4.2.3打开A20,实现32位寻址

打开A20,意味着CPU可以进行32位寻址,最大寻址空间为4GB。
回头看下,一起是CS:IP = 0xFFFFF; 现在5个F 扩展到8个F。 4 * 8总共是32位的寻址,为4GB寻址。

在这里插入图片描述

1.4.2.3对8259A重新编程

将中断号重新编程布局,采用新的中断。显然,之前使用过的老的内存搬移中断已经不需要了,而这个时候的中断系统也将更加完善。

1.4.2.4CR0寄存器PE位置一,处理器变为保护模式

变为保护模式的一个重要特征就是根据GDT决定后续执行哪里的程序。
jmpi 0, 8
0是段内偏移
8是gdt表中的偏移,每个元素占八位,刚好是第一个元素,内核代码段

该元素中获取内核读代码段的基址位0x0
因此跳到了 0x0000000处。

1.5 总结

可以看到,bootsect 和setup这两个模块主要做了两件事:
1. 内存搬移
2.为保护模式做准备

早期的linux加载使用多次腾挪,在有限的区域内对内存进行最大化利用,每当使用完一段代码,立即将其覆盖。如何在这种代码片之间进行交互这是一个技巧。比如直接将自己拷贝一份,然后换个cs寄存器直接切到另一份代码中运行,又比如无缝连接直接jmp, 又比如通过压栈+ret直接跳转到main,这些都是为了照顾这些片段代码直接的连续操作的技巧。

另一个重点就是描述符,可以通过《80X86汇编语言程序设计教程》来详细学习GDT, IDT。
总的来说,GDT 是用来找段的,代码段,数据段,进程的tss ldt,都要依靠它来跳转到相应的地址中

IDT则是用来挂接中断的,后面许多外设,系统调用都是通过中断的方式和内核交互,以后和这GDT IDT打交道的位置不会少,需要先足够了解才行。**

从现在的角度来看这分linux 0.11 的启动,会发现他是没有安全启动,镜像安全校验一说的,后续安全这块也将越来越重要。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值