[源码]Linux 0.11核心代码与操作系统设计思想
文章目录
Linux 0.11 源码
学习资料:
- https://github.com/sunym1993/flash-linux0.11-talk Linux 0.11源码讲解 及 相关学习内容
- https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html Intel手册
第一部分:进入内核前的苦力活
1.1 最开始的两行代码
上图中启动区的定义:只要硬盘中的0盘0道1扇区的512个字节的最后两个字节分别是0x55和0xaa,BIOS就认为它是启动区。
汇编代码bootsect.s 经过编译成二进制文件,存放在启动区的第一扇区。
随后就会如刚刚所说,由 BIOS 搬运到内存的 0x7c00 这个位置,而 CPU 也会从这个位置开始,不断往后一条一条语句无脑地执行下去。
ds:16位的段寄存器,具体表示 数据段寄存器,在内存寻址时充当段基址的作用。
|
|
为什么是 0x7c00呢?
为什么主引导记录的内存地址是0x7C00?
0x7C00的由来:
1981年8月,IBM公司最早个人电脑IBM PC 5150搭配的操作系统是86-DOS。这个操作系统需要的内存最少是32KB。内存地址从0X0000开始编号,32KB内存就是
0x0000 ~ 0x7FFF
;8088芯片本身占用
0x0000 ~ 0x03FF
用来保存各种中断处理程序的储存位置。(主引导记录本身就是中断信号INT 19h的处理程序。)因此,内存剩下0x0400 ~ 0x7FFF
可以使用。**为了把尽量多的连续内存留给操作系统,主引导记录就被放到了内存地址的尾部。**由于一个扇区是512字节,主引导记录本身也会产生数据,需要另外留出512字节保存。所以, 它的预留位置变成了:
1
0x7FFF - 512 - 512 + 1 = 0x7C00
计算机启动后,32KB内存的使用情况如下。
1 2 3 4 5 6 7 8 9 10 11 12 13
+--------------------- 0x0 | Interrupts vectors +--------------------- 0x400 | BIOS data area +--------------------- 0x5?? | OS load area +--------------------- 0x7C00 | Boot sector +--------------------- 0x7E00 | Boot data/stack +--------------------- 0x7FFF | (not used) +--------------------- (...)
Master Boot Record,MBR 主引导记录: 拥有BIOS控制权的存储设备的第一个扇区 最前面的512个字节。 计算机是如何启动的
主引导记录结构:
- 第1-446字节:调用操作系统的机器码;
- 第447-510字节:分区表(Partition table); 【作用:将硬盘分成若干个区】
- 第511-512字节:主引导记录签名(0x55和0xAA)
计算机启动过程:
- 通电
- 读取ROM里面的BIOS,用来检查硬件
- 硬件检查通过
- BIOS根据指定的顺序,检查引导设备的第一个扇区(即主引导记录),加载在内存地址 0x7C00
- 主引导记录把操作权交给操作系统
详解计算机启动过程:《X86汇编语言—从实模式到保护模式》
1.2 自己给自己挪个地儿
|
|
经过上面的几行指令后,
|
|
此时CPU寄存器的总图如下:
将内存地址 0x7c00 处开始往后的 512 字节的数据,原封不动复制到 0x90000 处。 如下图:
|
|
段基址 : 偏移地址 ** 这种格式的内存地址的计算: 段基址要先左移四位,所以是跳转到0x90000 + go**这个内存地址处执行。
go是一个标签,最终编译成机器码的时候被翻译成一个值,这个值 就是go这个标签在文件内的偏移地址。
到此为止,前两回的内容,其实就是一段 512 字节的代码和数据,从硬盘的启动区先是被移动到了内存 0x7c00 处,然后又立刻被移动到 0x90000 处,并且跳转到此处往后再稍稍偏移 go 这个标签所代表的偏移地址处,也就是 mov ax,cs 这行指令的位置。
1.3 做好最最基础的准备工作
接着上一节内容:
|
|
回顾 CPU中的关键寄存器:
cs寄存器 表示 代码段寄存器
,CPU当前正在执行的代码在内存中的位置,由cs : ip 这组寄存器配合指向的,cs是基址,ip是偏移地址。
sp寄存器被赋值为 0xFF00,所以目前的栈顶地址是 ss:sp所指向的地址0x9FF00
处。
到这里,操作系统的一些最最最基础的准备工作就做好了。具体如下:
- 代码从硬盘移到内存,又从内存挪了个地方,放在了 0x90000处;
- 数据段寄存器ds 和 代码段寄存器cs 此时都被设置为 0x9000,也为跳转代码和访问内存代码 奠定了同一个内存的基址地址,方便了跳转和内存访问,因为仅仅需要指定偏移地址 即可;
- 栈顶地址被设置为 0x9FF00,具体表现为栈段寄存器ss为0x9000,栈基址寄存器sp为0xFF00。栈是向下发展的,这个栈顶地址0x9FF00要远远大于此时代码所在的位置0x90000,所以栈向下发展就很难撞见代码所在的位置,也就比较安全。这也是为什么给栈顶地址设置为这个值的原因,其实只需要离代码的位置远远的即可。
这一部分其实就是把 代码段寄存器cs,数据段寄存器ds,栈段寄存器ss和栈基址寄存器sp分别设置好了值,方便后续使用。
再拔高一下,其实操作系统在做的事情,就是给如何访问代码,如何访问数据,如何访问栈进行了一下 内存的初步规划。其中访问代码和访问数据的规划方式就是 设置了一个基址而已,访问栈就是把栈顶指针指向了一个远离代码位置的地方而已。
三种段寄存器的作用:(来自Intel手册)
1.4 把自己在硬盘里的其他部分也放到内存来
|
|
int 0x13
表示 发起0x13号中断,
【TODO】1.5 进入保护模式前的最后一次折腾内存
1.6 先解决段寄存器的历史包袱问题
1.7 六行代码就进入了保护模式
1.8 烦死了又要重新设置一边idt和gdt
1.9 Intel内存管理两板斧:分段与分页
1.10 进入main函数前的最后一跃!
总结与回顾
第二部分:大战前期的初始化工作
2.11 整个操作系统就20几行代码
2.12 管理内存前先划分处三个边界值
2.13 主内存初始化 mem_init
2.14 中断初始化 trap_init
2.15 块设备请求项初始化 blk_dev_init
2.16 控制台初始化 tty_init
2.17 时间初始化 time_init
2.18 进程调度初始化 sched_init
2.19 缓冲区初始化 buffer_init
2.20 硬盘初始化 hd_init
总结与回顾
第三部分:一个新进程的诞生
3.21 新进程诞生全局概述
3.22 从内核态切换到用户态
3.23 如果让你来设计进程调度
第四部分 shell程序的到来
第五部分 从一个命令的执行看操作系统各模块的运作
第六部分 操作系统哲学与思想
文章作者 fzhiy
上次更新 2022-02-22