Linux内核(1)--从开机到main函数

 2023-09-10 阅读 26 评论 0

摘要:从开机到main函数1. 启动BIOS,准备中断向量表和中断服务程序1.1 启动BIOS1.2 BIOS在内存中加载中断向量表和中断服务程序2 加载操作系统内核程序2.1 加载第一部分内核代码--引导程序(bootsect)2.2 加载第二部分内核代码--setup2.3 加载第三部分内核代码

从开机到main函数

      • 1. 启动BIOS,准备中断向量表和中断服务程序
          • 1.1 启动BIOS
          • 1.2 BIOS在内存中加载中断向量表和中断服务程序
      • 2 加载操作系统内核程序
          • 2.1 加载第一部分内核代码--引导程序(bootsect)
          • 2.2 加载第二部分内核代码--setup
          • 2.3 加载第三部分内核代码--system模块
      • 3 为调用main函数做准备
          • 3.1 关中断并将system移动到内存地址起始位置0x00000
          • 3.2 设置中断描述符表和全局描述符表
          • 3.3 打开A20,实现32位寻址
          • 3.4 为保护模式下执行head.s做准备
          • 3.5 执行head.s

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

1. 启动BIOS,准备中断向量表和中断服务程序

1.1 启动BIOS

CPU上电之后将执行程序地址指向0xFFFF0处,这是一个纯硬件完成的动作,地址0xFFFF0的位置是BIOS程序的起始地址,也就是说,CPU上电之后,硬件引导执行BIOS程序。

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

BIOS程序被固化在计算机主板上的一块很小的ROM芯片里。BIOS程序在内存最开始的位置(0x00000)用1KB的内存空间构建中断向量表,在紧挨着它的位置用256字节的内存空间构建BIOS数据区,并在大约57KB以后的位置加载了8KB左右的与中断向量表相应的若干中断服务程序。
中断向量表中有256个中断向量,每个中断向量占4个字节,其中两个字节是CS的值,两个字节是IP的值,每个中断向量都指向一个具体的中断服务程序。

2 加载操作系统内核程序

现在开始要执行真正的boot操作了,也就是将硬盘中的操作系统程序加载至内存。计算机分三批逐次加载操作系统的内核代码:第一批由BIOS中断int 0x19把第一扇区的bootsect的内容加载到内存第二批和第三批在bootsect的指挥下,分别将其后的4个扇区和随后的240个扇区的内容加载至内存

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

如何增加电脑内核、下面先介绍几个概念:
bootsect:引导程序,负责扇区存储内容的引导和加载。
Interrupt Vector Table:中断向量表,记录所有中断号对应的终端服务程序的内存地址。
Interrupt Service:中断服务,通过中断向量表的索引对中断进行响应服务。
BIOS程序启动之后,其要做的就是找到硬盘并加载第一扇区。int 0x19中断向量所指向的中断服务程序,即启动加载服务程序,就是我们所说的bootsect引导程序。第一扇区被称为启动扇区,第一扇区的载入,标志着Linux中的代码即将发挥作用。

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

 BIOS已经把bootsect载入到内存了,现在它的作用就是把第二批和第三批程序陆续加载到内存中。bootsect要做以下三个步骤:
①规划内存
为了把第二批和第三批程序加载到内存中的适当位置,bootsect首先做的工作就是规划内存。
②复制bootsect
bootsect把自身的所有程序(512B)从0x07C00的位置复制到0x90000的位置。
③将setup程序加载到内存中
BIOS利用int 0x13中断向量所指向的终端服务程序来完成setup程序的加载。
这里说一下目前所用到的两个中断向量int 0x19和int 0x13的区别:
Ⅰ.int 0x19中断向量所指向的启动加载服务程序是BIOS执行的,而int 0x13中断服务程序是Linux操作系统自身的启动代码bootsect执行的;
Ⅱ.int 0x19的中断服务程序只负责把第一扇区的代码加载到0x7C00位置,而int 0x13的中断服务程序则不然,它可以根据设计者的意图,把指定扇区的代码加载到内存的指定位置。

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

