现在开始学习操作系统如何把启动区以外的操作系统代码从硬盘搬到内存
之前以经把数据段寄存器ds和代码段寄存器cs设置为0x9000方便之后程序访问代码和数据
将栈顶地址ss:sp设置在了离代码段位置0x90000足够幺幺的0x9ff00,保证栈向下发展不会轻易覆盖掉已有代码
即之前已经设置了如何访问数据的数据段,如何访问代码的代码段以及如何访问栈的栈顶指针,就是初步做了一次内存规划。从cpu的角度来看,访问内存就这么三块地方
接下来的代码是开始把硬盘中剩下的操作系统代码复制到内存
load_setup:
mov dx,#0x0000 ; drive 0, head 0
mov cx,#0x0002 ; sector 2, track 0
mov bx,#0x0200 ; address = 512, in 0x9000
mov ax,#0x0200+4 ; service 2, nr of sectors
int 0x13 ; read it
jnc ok_load_setup ; ok - continue
mov dx,#0x0000
mov ax,#0x0000 ; reset the diskette
int 0x13
jmp load_setup
ok_load_setup:
...
代码中汇编指令int 0x13表示发起0x13号中断
上面的mov指令是给寄存器赋值,这四个寄存器都是作为这个中断程序的参数,这叫通过寄存器传参。与之对应的另一种传参方式是通过栈传递(在C语言中应用很多)
发起中断后,cpu会通过这个中断号0x13
去寻找对应的中断处理程序入口地址,并跳转过去执行。逻辑上相当于执行了一个函数
0x13号中断处理程序是BIOS提前写好的,是读取磁盘的相关功能的函数
之后真正进入操作系统内核后,中断处理程序需要写操作系统的程序员重新编写(操作系统代码中各个模块注册自己的中断处理程序)
现在来看看Linux在此处用0x13号中断干了什么,其实从代码中就可以找到答案
load_setup:
mov dx,#0x0000 ; drive 0, head 0
mov cx,#0x0002 ; sector 2, track 0
mov bx,#0x0200 ; address = 512, in 0x9000
mov ax,#0x0200+4 ; service 2, nr of sectors
int 0x13 ; read it
...
从硬盘的第2个扇区开始,把数据加载到内存0x90200处,共加载了4个扇区,就像下面图中的这样
下面的jnc和jmp,表示成功和失败分别跳转到哪个标签处
load_setup:
...
jnc ok_load_setup ; ok - continue
...
jmp load_setup
ok_load_setup:
...
如果复制成功,就跳转到标签ok_load_setup;如果失败就不断重复这段代码(就是失败重试)
ok_load_setup:
...
mov ax,#0x1000
mov es,ax ; segment of 0x10000
call read_it
...
jmpi 0,0x9020
把从硬盘第 6 个扇区开始往后的 240 个扇区,加载到内存 0x10000 处
这样,整个操作系统的全部代码都从硬盘加载到内存中了,然后这些代码又通过jmpi 0,0x9020跳转到0x90200处,即从硬盘的第二个扇区开始的内容
极客时间《Linux0.11源码趣读》学习笔记day5