From 79d102159fa0442facd6ee7517436ab3c20957f8 Mon Sep 17 00:00:00 2001 From: Shine wOng <1551885@tongji.edu.cn> Date: Thu, 9 May 2019 20:58:01 +0800 Subject: [PATCH] create chp1.md of thu_os/ --- thu_os/chp1.md | 113 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 113 insertions(+) create mode 100644 thu_os/chp1.md diff --git a/thu_os/chp1.md b/thu_os/chp1.md new file mode 100644 index 0000000..660a90c --- /dev/null +++ b/thu_os/chp1.md @@ -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),以便于从内核堆栈恢复到用户堆栈