1-操作系统启动前的工作
Posted PacosonSWJTU
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1-操作系统启动前的工作相关的知识,希望对你有一定的参考价值。
【README】
1.本文总结自B站《操作系统(哈工大李治军老师)》的视频讲解,非常棒,墙裂推荐;
【1】计算机上电
1) 问题: 这神秘的黑色背后发生了什么 ?计算机是怎么工作的?
【2】从白纸到图灵机
计算3+2的加法:
- 控制器从纸带上读取已经写好的3 2 +;读取后,执行加法逻辑得到5,并把5再次送入到纸带上;但问题是,这个控制器只会做加法,不能做其他,这就是问题所在了;
【3】从图灵机到通用图灵机
如果一个控制器,像一个只会做饼的厨师那样,就显得很低级,不灵活,没法扩展了;
我们需要的是能够看懂菜谱的厨师一样的控制器;能够灵活与扩展,不仅仅只会做加法,还会做其他算术或逻辑运算;
厨师读到菜谱1,可以做菜品1; 厨师读到菜谱2,可以做菜品2; |
控制器也一样:
先预先设置控制器动作逻辑; 然后读取数据对象,根据预设动作进行相应的计算; |
通用图灵机:能够设置控制器动作逻辑的控制机;其中控制器动作逻辑就是应用程序;
【4】从通用图灵机到计算机
冯诺依曼存储程序思想,在1946年提出 :
4.1)存储程序的主要思想:将程序和数据存放到计算机内部的存储器中,计算机在程序的控制下一步步处理;
把程序或指令存储到内存(主存)里,然后用一条指令指针(IP或PC)指向某条指令;把该指令载入到控制器(取指),从而形成控制逻辑,即指令描述了控制器如何工作,接着控制器根据指令进行相应操作(执行); |
小结:计算机怎么工作的呢? 取指+执行;
4.2)冯诺依曼计算机由5大部件组成:
- 输入设备;
- 输出设备;
- 存储器;
- 运算器;
- 控制器;
那windows系统启动的黑色背景如何产生的?
- 经过刚刚的分析,控制器是在指令下工作的,指令是由PC或IP的指向得到的;那计算机启动时,PC或IP指向的第一条指令是什么?
【5】打开电源,计算机执行的第一句指令是什么?
【问题】打开电源,计算机执行的第一句指令是什么?
1)Bios:Basic Input Output System, 基本输入输出系统;(开机时,唯一有程序的地方)
开机执行步骤:
- 步骤1:开机后,CS=0xFFFF, IP=0x0000 ;CS-Code Segment-代码段寄存器;IP=Instruction Point=指令指针寄存器;
- 步骤2:读取CS和IP的值,并把CS的地址左移4位与IP寄存器的地址相加,得到BIOS程序所在地址 0xFFFF0 ;
- 步骤3:从只读存储器ROM中 CS:IP地址上读取BIOS程序到控制器,控制器根据BIOS程序进行相应操作;
- 步骤4:BIOS程序首先检查 ram,主板,键盘,显示器,软硬磁盘;
- 步骤5:接着BIOS程序把磁盘的0磁道0扇区(即第1个扇区512字节)的数据读取到以0x7c00为首地址的内存单元中;其中0磁盘0扇区即第1个扇区就是操作系统的引导程序所在扇区(又称引导扇区),1个扇区512k;
- 步骤6:设置cs=0x07c0,ip=0x0000;所以可以得到内存地址0x7c00;
接下来,控制器就会执行内存地址0x7c00保存的程序,即操作系统引导扇区的程序;
【5.1】0x7c00存放的代码是什么 ?
1)引导扇区代码示例 :
Bootsect.s 中的 .s 指的是 汇编代码assembly;其中bootsect指的是引导扇区,boot sector;
所以引导扇区代码,是一段汇编代码;
2)为什么要用汇编代码? 而不用C呢?
- C语言无法控制某个变量在内存中的哪个位置上存储;
- 而因为汇编的每一条指令都转为了机器指令,汇编可以控制变量存储的内存地址;
- 又引导扇区需要对程序进行完整控制,包括变量存储的内存地址;
3)Bootsect.s 引导扇区的代码要做什么事情呢?
引导扇区程序(0x7c00处存放的代码)的汇编指令执行步骤:
表1 引导扇区汇编程序bootsect执行步骤
步骤 | 指令 | 描述 |
1 | mov ax,#BOOTSEG | #BOOTSEG=0x07c0; |
2 | mov ds,ax | 把 0x07c0送入ds寄存器; |
3 | Mov ax,#INITSEG | #INITSEG=0x9000; |
4 | Mov es,ax | 把 0x9000送入es寄存器 |
5 | Mov cs,#256 | 把256赋值给cs寄存器; |
6 | Sub si si | 把0x0000送入si寄存器; |
7 | Sub di di | 把0x0000送入di寄存器; |
8 | Rep movw | 重复移动256个字=512字节; 源地址:ds:si=0x07c0:0x0000;即0x7c00; 目标地址:es:di=0x9000:0x0000;即0x90000; 即把0x7c00的代码拷贝到0x90000内存地址 |
9 | jmpi go, INITSEG | jump intersegment-段间跳转: 目标地址=cs:ip=INITSEG:go=0x9000:go; 即下面进入到go为标号(锚点)的汇编指令执行; |
补充,寄存器列表:
- 1. AX――累加器(Accumulator),使用频度最高
- 2. BX――基址寄存器(Base Register),常存放存储器地址
- 3. CX――计数器(Count Register),常作为计数器
- 4. DX――数据寄存器(Data Register),存放数据
- 5. SI――源变址寄存器(Source Index),常保存存储单元地址
- 6. DI――目的变址寄存器(Destination Index),常保存存储单元地址
- 7. BP――基址指针寄存器(Base Pointer),表示堆栈区域中的基地址
- 8. SP――堆栈指针寄存器(Stack Pointer),指示堆栈区域的栈顶地址
- 9. IP――指令指针寄存器(Instruction Pointer),指示要执行指令所在存储单元的地址。IP寄存器是一个专用寄存器。
4)为什么要把0x7c00的引导扇区程序拷贝到0x90000呢?
为了腾出空间,腾出空间干什么呢 ?
5)jmpi go, INITSEG 是什么作用(引导扇区代码最后一条指令)?
jump intersegment-段间跳转: 目标地址=cs:ip=INITSEG:go; |
图 1 启动盘结构图
表1中的步骤9 执行了 jmpi go, INITSEG;意思是跳转到目标地址=cs:ip=INITSEG:go=0x9000:go 去执行;所以接着执行上图的 go标号的汇编指令,步骤如下:
步骤 | 指令 | 描述 |
go标号 | ||
1 | Mov ax,cs | 把cs=0x9000赋值给ax |
2 | Mov ds,ax | 把值0x9000赋值给ds |
3 | Mov es,ax | 把值0x9000赋值给es |
4 | Mov ss,ax | 把值0x9000赋值给ss |
5 | Mov sp,#0xff00 | 把值0xff00赋值给sp |
load_setup标号 | 载入setup模块 | |
7 | Mov dx,#0x0000 | |
8 | Mov cx,#0x0002 | ch=0x00 cl=0x02 |
9 | Mov bx,#0x0200 | 把0x0200赋值给bx |
10 | Mov ax,#0x0200+SETUPLEN | //SETUPLEN=4; 把0x0204赋值给ax; ah=0x02 al=0x04 |
11 | Int 0x13 | //BIOS中断,读取磁盘数据 |
12 | Jnc ok_load_setup | //跳转到 ok_load_setup标号(锚点) |
13 | Mov dx,#0x0000 | |
14 | Mov ax,#0x0000 | // 复位 |
15 | Int 0x13 | |
16 | J load_setup | // 重读 |
最重要的指令是 int 0x13,这是一个BIOS中断;这个中断从磁盘读取数据,磁盘寻址如下:
从ch:cl(柱面号:开始扇区=0x00:0x02)开始读取磁盘数据,共计读取ah:al(0x02:0x04读磁盘:扇区数量)个扇区到内存地址es:bx(0x9000:0x0200);
即从第2个扇区读取4个扇区的数据(setup数据)到内存0x90200,因为第1个扇区存储了引导扇区程序;
6)接着,读入setup模块后,执行 ok_load_setup 锚点:
Int 0x10 表示 一种BIOS中断,用于显示字符;显示的字符为 #msg1 即bp寄存器;
ok_load_setup标号的程序执行步骤如下:
表3 ok_load_setup汇编程序执行过程
步骤 | 指令 | 描述 |
ok_load_setup标号 | // 载入setup模块 | |
1 | Mov dl,#0x00 | 把0x00赋值给dl |
2 | Mov ax,#0x0800 | 把0x0800赋值给ax,即ah:al=08:00 |
3 | Int ox13 | //BIOS中断,读取磁盘数据 |
4 | Mov ch,#0x00 | 把0x00赋值给ch |
5 | Mov sectors,cx | 把cx赋值给sectors |
Mov ah,#0x03 | 把0x03赋值给ah | |
7 | Xor bh,bh | 异或运算,把0x00赋值给bh |
8 | Int 0x10 | //BIOS中断,读光标 |
9 | Mov cx,#24 | 把24赋值给cx,表示要输出24个字符; |
10 | Mov bx,#0x0007 | 把0x0007赋值给bx;其中0007是显示属性; |
11 | Mov bp,#msg1 | 把msg1赋值给bp;bp存储要显示的字符; msg1是偏移;就是把 Loading system字符串打到屏幕上的光标位置; 其中byte13,10 分别是换行和回车ascii码 |
12 | Mov ax,#1301 | 把1301赋值给ax |
13 | int 0x10 | //BIOS中断,显示字符 |
14 | Mov ax,#SYSSEG | //SYSSEG=0x1000 |
15 | Mov es,ax | 把ax赋值给es; |
16 | Call read_it | //读取system模块 read_it 仍然是13号中断,把操作系统后面的代码读入到内存;读完之后,bootsect汇编文件执行完成; |
Jmpi 0,SETUPSEG | // SETUPSEG=0x9020 jump intersegment-段间跳转: 目标地址=cs:ip=SETUPSEG:0=SETUPSEG:0; 即下面跳转到SETUPSEG为标号(锚点)的汇编指令执行; |
如果我们要把 Loading System字符串修改为 diyos is loading字符串(长度15)在屏幕上显示的话,还需要把第9条汇编指令修改为 mov cx,#15;
小结:
- bootsect.s 文件中的汇编代码干的事情就是,把操作系统读入到内存,打出一个logo或字符串(diyos is loading);
Bootsect.s 汇编程序执行完成后,就需要把控制权交给下一个程序执行,即setup程序;
如何跳转到setup程序呢?
- 执行 jmpi 0, SETUPSEG 即可;根据图1,我们知道SETUPSEG 的汇编程序在启动盘的第2~5个扇区;
【总结】计算机启动过程
以上是关于1-操作系统启动前的工作的主要内容,如果未能解决你的问题,请参考以下文章