L2揭开钢琴的盖子–逐渐深入操作系统

前言:

学习操作系统目的是理解操作系统的原理,而不是只停留在会调用接口的阶段。

正如标题,会弹钢琴不是我们的目标,我们是为了造钢琴,剖析钢琴的原理。

本文涉及到部分汇编知识,但是原理并不难,尽量以文字理清原理。

本文阅读学习大概需要30+分钟

目录

  • 0x00 一台电脑的启动
  • 0x01 计算机的核心结构
  • 0x02 打开计算机执行的第一条指令
  • 0x03 BIOS的介绍以及和操作系统的关系。
  • 0x04 操作系统的第一条代码(boot)
  • 0x05 接力棒的传递,boot跳转setup。
  • 0x06 小结

0X00 一台电脑的启动

图片[1]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

为什么电脑启动之后,都有一个启动动画,在这个过程中,计算机做了那些事?

想要得到这些答案,首先要清楚计算机是怎么工作的。

0X01 计算机的核心结构

计算机是在一个计算模型设计出来的

图灵借鉴了人在纸上计算的过程,将这个过程模拟出来,就是计算机的过程;

对于这样一个计算器他只能算加法; 最简单的加法器

图片[2]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

但是这种加法器几乎满足不了人们日常所需,于是人们对图灵机进行演化得到通用图灵机。

图片[3]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

对于这么一个机器核心就是设置控制器动作的编码 也就是菜谱 厨师对应不同的菜谱 采用不同的做法 从而制作出不同的菜肴。而控制器也就是这个原理,通过获取控制器动作, 从而采用不同的操作处理数据从而得到不同的程序

比如:

把ppt放进来 就可以播放ppt。

把qq放进来 可以执行qq进行聊天。

上述流程图就是计算机的核心工作大致流程,到后面冯·诺依曼提出了一个伟大的计算机构造。

图片[4]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

这种计算机构造运行程序时,把程序从外部存储器(外存)加载到内部存储器(内存)中来,通过指令指针(IP)指向对应指令的内存地址,并把地址载入cpu进行操作,这个过程称为取指执行

这就是计算机的核心结构

0X02 打开计算机执行的第一条指令

由上述计算机核心结构我们知道,计算机想要执行指令就离不开IP指针,打开电源时,想要计算机执行指令,那么这条指令一定存在内存中,那么刚开机时,内存中有什么?

图片[5]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

对于x86(英特尔)结构 刚通电时 内存有一部分是固化的 叫做(ROM BIOS基本输入输出系统)

他在内存中的地址是:0xFFFF0

CS和IP是8086CPU中两个关键的寄存器 ,它们指示了CPU当前要读取指令的地址。

CS : 代码段寄存器;IP : 指令指针寄存器。 在8086机中,任意时刻,CPU将CS:IP指向的内容当作指令来执行。

为什么CS:IP中0xffff左移四位是0xffff0

x86是20条地址总线,寻址方式是CS(段地址)+IP(偏移地址),20位对应的也就是5位的16进制数。

而左移四位对应的是2进制数的位数,而0xffff是16位进制数,如果将2进制数左移四位对应到16进制,那就是左移一位,也就是0xffff0。

CS<<4+ip 0xFFFF<<4+0X0000=0XFFFF0 这样开机时 就执行bios

0X03 BIOS的介绍以及和操作系统的关系。

BIOS 其实是一个英文缩略词,即 “Basic Input Output System” 四个单词的首字母组合,中文直译为“基本输入输出系统”,是一组固化到计算机内主板上一个 ROM 芯片上的程序。顾名思义,它保存着计算机最重要的基本输入输出的程序,还包括开机后自检程序和系统自启动程序。除此以外,它还可从 CMOS 中读写系统设置的具体信息。 其主要功能是为计算机提供最底层的、最直接的硬件设置和控制

简单来说,BIOS 是计算机启动时加载的第一个软件,BIOS 的设置直接关系到电脑是否可以正常启动,并影响到之后的使用效率。 Windows 操作系统,也是在 BIOS 的引导下进行工作的。

bios启动后去检查RAM(内存) 键盘 显示器 软硬磁盘 主版等硬件设施如果检查有问题 就不会执行启动操作系统,说明电脑坏了,如果没问题就去读取0磁道0扇区(操作系统的引导扇区,计算机常识)操作系统引导 执行来引导 操作系统 的启动

0x04 操作系统的第一条代码(boot)

操作系统的第一条代码就是操作系统的引导扇区。

刚才上述中bios以及将磁盘0磁道0扇区的指令读入内存地址为0x7c00处了。

图片[6]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客

操作系统的所有故事从这里开始。

引导扇区代码:bootsect.s 部分源码

.globl begtext, begdata, begbss, endtext, enddata, endbss

.text //文本段

begtext:

.data  //数据段

begdata:

.bss  //未初始化数据段

begbss:

.text

!对后续操作涉及的内存位置进行设置

BOOTSEG  = 0x07c0  

INITSEG  = 0x9000   

SETUPSEG = 0x9020   
 


!将源地址0x07C00的bootsect复制到目的地址0x90000

