操作体系怎么把自己从硬盘搬运到内存
前文提要
栈顶地址被设置为了 0x9FF00,具体表现为栈段寄存器 ss 为 0x9000,栈指针寄存器 sp 为 0xFF00。
本节问题
- 操作体系是怎么把在硬盘中的自己搬运到内存中来的
拜访内存
在上文中,操作体系将寄存器ds和cs设置为了0x9000,并且将栈顶地址ss:sp设置在了离代码的方位0x9000足够遥远的0x9FF00,保证栈向下开展不会容易覆盖掉已有的代码。
寄存器 | 寄存器名 | 寄存器值 | 作用 |
---|---|---|---|
数据段寄存器 | ds | 0x9000 | 拜访数据 |
代码段寄存器 | cs | 0x9000 | 拜访代码 |
栈顶代码 | ss:sp | 0x9FF00 | 拜访栈 |
简略来说,设置了怎么拜访数据的数据段、怎么拜访代码的代码段、怎么拜访栈的栈顶指针——>初步做了一次内存规划
小结:硬盘中最开端的512字节加载到了内存中,但还有很多代码仍然在硬盘的其他扇区里
把剩余的操作体系代码从硬盘请到内存
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:
...
Tips :这里有两个int指令,留意,它们不是高档言语中的整型变量,而是汇编指令
int指令
INT 指令是汇编言语中的中止指令。它允许程序在运行时调用体系服务或驱动程序。INT 指令的格式为 INT imm8,其间 imm8 是一个8位立即数,它指示哪个中止号。例如,INT 0x21 指示调用功用 0x21 的中止服务程序。
INT 指令会将程序的控制权转移到中止服务程序。中止服务程序履行完后,程序会康复履行。
INT 指令在 DOS 体系中是常用的体系调用方式,在 Windows 和 Linux 体系中则不常用。
寄存器传参
寄存器传参是指在汇编言语中运用寄存器来传递参数给函数或子程序。这种方式不需求在内存中开辟空间来存储参数,而是直接运用寄存器来存储参数。
寄存器传参一般运用 CPU 内部的寄存器来传递参数。常用的寄存器有 EAX, EBX, ECX, EDX 等。不同的编译器和操作体系有不同的寄存器用法。
寄存器传参的长处是速度快,由于数据不需求在内存和寄存器之间来回复制。缺点是需求更多的寄存器来存储参数,如果参数过多可能会导致寄存器不够用。
中止
在汇编言语中,中止一般经过 INT 指令来完结。INT 指令会将程序的控制权转移到对应的中止服务程序(ISR)。中止服务程序履行完后会返回到本来的程序继续履行。
每个中止都有一个独特的中止号,在汇编中经过 INT 指令来指定中止号来调用不同的中止服务程序。例如,INT 0x21 会调用功用号为 0x21 的中止服务程序。
在汇编言语中,中止服务程序一般运用 IRET 指令来完毕中止处理并返回到本来的程序。
在汇编言语中还可以运用 CLI 和 STI 指令来控制中止,CLI 指令用来禁止中止,STI 指令用来康复中止。
示例
以上面的代码为例,int 0x13 表明建议 0x13 号中止,,这条指令上面的各种 mov 指令,用来给 dx、cx、bx、ax 赋值,这四个寄存器都是作为这个中止程序的参数。
扩展 :与寄存器传参对应的是栈传参,在C言语中运用很广。
伪*履行函数
建议中止后,CPU 就会经过这个中止号 0x13,去寻找对应的中止处理程序的进口地址,并跳转曩昔履行,逻辑上就相当于履行了一个函数。
Tips: 而 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 个扇区。
load_setup:
...
jnc ok_load_setup ; ok - continue
...
jmp load_setup
ok_load_setup:
...
此处的jnc和jmp指令表明成功和失利别离跳转到哪个标签处,相当于高档言语的if else
,如果复制成功,就跳转到 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,0×9020,跳转到 0x90200 处,便是硬盘第二个扇区开端处的内容。
操作体系的编译进程
编译操作体系的进程一般包括以下几个步骤:
- 装备:运用装备文件或命令行工具来装备体系的各种参数和选项。
- 编译:运用编译器将源代码编译成机器码。这一步可能需求花费很长时刻。
- 链接:将编译生成的机器码和其他库文件连接在一起,构成可履行文件。
- 装置:将可履行文件和其他必要的文件装置到体系中,以便启动和运行。
- 测验:对编译好的体系进行测验,以保证其功用正确且安稳。
关于现在现已阅览的代码,整个编译进程,便是经过 Makefile 和 build.c 配合完结的,终究达到这样一个作用:
- 把 bootsect.s 编译成 bootsect 放在硬盘的 1 扇区;
- 把 setup.s 编译成 setup 放在硬盘的 2~5 扇区;
- 把剩余的全部代码(head.s 作为最初,与各种 .c 和其他 .s 等文件一起)编译并链接成 system,放在硬盘的随后 240 个扇区。
一起,0x90200 处的代码,也便是咱们行将跳转到的内存地址处的代码,便是从硬盘第二个扇区加载过来的。而第二个扇区的最开端处,也便是 setup 二进制文件的内容,是由 setup.s 源代码文件编译后构成的。
总结
了解了操作体系怎么把自己从硬盘加载到内存,研究了 Linux 0.11 整个编译和加载的简要进程,操作体系的代码现已完全从硬盘被搬到内存中。
最后
笔记收拾:千石
内容来源:极客时刻《Linux源码趣读》学习笔记 Day 4
支持:点赞、谈论、收藏