系列文章目录
操作系统入门系列-MIT6.S081(操作系统)学习笔记(一)---- 操作系统介绍与接口示例
操作系统入门系列-MIT6.828(操作系统工程)学习笔记(二)----课程实验环境搭建(wsl2+ubuntu+quem+xv6)
操作系统入门系列-MIT6.828(操作系统工程)学习笔记(三)---- xv6初探与实验一(Lab: Xv6 and Unix utilities)
操作系统入门系列-MIT6.828(操作系统工程)学习笔记(四)---- C语言与计算机架构(Programming xv6 in C)
操作系统入门系列-MIT6.828(操作系统工程)学习笔记(五)---- 操作系统的组织结构(OS design)
文章目录
- 系列文章目录
- 前言
- 一、操作系统的目的
- 1.如果没有操作系统?
- 2.操作系统的目的
- 3.操作系统的隔离性
- 4.操作系统的防御性
- 5.操作系统的硬件资源抽象
- 6.操作系统的交互性
- 二、宏内核与微内核
- 1.宏内核(monolithic kernel)
- 2.微内核(microkernel)
- 3.宏内核与微内核的应用
- 总结
前言
本节对应的是MIT 6.828课程第三节:OS design
有大佬讲视频课程的内容进行了中文记录,链接如下:MIT6.828 简介
按照课程官方的进度安排:课程进度计划表
一、课前预习:
1.读xv6实验指导手册第二章
2.精读xv6源码: kernel/proc.h, kernel/defs.h, kernel/entry.S, kernel/main.c, user/initcode.S, user/init.c
3.略读xv6源码: kernel/proc.c and kernel/exec.c
二、课后任务:
1.完成Lab: system calls
本文主要讲解课程的视频内容以及xv6实验指导手册的第二章,理论内容。下一篇文章将讲解xv6启动与源码阅读。课程视频与实验指导手册的内容有诸多相似点,笔者按照自己的思路进行整理总结。该篇文章是在操作系统入门系列-MIT6.S081(操作系统)学习笔记(一)---- 操作系统介绍与接口示例基础上对操作系统的宏观层面(high level)进一步深入探讨。
一、操作系统的目的
1.如果没有操作系统?
要理解操作系统的目的,我们可以先假设一下:如果没有操作,会是什么样的情境。
如果有单片机(裸机)开发经验的读者,很容易能想到该情景:众多应用程序,按照事先约定好的规则,分时去直接使用硬件资源(分时复用),各种硬件资源的访问会被抽象会库函数被应用程序直接调用。
根据这个情景,我们再具象化一点:现在只有一个硬件开发板,有众多不相识的人想使用它。那么很可能会有如下隐患:
1.有个人很强壮,他仗着无人可敌故意一直占用硬件开发板,不给别人使用
2.在使用硬件开发板的时候,可能有人的项目没做完,想下次续上继续做,就会存一些数据在硬件开发板里面。那么有人在使用硬件开发板的时候,无意或者有意的会访问甚至破坏别人保存的私人数据。
3.有人不小心把硬件开发板弄坏了
那么我们基于隐患再回到最之前的情景,很容易得到:
1.有恶意程序,故意写死循环,一直占用硬件资源。
2.各个程序直接访问内存,可能导致各个应用程序之间的内存重叠,甚至有恶意程序篡改其他程序的私有内存。
3.有程序存在BUG,使得CPU直接宕机
那么,该如何解决“众人使用硬件开发板”的隐患或者风险呢?很显然,可以选一个有公信力和权力的机构或者个人,来管理这个仅有的硬件开发板:(1)安排协调时间分配和资源的分配(2)防止恶意个体破坏共有资源(3)保证每个人有各自独立的私人空间来记录项目进度,实现个体间的隔离(4)在隔离的基础上实现有监管的个体之间信息交流,以提供个体之间合作的需求
这不就是政府机构的作用吗?机构和政策就是一个“国家操作系统”!国家的资源与人民之间存在一个人民政府,作为操作系统使得国家得以有效率的稳定的运转。
2.操作系统的目的
基于上述假设和推理,操作系统的目的就显而易见了:
1.隔离性(课程视频+xv6指导书 提到)
2.硬件复用(xv6指导书 提到)
3.防御性(课程视频 提到)
4.交互性(xv6指导书 提到)
可以看到,四个目的中,隔离性是很重的一个目的,视频和实验指导书中都反复提及。笔者认为的原因是:(1)硬件复用的基础依赖于CPU芯片内部机制、各种总线等硬件级实现,从操作系统设计的角度来说便是将提供的硬件功能抽象成函数;(2)防御性在一般运行情况下的占比很小,同时隔离也有助于防御性的实现(应用A蓄意破坏应用B的内存空间)(3)交互性是两个应用程序之间传递信息,但是必须基于隔离性实现的基础上。
所以隔离性在操作系统设计中显得尤为重要。实现的机制有:
1.虚拟内存与页表(pagetable),第四节课会着重介绍
2.user模式与kernel模式,不同权限
3.“进程”概念的提出与设计
4.……
3.操作系统的隔离性
(1)虚拟内存与页表(pagetable)
该内容的具体细节很复杂,课程的第四讲会通过一整讲来探讨改内容
该设计有两个点:
1.通过page table以及一些硬件支持实现虚拟内存到物理内存的映射
2.每个进程被分配有自己独有的虚拟内存,进程有“独立内存视图”
每一个进程都会有自己独立的page table,这样的话,每一个进程只能访问出现在自己page table中的物理内存。操作系统会设置page table,使得每一个进程都有不重合的物理内存,这样一个进程就不能访问其他进程的物理内存,因为其他进程的物理内存都不在它的page table中。一个进程甚至都不能随意编造一个内存地址,然后通过这个内存地址来访问其他进程的物理内存。这样就给了内存的强隔离性。
每一个用户进程都有自己对于内存的独立视图:(ls程序与echo程序的虚拟内存视图)
(2)user/kernel模式
简单来讲:
1.user模式下,程序不可以执行权限指令,只能执行普通指令
2.kernel模式下,程序可以执行权限指令,也可以执行普通指令
user/kernel mode是分隔用户空间和内核空间的边界,用户空间运行的程序运行在user mode,内核空间的程序运行在kernel mode。操作系统位于内核空间。如下图:
例如当ls程序运行的时候,会调用read/write系统调用;Shell程序会调用fork或者exec系统调用,所以必须要有一种方式可以使得用户的应用程序能够将控制权以一种协同工作的方式转移到内核,这样内核才能提供相应的服务。
在RISC-V中,有一个专门的权限指令用来实现这个功能,叫做ECALL。ECALL接收一个数字参数,当一个用户程序想要将程序执行的控制权转移到内核,它只需要执行ECALL指令,并传入一个数字。这里的数字参数代表了应用程序想要调用的System Call。
(3)进程概念的抽象
个人认为进程概念的抽象也是隔离性实现的重要手段。可能有读者认为,进程和程序不就是一个东西吗?个人认为,程序是一段可以执行的代码,而进程是操作系统对CPU、内存等硬件资源的抽象。程序代码,想要使用CPU资源,得先向操作系统申请(fork函数)一个“进程”,也就是CPU上的一个坑位。操作系统在初始化阶段,就会合理的讲硬件资源拆分成多个进程坑位,之后程序通过申请来使用这些坑位。
而通过“坑位”这个抽象,不同程序仅能使用自己独有的进程,那么只要实现进程之间的隔离,就可以实现程序之间的隔离。
4.操作系统的防御性
(1)操作系统的设计者需要保持“程序都是恶意”的心态
1.用户程序可能主动地破坏隔离性
2.用户程序可能主动地使用一些诡计去使系统调用函数干坏事
3.用户程序可能主动地干扰其他程序
(2)我们需要保证操作系统的代码完全正确,值得信赖
一个没有BUG的程序是不现实的,Linux过一段时间就会修复一些BUG。但是我们仍然得以保证操作系统的代码完全正确,值得信赖为目标,才能实现操作系统的良好的防御性。
5.操作系统的硬件资源抽象
操作系统对硬件资源的抽象和复用,最终表现为系统调用函数。
这里举fork和exec两个例子,来大致展现系统调用函数的巧妙设计:
(1)操作系统通过fork()函数,透明地在进程之间切换硬件cpu,根据需要保存和恢复寄存器状态,因此应用程序不必意识到时间共享。这种透明性允许操作系统共享cpu,即使某些应用程序处于无限循环中。
(2)操作系统使用exec来构建它们的内存映像,而不是直接与物理内存交互。这允许操作系统决定在内存中放置进程的位置;如果内存紧张,操作系统甚至可能将进程的一些数据存储在磁盘上。Exec还为用户提供了方便的文件系统来存储可执行程序映像。
6.操作系统的交互性
Unix进程之间的许多交互形式都是通过文件描述符实现的。文件描述符不仅抽象了许多细节(例如,管道或文件中的数据存储在哪里),而且还以简化交互的方式定义了它们。
二、宏内核与微内核
搞清楚操作系统设计的目的之后,对于宏观层面(high level)操作系统的设计方法理解起来将会很简单。
宏内核和微内核是操作系统设计的两个思路:操作系统的哪个部分应该在kernel模式下运行。(1)一种是整个操作系统驻留在内核中,因此所有系统调用的实现都以管理器模式运行。这种组织称为宏内核(monolithic kernel)。(2)另一种是,操作系统设计者最小化在kernel模式下运行的操作系统代码的数量,并在用户模式下执行大部分操作系统。这种内核组织称为微内核(microkernel)。
1.宏内核(monolithic kernel)
(1)优点
a.整个操作系统以完全硬件特权运行。这种组织很方便,因为操作系统设计者不必决定操作系统的哪一部分不需要完全的硬件特权。
b.操作系统的不同部分更容易协作。例如,一个操作系统可能有一个缓冲cache,它可以被文件系统和虚拟内存系统共享。
(2)缺点
a.操作系统的不同部分之间的接口通常是复杂的
b.操作系统开发人员很容易犯错误。在宏内核中,错误是致命的,因为在管理器模式下的错误通常会导致内核崩溃。如果内核崩溃,计算机将停止工作,因此所有应用程序也将崩溃。计算机必须重新启动才能重新启动。
2.微内核(microkernel)
在图中,文件系统作为用户级进程运行。作为进程运行的操作系统服务称为服务器。为了允许应用程序与文件服务器交互,内核提供了一个进程间通信机制,将消息从一个用户模式进程发送到另一个用户模式进程。例如,如果像shell这样的应用程序想要读取或写入文件,它会向文件服务器发送消息并等待响应。
在微内核中,内核接口由几个低级函数组成,用于启动应用程序、发送消息、访问设备硬件等。这种组织允许内核相对简单,因为大多数操作系统驻留在用户级服务器中。
微内核的挑战在于性能更差,这里有两个方面需要考虑:
(1)在user/kernel mode反复跳转带来的性能损耗。
(2)在一个类似宏内核的紧耦合系统,各个组成部分,例如文件系统和虚拟内存系统,可以很容易的共享page cache。而在微内核中,每个部分之间都很好的隔离开了,这种共享更难实现。进而导致更难在微内核中得到更高的性能。
3.宏内核与微内核的应用
在实际中,两种内核设计都会出现.
(1)出于历史原因大部分的桌面操作系统是宏内核,如果你运行需要大量内核计算的应用程序,例如在数据中心服务器上的操作系统,通常也是使用的宏内核,主要的原因是Linux提供了很好的性能。
(2)但是很多嵌入式系统,例如Minix,Cell,这些都是微内核设计。这两种设计都很流行,如果你从头开始写一个操作系统,你可能会从一个微内核设计开始。
但是一旦你有了类似于Linux这样的宏内核设计,将它重写到一个微内核设计将会是巨大的工作。并且这样重构的动机也不足,因为人们总是想把时间花在实现新功能上,而不是重构他们的内核。
实验采用的xv6是宏内核设计
总结
本文并没有将课程与指导书的每一个知识点都覆盖,总结一个大致的思路与框架,更多的细节可以看课程的中文记录与实验指导书(前言给链接)