电脑从开机到执行main函数的过程(启动盘加载操作系统程序,完成main函数所需要的准备工作)

从开机到main函数的执行分为三步,目的是实现从启动盘加载操作系统程序,完成main函数所需要的准备工作。第一步,启动BIOS,准备实模式下的中断向量表和中断服务程序;第二步,从启动盘加载操作系统程序到内存,加载操作系统程序工作就是利用第一步中准备的中断服务程序实现的;第三步,为执行32位的main函数做过渡工作。

操作系统是一款做管理的软件,计算机上有操作系统,才能使我们使用计算机。

1 启动BIOS

启动BIOS,准备实模式下的中断向量表和中断服务程序。

1.1 BIOS启动原理

计算机的运行离不开程序,而程序被加载在内存中才能被CPU执行,但是内存是半导体材料做成的,所以断电之后内存中的所有东西都就没有了。在开机时RAM中什么也没有,就要靠BIOS加载软盘中的操作系统。BIOS是一组固化到计算机内主板上的一个ROM(只读存储器:一次写入,永远存在,不能修改)芯片上的程序。

CPU硬件逻辑设计为加电瞬间强行将CS的值置为0xF000、IP的值置为0xFFF0,这样CS:IP就指向0xFFFF0这个地址位置,而BIOS程序的入口地址恰恰就是0xFFFF0。这是一个纯硬件完成的动作。

1.2 BIOS在内存中加载中断向量表和中断服务程序

CS:IP已经指向0xFFFF0单元,就意味着BIOS(本文中选用的BIOS程序只有8KB)开始启动了,随着BIOS的执行,屏幕上会显示显卡的信息、内存的信息……着说明BIOS正在检查显卡、内存……这期间BIOS会在内存中建立中断向量表和中断服务程序。

BIOS程序会在内存最开始的位置(0x00000)用1KB的空间构建中断向量表(中断向量表共有256个,每个占4个字节,其中两个是CS的值,2个是IP的值),在紧挨着中断向量表的位置用256字节的内存空间构建BIOS数据区,并在大约57KB以后的位置(0x0E05B)加载了约8KB左右的与中断向量表相应的中断服务程序(每个中断向量指向一个具体的中断服务程序)。如图1-1所示。

图1-1 BIOS在内存加载中断向量表和中断服务程序

2 加载操作系统内核程序

加载操作系统内核程序,并为保护模式做准备。

对于Linux0.11操作系统而言,计算机将分三批逐次加载操作系统的内核代码。第一批由BIOS中断int 0x19把第一扇区bootsect的内容加载到内存;第二批、第三批在BIOS的指挥下,分别把其后的4个扇区和随后的240个扇区的内容加载至内存。

2.1 加载第一部分内核代码—引导程序(bootsec)

现在我们基本上都是将硬盘设置为启动盘。Linux0.11是1991年设计的操作系统,那时常用的启动设备是软驱以及其中的软盘。由于我们把软盘作为启动设备,计算机的硬件体系结构的设计与BIOS联手操作,会让CPU接收到一个int 0x19中断。CPU接收到这个中断后,会立即在中断向量表中找到int 0x19中断向量,中断向量中存放着int 0x19向对应的中断服务程序入口地址0x0E6F2。这个中断服务程序的作用就是把软盘第一扇区中的程序(512B)加载到内存中的指定位置(0x07C00处)。这样制作的第一扇区就称启动扇区。这个中断服务程序的功能是BIOS事先设计好的,代码是固定的,与Linux操作系统无关。

从此计算机开始和软盘上的操作系统产生了关联。第一扇区中的程序由bootsect.s中的汇编程序汇编而成。这是计算机字开机以来内存中第一次有了Linux操作系统自己的代码,虽然只是启动代码。

2.2 加载第二部分内核代码—setup

2.2.1 bootsect对内存的规划

bootsect的作用就是把进行内存规划,为了把第二、第三批程序加载到内存合适的位置。

通常,我们是用高级语言编写应用程序的,这些程序是在操作系统的平台上运行的。操作系统本身使用的是汇编语言,没有高级语言编译器为操作系统提供保障,只有靠操作系统的设计者把内存的安排想清楚,确保无论操作系统如何运行,都不会出现代码与代码,数据与数据,代码与数据之间的相互覆盖的情况。

2.2.2 复制bootsect

bootsect启动程序将它自身(全部的512B内容)从内存0x07C00处复制到内存0x90000。

在这次的复制过程中DS(0x07C0)与SI(0x0000)联合使用构成了源地址0x07C00,ES(0x9000)与DI(0x0000)联合使用构成了目标地址。此时,内存中就有了两份bootsect代码了。

