操作系统的启动

明确:

1、编译器将程序翻译成了机器指令,CPU执行的是机器指令。

2、操作系统本身也是个程序,执行之后会生成一大堆机器指令。

回顾:当按下开机键的那一刻,在主板上提前写死的固件程序 BIOS 会将硬盘中启动区的 512 字节的数据,原封不动复制到内存中的 0x7c00 这个位置,并跳转到那个位置进行执行。(只要硬盘中的 0 盘 0 道 1 扇区的 512 个字节的最后两个字节分别是 0x55 和 0xaa,那么 BIOS 就会认为它是个启动区。)所以作为操作系统的开发人员,仅需要把操作系统最开始的那段代码,编译并存储在硬盘的 0 盘 0 道 1 扇区即可。之后搬运工BIOS就会把它放到内存去跳转执行。

 

 

 

 Linux-0.11 的最开始的代码,就是这个用汇编语言写的 bootsect.s,位于 boot 文件夹下。通过编译,这个 bootsect.s 会被编译成二进制文件,存放在启动区的第一扇区。

 

 随后就会由 BIOS 搬运到内存的 0x7c00 这个位置,而 CPU 也会从这个位置开始,不断往后一条一条语句无脑地执行下去。之后的一切将从这两行代码开始:

mov ax,0x07c0
mov ds,ax

  这段代码是用汇编语言写的,含义是把 0x07c0 这个值复制到 ax 寄存器里,再将 ax 寄存器里的值复制到 ds 寄存器里。那其实这一番折腾的结果就是,让 ds 这个寄存器里的值变成了 0x07c0。

 

 当我们写一段汇编语言时,实际上仅仅写了偏移地址,比如:

mov ax, [0x0001]

  相当于

mov ax, [ds:0x0001]

  ds是一个16位的段寄存器,具体表示数据段寄存器,在内存寻址时充当段基址的作用。ds 是默认加上的,表示在 ds 这个段基址处,往后再偏移 0x0001 单位,将这个位置的内存数据,复制到 ax 寄存器中。

        这个 ds 被赋值为了 0x07c0,由于 x86 为了让自己在 16 位这个实模式下能访问到 20 位的地址线这个历史因素,所以段基址要先左移四位。那 0x07c0 左移四位就是 0x7c00,那这就刚好和这段代码被 BIOS 加载到的内存地址 0x7c00 一样了。也就是说,之后再写的代码,里面访问的数据的内存地址,都先默认加上 0x7c00,再去内存中寻址。

因为BIOD规定死了把操作系统代码加载到内存 0x7c00,所以里面的各种数据都被偏移了这么多,所以把数据段寄存器ds设置为这个值,方便了之后访问内存时利用这个段基址进行寻址。

 

 之后的代码:

mov ax,0x07c0
mov ds,ax
mov ax,0x9000
mov es,ax
mov cx,#256
sub si,si
sub di,di
rep movw

  sub si,si意思位si=si - si,si是寄存器,这个举动将寄存器里的值清零。此时几个寄存器分别被赋初值

 

赋值为了执行:

rep movw

 意思是     “重复执行后面的指令    字(16位)",执行cx=256次,从 ds:si 处复制到 es:di 处,一次复制两个字节。这条命令就是为了将内存地址 0x7c00 处开始往后的 512 字节的数据,原封不动复制到 0x90000 处。

 

 后面接一个跳转指令

jmpi go,0x9000
go: 
  mov ax,cs
  mov ds,ax

  jmpi 是一个段间跳转指令,表示跳转到 0x9000:go 处执行。go 是一个标签,最终编译成机器码的时候会被翻译成一个值,这个值就是 go 这个标签在文件内的偏移地址。这个偏移地址再加上 0x90000,就刚好是 go 标签后面那段代码 mov ax,cs 此时所在的内存地址了。

那假如 mov ax,cx 这行代码位于最终编译好后的二进制文件的 0x08 处,那 go 就等于 0x08,而最终 CPU 跳转到的地址就是 0x90008 处。

总结:一段 512 字节的代码和数据,从硬盘的启动区先是被移动到了内存 0x7c00 处,然后又立刻被移动到 0x90000 处,并且跳转到此处往后再稍稍偏移 go 这个标签所代表的偏移地址处,也就是 mov ax,cs 这行指令的位置。简称”自己给自己挪地方“  为甚末跳转?

这些指令都是cpu中的

go: mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,#0xFF00

  这段代码的直意思就是把 cs 寄存器的值分别复制给 dses 和 ss 寄存器,然后又把 0xFF00 给了 sp 寄存器。cs 寄存器表示代码段寄存器,CPU 当前正在执行的代码在内存中的位置,就是由 cs:ip 这组寄存器配合指向的,其中 cs 是基址,ip 是偏移地址。

其实操作系统在做的事情,就是给如何访问代码,如何访问数据,如何访问栈进行了一下内存的初步规划。其中访问代码和访问数据的规划方式就是设置了一个基址而已,访问栈就是把栈顶指针指向了一个远离代码位置的地方而已。

 

posted @ 2022-04-13 19:32  七月猫合  阅读(278)  评论(0)    收藏  举报