! 代码主要设定源地址和目标地址后,使用循环指令rep和移动指令movw进行移动

entry start

start:

mov ax,#BOOTSEG      !BOOTSEG  = 0x07c0

mov ds,ax            ! ds寄存器置为0x07c0

mov ax,#INITSEG      !INITSEG  = 0x9000

mov es,ax            !es寄存器置为0x9000

mov cx,#256          !计数器,提供需要复制的字的数量 256字=512字节

sub si,si            !sub是做减法操作,此处将si,di自己减自己,即置0。

sub di,di

                !移动时源地址ds:si=0x07c0:0x0000,目的地址es:di=0x9000:0x0000

                  !即将BIOS移动到0x9000(0x07c0用于放置bootsect.s)

    rep                  !rep指令作用:重复执行后面一句操作,并递减cx的值,直到cx=0停止

    movw                  !movw指令作用:这里从内存[si]处移动cx个字到[di];注意一次的移动单位是“字”,  mov指令+w(word)是一次移动一个字

jmpi    go,INITSEG    !将BIOS移动到0x9000后,跳转(go)到INITSEG(0x9000),CS=0x90000

部分知识补充:

后缀.s是汇编代码

为什么要做成一个汇编代码而不用c语言代码呢?

c语言编程产生汇编;编译过程会产生意想不到的问题;

比如 int a一个变量不能控制它产生到内存中的那个位置;

而汇编可以指定内存地址;而引导程序必须对内存地址进行绝对的控制,所以采用汇编语言;

图片[7]-L2揭开钢琴的盖子–逐渐深入操作系统-Drton1博客
此时boot扇区已经读取完成,接下来读取setup扇区 通过setup来完全启动我们的system 也就是操作系统。

0X05 接力棒的传递,boot跳转setup。

! 对ds,ex,ss,sp进行调整

go: mov ax,cs

mov ds,ax

mov es,ax

! put stack at 0x9ff00.  !下面两条指令是将堆栈指针sp指向0x9ff00处(即0x9000:0xff00)

mov ss,ax

mov sp,#0xFF00  ! arbitrary value >>512


!INT 0x13的使用方法:

!ah = 0x20-读磁盘扇区到内存; al = 需要读出的扇区数量;

!ch=磁道(柱面)号的低8位;  cl =开始扇区(位0-5),磁道号高2位(位6-7);

!dh = 磁头号;  dl = 驱动器号;

!es:bx = 指向数据缓冲区;

!如果出错则CF标志置位,ah中是出错码。

load_setup:

mov dx,#0x0000  ! drive 0, head 0 磁头号0,驱动器号0

mov cx,#0x0002  ! sector 2, track 0,开始扇区2,磁道号0

mov bx,#0x0200  ! address = 512, in INITSEG  es:bx=0x9000:0x0200 即数据缓冲区为0x90200

mov ax,#0x0200+SETUPLEN ! service 2, nr of sectors,SETUPLEN初始设置为4,ax=0x0210,ah=0x02-读磁盘扇区到内存,需要读出的扇区数量-4

int 0x13    ! read it 打开中断

jnc ok_load_setup   ! ok - continue jnc指令:如果(上条指令)成功,则跳转,即中断INT 0x13成功,则继续执行ok_load_setup

mov dx,#0x0000      ! 如果不成功,则复位驱动器,并重试(重新跳转函数load_setup)

mov ax,#0x0000  ! reset the diskette

int 0x13

j   load_setup

上述代码执行完后setup的四个扇区已经读入内存当中,读入后执行ok_load_setup模块。




ok_load_setup:

! Get disk drive parameters, specifically nr of sectors/track 利用BIOS中断0x13取磁盘参数表中当前启动引导盘的参数

! 这段代码主要还是获得每磁道的扇区数量,保存在了sectors中

mov dl,#0x00        !驱动器号为0

mov ax,#0x0800  ! AH=8 is get drive parameters,AH=8,是INT 0x13取磁盘驱动器的参数,AL初始化0,作为返回值

int 0x13            ! 打开中断

mov ch,#0x00


mov sectors,cx

mov cx,#0x03

xor bh,bh

int 0x10  //读光标

mov cx,#24  //系统开机时候加载显示的字符个数

mov bx,#0x0007 //7是显示属性

mov bp,#msg1  //加载显示的文本内容保存在msg1中 比如“Loading system”

mov ax,#1301

int 0x10//显示字符

mov ax.#SYSSEG//SYSSEG=0x1000

mov es,ax

call read_it //读入system模块


jmpi 0,SETUPSEG  //转入0x9020:0x0000执行setup.s

这部分代码就是系统开机时显示的开机加载动画与文字的功能实现。

0x06 小结

总结来讲就是计算机通电时 先启动固化在内存当作bios基本系统,然后bios去检查计算机硬件是否有故障,如果没有就去加载操作系统的引导程序boot ,boot在去读取setup 进而读取system,启动操作系统。

代码基本行行带有注释,需要一点汇编基础,但是整体理解起来并不困难。

© 版权声明
THE END
喜欢就支持一下吧
点赞1 分享
评论 抢沙发

请登录后发表评论