BIOS借着BIOS中断 int 0x13,将240个扇区的system模块加载到内存。到此位置,第三批程序已经加载完毕,整个操作系统的代码已经全部加载至内存,bootsect的主要工作已经做完。
接下来setup程序开始执行。它做的第一件事情就是利用BIOS提供的中断服务程序从设备上提取内核运行所需要的机器系统数,其中包括光标位置、显示页面等数据,并分别从中断向量0x41和0x46向量值所指的内存地址处获取硬盘参数表。
到此为止,操作系统内核程序加载工作已完成。

3 为调用main函数做准备

之前操作系统是在16位实模式下工作的,接下来,操作系统要使计算机在32位保护模式下工作,这期间要做大量的重建工作,并且持续到操作系统的main函数执行,包括打开32位的寻址空间打开保护模式建立保护模式下的中断响应机制等于保护模式配套的相关工作、建立内存的分页机制,最后做好调用main函数的准备。

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

准备工作要先关闭中断,因为接下来的过程中,在main函数中能够适应保护模式的中断服务体系重建之前,系统都不应该收到中断源的影响。随后,setup程序将位于0x10000的内核程序复制到了内存地址起始位置0x00000处。这个复制动作将BIOS中断向量表和BIOS数据区完全覆盖,使它们不复存在,知道新的中断服务体系构建完毕之前,操作系统不再具备相应并处理中断的能力。

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

setup程序在完成3.1的步骤后,要通过自身的数据信息对中断描述符表寄存器(IDTR)和全局描述符表寄存器(GDTR)进行初始化设置。下面介绍有关的几个概念:
① GDT:全局描述符表(Global Descripter Table),在系统中唯一存放段寄存器内容的数组。可理解为所有进程的总目录表,其中存放每一个任务(task)局部描述符表(LDT)地址和任务状态段地址,完成进程中各段的寻址、现场保护与现场恢复。
② GDTR:GDT基地址寄存器(Global Descripter Table Register),保存GDT的起始地址
③ IDT:中断描述符表(Interrupt Descripter Table), 保存保护模式下所有中断服务程序的入口地址,类似于实模式下(1.2中)的中断向量表。
④ IDTR:IDT基地址寄存器(Interrupt Descripter Table Register),保存IDT的起始地址

3.3 打开A20,实现32位寻址

电脑开机main、打开A20,以意味着CPU可以进行32位寻址,最大寻址空间为4GB。内存范围从0xFFFFF变到0xFFFFFFFF,从1MB变为4GB。地址线从20根变成了32根。

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

为了建立保护模式下的中断机制,setup程序将对可编程中断控制器8259A进行重新编程。CPU在保护模式下,int 0x00~int 0x1F被Intel保留作为内部(不可屏蔽)中断和异常中断。如果不对8259A进行重新编程,int 0x00 ~ int 0x1F中断将被覆盖。
到这里,setup就执行完毕了。它为系统能够在保护模式下运行做一系列的准备工作。但这些准备工作还不够,后续的准备工作由head程序来完成。

3.5 执行head.s

在执行main函数之前,先要执行三个由汇编代码生成的程序,即bootsect,setup和head。之后才执行由main函数开始的C语言编写的操作系统内核程序。
根据前面的步骤,第一步,加载bootsect到0x7C00,然后复制到0x90000;第二步,加载setup到0x90200。这两段程序是分别加载,分别执行的。
head程序的加载方式是这样的:先将head.s汇编成目标代码,将用C语言编写的内核程序编译成目标代码,然后链接成system模块。也就是说,system模块里面既有内核程序,又有head程序,head程序在前,内核程序在后。所以head程序的名字为“head”。head程序在内存中占有25KB+184B的空间。根据3.1的内容,system模块加载到内存后,setup将system模块复制到0x00000位置,由于head程序在system的前面,所以实际上,head程序就在0x00000这个位置。head程序在内存中的分布示意图如下图所示:
在这里插入图片描述
head程序除了做一些调用main的准备工作之外,就是用程序自身的代码在程序所在的内存空间创建了内核分页机制,即在0x000000的位置创建了页目录表,页表,缓冲区,GDT,IDT,并将head程序已经执行过的代码所在的内存空间覆盖。这意味着head程序自己将自己废弃,main函数即将开始执行。

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/40494.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息