通过 XV6 来学习操作系统(OSTEP)
该笔记以OSTEP 为主体
操作系统有以下四个主要的主题(来自 OSTEP),我将从四个主题慢慢钻研
- CPU 虚拟化
- 内存虚拟化
- 并发
- 持久性
CPU 虚拟化
什么是进程?
非正式(我认为)的说,Process
是指正在 CPU 中进行执行操作的程序,其中又包括着该程序需要的资源,即:内存、堆栈、读写的数据等。
<p>为什么需要进程?</p>
人们常常希望计算机可以同时运行多个程序。比如:在使用计算机或者笔记本的时候,我们会同时运行浏览器、邮件、游戏、音乐播放器等等。实际上,一个正常的系统可能会有上百个进程同时在运行。如果能实现这样的系统,人们就不需要考虑这个时候哪一个 CPU 是可用的,使用起来非常简单。因此我们需要解决的问题是:怎么实现同时运行?
进程的一般函数
[[fork() 函数]]
操作系统的机制
机制是指在操作系统中的一些低级方法或协议来实现所需的功能。例如:context switch (上下文切换),这是一种分时机制(time sharing)。
还有一种是高级智能称作策略,是对操作系统内做出某种决定的算法。
进程加载
在早期的(或简单的)操作系统中,加载过程尽早(eagerly)完成,即在运行程序之前全部完成。现代操作系统惰性(lazily)执行该过程,即仅在程序执行期间需要加载的代码或数据片段,才会加载。
进程状态
- 运行(running):在运行状态下,进程正在处理器上运行。这意味着它正在执行指令。
- 就绪(ready):在就绪状态下,进程已准备好运行,但由于某种原因,操作系统选择不在此时运行。
- 阻塞(blocked):在阻塞状态下,一个进程执行了某种操作,直到发生其他事件时才会准备运行。一个常见的例子是,当进程向磁盘发起 I/O 请求时,它会被阻塞,因此其他进程可以使用处理器。
进程 API
在现代的操作系统上,都为开发者或使用者提供了以下五类的 API
- 创建(create):操作系统必须包含一些创建新进程的方法。在 shell 中键入命令或双击应用程序图标时,会调用操作系统来创建新进程,运行指定的程序。
- 销毁(destroy):由于存在创建进程的接口,因此系统还提供了一个强制销毁进程的接口。当然,很多进程会在运行完成后自行退出。但是,如果它们不退出,用户可能希望终止它们,因此停止失控进程的接口非常有用。
- 等待(wait):有时等待进程停止运行是有用的,因此经常提供某种等待接口。
- 其他控制(miscellaneous control):除了杀死或等待进程外,有时还可能有其他控制。例如,大多数操作系统提供某种方法来暂停进程(停止运行一段时间),然后恢复(继续运行)。
- 状态(statu):通常也有一些接口可以获得有关进程的状态信息,例如运行了多长时间,或者处于什么状态。
操作系统隔离性(isolation)
- 进程本身不是 CPU,但是它们对应了 CPU,它们使得你可以在 CPU 上运行计算任务。所以你懂的,应用程序不能直接与 CPU 交互,只能与进程交互。
<p>CPU 一个核运行一个进程并运行一段时间,后换另一个进程再运行一段时间</p>
exec 抽象了内存,使得应用程序可以在有限且“封闭”的空间运行。
Files 基本上来说抽象了磁盘,利用 File 数据结构来对 dick 中的块进行的读写操作
Q.在权限切换的时候,如果设置那个 bit 位的指令必须是特殊权限指令,但是因为应用程序不应该能够设置那个 bit 到 kernel mode(即‘0’),应用程序不可以运行各种特殊权限指令了。所以那个 bit 是被保护的。 我的问题:那么,当应用程序正在使用 user mode 的时候这时会将控制权限从 user mode 切换到 kernel mode,是谁在将 bit 切换到 0(即 kernel mode)? A.我认为:在这个过程中,控制权从用户模式切换到内核模式,而这是由 CPU 内部的特权级 (machine mode??? BIOS???)机制来控制的。
<p>举个例子,不论是 Shell 还是其他的应用程序,当它在用户空间执行 fork 时,它并不是直接调用操作系统中对应的函数,而是调用 ECALL 指令,并将 fork 对应的数字作为参数传给 ECALL。之后再通过 ECALL 跳转到内核。</p>
user.frok() --->> ECALL syscall() --->> kernel.frok()
Q.在 Linux 中的 root 有全部的特权吗? A.不会,只有特定的特权, 比一般的 user 多。
User/Kernel mode 切换
我们可以认为 user/kernel mode 是分隔用户空间和内核空间的边界,用户空间运行的程序运行在 user mode,内核空间的程序运行在 kernel mode。操作系统位于内核空间
调度算法
<p>鱼与熊掌不可兼得</p>
当我们在工作时间段的时候,我通常要进行资源和时间等的合理分配这是一个在现实生活中常见的管理模式,对于操作系统来说,如何管理 CPU 的资源分配是主要目的。
我们了解了 context 的作用,即再:将要切换正在运行 CPU 上的进程时,必须先从寄存器存储目前进程的状态(内存、寄存器、指令、PC 等等)存出来内存中,再将欲执行的进程之状态读回 CPU,把进程的使用空间存来寄存器中。
FIFO
先进先出的模式,对这个模式有个前提“进程进来的时间相同和运行时间相同”,
SJF
STCF
轮转 RR
多级反馈队列 MLFQ
[[并发和并行]]
内存虚拟化
<p>它们是“隐形”的</p>
地址空间
内存操作 API
<p>一些在 c 语言中常见的 API</p>
malloc(size_t)
free(void*)
calloc()
realloc()
忘记释放内存 这是一个很糟糕的情况
使用自己的内存
分段
<p>怎样支持大地址空间?</p>
线程虚拟化
并发是指多个任务在同一时间段内交替执行,这些任务之间可能会有时间上的重叠,但并不一定是同时执行的。 在并发中,任务之间可能会通过时间片轮转或者事件驱动的方式来实现交替执行。
并行是指多个任务在同一时刻同时执行,通常需要多个处理单元(比如多核处理器或者多个计算节点)来实现。 在并行中,多个任务可以同时进行,互相之间不会有时间上的重叠。
Threads
一个工人做所有工作和两个(或多个)工人做所有工作的区别。安排时间表,轮班工作
Critical section
11dd: 8b 05 31 2e 00 00 mov 0x2e31(%rip),%eax
11e3: 83 c0 01 add $0x1,%eax
11e6: 89 05 28 2e 00 00 mov %eax,0x2e28(%rip)