create chp1.md of thu_os/

This commit is contained in:
Shine wOng
2019-05-09 20:58:01 +08:00
parent 7c42e9275f
commit 79d102159f

113
thu_os/chp1.md Normal file
View File

@@ -0,0 +1,113 @@
Conclusion on Operating System Chpater One
==========================================
## 系统启动流程
### 简单的版本
BIOS -> 加载程序(Bootloader) -> 操作系统
+ BIOS
- 芯片上电后为实模式只能索引1M的内存空间。BIOS就在其中作为ROM的一部分因此可以成为固件不会关机就失去数据了。
- 芯片刚上电时各个寄存器都会有一定的初始值其中CS:IP就指向BIOS中第一条指令。
- BIOS中的第一条指令被读入CPU。
+ Bootloader
- BIOS程序运行中会将硬盘上的Bootloader读入内存中然后控制权转向Bootloader。
- Bootloader加载操作系统到内存中。
- 控制权转给操作系统,启动完成。
简单的过程就是上面那样,但是这里需要明确一些问题。
首先为什么需要Bootloader这样一个程序为什么不能直接由BIOS读入操作系统
+ 这是因为操作系统的种类复杂有许多不同的操作系统他们各自具有不同的文件系统。BIOS的内容是出厂固化的不可能使之可以分辨所有的操作系统。让BIOS的那点程序分辨不同操作系统的文件系统并将其加载到内存中会使BIOS的设计非常复杂以至于BIOS自己就成为了一个操作系统。
+ 而Bootloader并不属于任何的操作系统不属于任何的磁盘扇区而是存在于一个叫引导扇区的地方其中一个扇区大小512字节因此Bootloader也是512字节大小。它可以具有独立于操作系统的编码方式因此可以被BIOS识别。故可以先将Bootloader读入内存再又Bootloader加载操作系统。
此外我们看到BIOS在加载Bootloader的过程中已经有用到一些操作系统的功能他们包括
+ 字符显示
+ 磁盘扇区读写
+ 检测内存大小
+ 键盘输入
实际上这些操作肯定不是操作系统提供的而是BIOS以中断调用的方式提供的。可以说BIOS的功能已经类似一个小型的操作系统了。需要注意的是这些功能只能在实模式下使用进入保护模式后就不能访问BIOS的这些功能了。这里我们也可以发现BIOS的另一个重要功能就是切换到保护模式。
### 具体一点的版本
BIOS -> 磁盘主引导扇区的主引导记录 -> 活动分区的引导扇区的引导代码 -> 加载程序Bootloader -> 操作系统
相对于之前的简单版本,多了两个中间流程:读取主引导扇区的引导代码,读取活动分区的引导扇区代码。这两个过程实际上是非常自然的:
+ 现代计算机一般都具有多个分区,因此就可能从不同的硬盘分区读取操作系统代码。此外,还可以选择从即插即用设备启动(U盘)从网络启动。这使得上面BIOS直接读取Bootloader变得行不通了因为可能还需要由用户选择启动哪个操作系统不同的操作系统也需要不同的Bootloader进行加载因为具有不同的文件系统
+ 为什么还需要分区引导扇区而不是直接由主引导扇区跳转到不同分区的加载程序呢我的想法是一个分区也可以存在几个操作系统因此还需要分区引导扇区来加以选择之后才能跳转到Bootloader。
从上面的分析可以看出BIOS还需要具有一些其他的功能像是可以识别到不同分区的操作系统识别到即插即用设备从网络启动的话还需要加载网络协议栈。这样的话系统的启动可以具体为以下的步骤
+ BIOS
- 第一条指令被读入内存
- 进行一些系统初始化工作
+ 硬件自检
+ 检测系统中各个关键部件(如内存和显卡)的存在以及工作状态
+ 查找和执行各个接口卡的BIOS完成设备的初始化
- 执行系统BIOS进行系统检测如磁盘分区和即插即用设备
- 读取主引导扇区的主引导记录
+ 主引导记录(MBR, Main Boot Record)
- 包括三个部分
+ 启动代码446字节
- 检查分区的正确性
- 加载并跳转到磁盘上的分区引导扇区
+ 硬盘分区表64字节 描述每个分区的状态和位置一个分区占16个字节
+ 结束符2字节 0x55AA
- 执行启动代码,跳转到活动分区的引导程序
+ 活动分区引导扇区
- 到这里为止,已经出现了文件结构
- 包含四个部分
+ 跳转指令: 跳转到启动代码,现在是与平台相关的代码了
+ 文件卷头: 文件系统描述信息
+ 启动代码: 跳转到加载程序
+ 结束标志: 0x55AA
+ 加载程序
- 读取文件系统中的配置信息
- 根据配置信息加载操作系统的代码
- 控制权转交给操作系统
## 中断,异常,系统调用
还是一个问题,为什么操作系统需要中断、异常和系统调用?
其实,按照软件工程的思想,这三者都是操作系统对外提供的接口,外部的应用程序可以通过这三种接口进入操作系统内核,而不能直接操作底层的寄存器这种,都是为了操作更加地安全。
这是因为,在计算机运行当中,只有内核才是被信任的第三方,只有内核才能执行特权指令。这就好比你去银行取钱,你可以通过柜台或者是自动取款机取钱,却不可以直接去保险柜里面拿钱。
接下来辨析一下中断,异常和系统调用的区别
+ 中断是用于服务硬件的,是处理来自硬件设备的处理请求,如键盘中断这种。
+ 异常是用于处理非法指令或者其他原因导致当前指令执行失败,例如除零异常,缺页异常这种
+ 系统调用是用于应用程序主动地调用操作系统的功能,是应用程序主动发出的服务请求
但是这三者显然具有更多的共同点:
+ 他们都是操作系统对外提供的接口。外部应用只有通过这三种接口才能进入操作系统内核
+ 在实现机制上,三者都是采用中断处理机制。就是说,三者尽管名字不同,处理起来确实类似的
- 在硬件方面收到了中断处理请求,根据优先级查找中断向量表,找到对应的中断服务例程
- 这里,中断、异常、系统调用都有各自的中断服务例程
+ 对于系统调用一般都是通过INT 80指令进入中断然后通过中断向量表找到系统调用表再根据具体的系统调用号找到对应的系统调用程序
+ 对于异常,会进入对应的异常服务例程。不同的异常自然会有不同的异常服务例程
- 找到对应的服务例程后,三者都是进入同样的软件方面的机制
- 现场保护
- 中断服务处理
- 清除中断标记
- 现场恢复
+ 此外,他们之间是可以相互嵌套的。例如异常服务例程执行过程中可以会出现硬件中断,异常服务例程执行时也可能会遇到缺页异常。
关于系统调用与常规调用的不同点:
- CALL RET 用于常规调用
- INT IRET 用于系统调用
- 系统调用往往开销更高,这是因为系统调用还涉及到堆栈的切换,从用户堆栈切换到内核堆栈(也是出于安全的考虑)因此系统调用相对于常规调用在保护现场的操作中除了要保存当前的CS:IP以及一些关联寄存器外还需要保存状态信息(EFLAGS),以及当前用户堆栈的地址(SS和ESP),以便于从内核堆栈恢复到用户堆栈