2.2.3将setup程序加载到内存中

加载setup这个程序,要借助BIOS提供的int 0x13中断向量所指向的中断服务程序来完成。图2-1标注了int 0x13中断向量的位置以及这个中断向量所指向的磁盘服务程序的入口位置。

int 0x19所指向的启动加载程序是由BIOS执行的,而int 0x13的中断服务程序是Linux操作系统自身的启动代码bootsect执行的。

图2-1 调用int 0x13中断

执行int 0x13指令,产生int 0x13中断,通过中断向量表找到中断服务程序,将软盘第二扇区开始的4个扇区,即setup.s对应的程序加载到内存的SETUPSEG(0x90200)处。它紧挨着复制后的bootsect的尾端,所以bootsect始于setup连在一起的。

2.3 加载第三部分内核代码—system模块

系统模块载入内存仍然使用BIOS提供的 int 0x13中断,方法基本与图1-2基本相同。

这次载入从底层技术上看,与前面的setup程序的载入没有本质区别。比较突出的特点是这次加载的扇区数是240个,是之前的60倍。

第三批程序加载完成后,整个操作系统的代码已经全部加载至内存了。之后需要再次确定一下跟设备号。

  1. 开始向2位模式转变

操作系统要使计算机在32位保护模式下工作。这期间要做大量的工作,并且持续工作到操作系统的main函数的执行过程中。操作系统执行的操作包括打开32位的洗凝脂空间,打开保护模式、建立保护模式下的中断响应机制等与保护模式配套的相关工作、建立内存的分页机制,最后做好调用main函数的准备。

3.1 关中断并将system移动到内存地址起始位置0x00000

这个准备工作要先关闭中断,即将CPU标志寄存器(EFLAGS)中的中断允许标志位(IF)置0,这意味着,程序在接下来的执行过程中,无论是否发生中断,系统都不会再对此中断进行响应。直到main函数中能够适应保护模式的中断服务体系被重建完毕才会打开中断,而那时候响应中断的服务程序不再是BIOS提供的中断服务程序,取而代之的是由系统自身提供的中断服务程序。

接下来,setup程序将位于0x10000的内核程序复制到内存的起始地址0x00000处。如图3-1所示。

图3-1 复制system模块至内存起始处

system模块复制到ox00000处,收回了刚刚结束寿命的程序所占空间,让内核代码占据内存物理地址最开始、天然的、有利的位置,废除了BIOS的中断向量表,等同于废除了BIOS提供的中断向量表,也就废除了16位的中断机制。但操作系统不能没有中断,这么做的根本原因是16位的中断机制对于32位的操作系统显然是不合适的,我们要建立32位的操作系统。

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

setup程序继续为保护模式做准备。此时要通过setup程序自身提供的数据信息对中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR)进行初始化。

3.3 打开A20,实现32位寻址

打开A20,意味着CPU可以进行32位寻址,最大寻址空间为4GB。

Linux0.11最大只能支持16MB的物理内存,但是其线性寻址空间已经是4GB了。如图3-2、3-3所示。

图3-2 打开A20后内存寻址空间的变化

图3-3 打开A20后物理内存寻址空间的变化

 

3.4为保护模式下执行head.s做准备

为了建立保护模式下的中断机制,setup程序对可编程中断控制器8259A进行重新编程。CPU再保护模式下,int 0x00~int 0x1F被Intel保留作为内部(不可屏蔽)中断和异常中断。如果不对8259A重新编程,int 0x00~int 0x1F中断将会被覆盖。

3.5 head.s开始执行

在执行main函数之前,要先执行三个由汇编代码生成的程序:bootsect、setup和head。之后才执行由main函数开始的用C语言程序编写的操作系统内核程序。

第一步,加载bootsect到0x7C00,然后复制到0x90000;第二步,加载setup到0x90200。

head程序与他们加载方式有所不同。先将hand.s汇编成目标代码,将用C语言编写的内核程序编译成目标代码,然后链接成system模块。也就是说,system模块里面既有内核程序,又有head程序。两者是紧挨着的。head在前,内核程序在后,实际上head程序就在0x00000处。head程序、以main函数开始的内核程序在system模块中的布局示意图如图3-4所示。

图3-4 system在内存中的分布示意图

head程序除了做一些调用main的准备工作之外,还做一些对内核程序在内存中的布局及内核程序的正常运行有重大意义的事,就是用程序自身的代码在程序自身所在的内存空间创建了内核分页机制,即在0x000000的位置创建了页目录表、页表、缓冲区、GDT、IDT,并将head程序已经执行的过的代码所占内存空间覆盖。这意味着head程序自己将自己废弃,main函数即将开始执行。

 

 

 

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值