《Linux0.11源码趣读》学习笔记 Day 1

976 阅读5分钟

开机后最开始的两行代码

本节问题

  • 开机后执行的第一行操作系统代码是什么?
  • 在这行代码之前发生了什么?

开机后初始化指向BIOS

PC寄存器(CPU中)存储着将要执行的指令在内存中的地址 按下开机键 ——> 初始化PC寄存器(CPU) ——> CPU按照PC寄存器的数值 ——> 寻找并执行内存中对应地址处的指令

sequenceDiagram
    participant PC_Register
    participant CPU
    participant Memory
    PC_Register->>CPU: 初始化PC寄存器
    CPU->>PC_Register: 存储着将要执行的指令在内存中的地址
    Note right of CPU: 按下开机键
    CPU->>Memory: 按照PC寄存器的数值寻找并执行内存中对应地址处的指令

问题一:初始化的值是多少?

  • 答:(Inter手册规定)开机后PC寄存器初始化为0xFFFF0(从这个内存地址开始执行CPU第一条指令)

问题二:CPU根据这个地址值去内存中找指令,为什么会找到BIOS?

  • 答:CPU是把0xFFFF0作为CPU的地址线型号传输出去,去这个地址线对应的位置处找。

问题三:CPU的地址线连接的设备还不仅仅是内存?

  • 答:CPU的地址线连接的设备有:RAM(内存)、ROM(BIOS)、Memory-Mapped IO(一些外设的IO端口)
  • 答(二):0xFFFF0指向了BIOS程序所在地ROM区域

读取硬盘启动区(第一扇区)

启动区定义:硬盘中的0盘0道1扇区(第一扇区)的512个字节的最后两个字节分别是0x55和0xaa,BIOS就会认为它是个启动区

!!!注意:启动区一定在第一扇区,但第一扇区并不一定是启动区

对于操作系统而言:此时BIOS的工作是把512字节的二进制数据从硬盘搬运到了内存

对于操作系统的开发人员:我们仅需要把操作系统最开始的那段代码编译出来,并存储在硬盘的0盘0道1扇区即可

eg:在Linux0.11中,最开始的代码是汇编语言写的bootsect.s,位于boot文件夹下,通过编译,bootsect.s会被编译成二进制文件,存放在启动区的第一扇区

加载到内存0x7c00位置并跳转

将操作系统代码编译好存放在硬盘的启动区 ——> 开机后,BIOS程序将代码搬运到内存的0x7c00位置 ——> CPU会从这个位置开始,一条指令一条指令不断地往后执行下去

问题一:为什么要将操作系统代码搬运到0x7c00?

  • 答:这是一开始的开发者规定的,不管是谁开发的操作系统,只要是用BIOS这种启动方式,都要假定自己将会被搬运到内存0x7c00这个位置,否则运行就会出错

问题二:BIOS是如何完成从硬盘加载到内存这个目标?

  • 答:BIOS程序除了这个工作之外,还有很多计算机自检程序,不过这不是研究重点(小声BB:把硬盘中的数据加载到内存也不是啥难事)
  • 答:BIOS只帮我们把启动区的这512字节加载到内存,仍在硬盘其他扇区的操作系统代码得我们自己来处理。 bootsect.s这个文件的前两行代码会被编译并存储在启动区,然后搬运到内存0x7c00,之后也会成为CPU执行的第一个指令,bootsect.s前两行代码如下
mov ax,0x07c0
mov ds,ax

代码含义:把0x07c0这个值复制到ax寄存器里,再将ax寄存器里的值复制到ds寄存器里 代码执行结果:让ds这个寄存器里的值变成了0x07c0 ds寄存器:一个16位的段寄存器,具体表示数据段寄存器,在内存寻址时充当段基址的作用 段基址:我们用汇编写一个内存地址时,实际上仅仅是写了偏移地址 eg:

mov ax, [0x0001]

相当于

mov ax, [ds:0x0001]

ds是默认加上的,表示在ds这个段基址处,往后在偏移0x0001单位,将这个位置的内存数据复制到ax寄存器中。

问题三:为什么ds寄存器的数值要赋值为0x07c0?

  • 答:x86为了让自己在16位的实模式下,能访问到20位的地址线,所以要把段基址先左移四位。0x07c0左移四位就是0x7c00,这刚好就和这段代码被BIOS加载到的内存地址0x7c00一样了
  • 答:也就是说,在以后写的代码里,访问的数据的内存地址都先默认加上0x7c00,然后再去内存中寻址

问题四:为什么统一加上0x7c00这个数?

  • 答:BIOS规定死了把操作系统代码加载到内存0x7c00,里面的数据自然就全都被偏移了这么多,所以把数据段寄存器ds设置为这个值,方便了以后通过这种基址的方式访问内存里的数据

总结

本节的代码主要完成了两步操作:

  1. 第一步:BIOS将操作系统代码加载到内存0x7c00;
  2. 第二步:通过mov指令将默认的数据段寄存器ds的值改为0x07c0,方便以后的基址寻址方式