Files
my-notes/UCB-CS162/L02-Fundatmental-OS-Concepts.md
2026-04-23 23:11:27 +08:00

187 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Four Fundamental OS Concepts
## Complexity 复杂性
操作系统的硬件非常复杂。如果没有操作系统,用户必须直接与硬件交互,这将非常困难。操作系统通过提供抽象来隐藏硬件的复杂性,使用户能够更容易地使用计算机。
这也就意味着,如果处理不当:
- 复杂性会“泄露”出来。例如一些第三方驱动程序导致系统崩溃,或者某些功能无法正常工作。
- 安全漏洞。例如2017年Intel的CPU存在一个名为Meltdown的漏洞。攻击者甚至可以访问kernel模式的内存
- 不同版本的库会导致应用程序可能出问题。(现在我们有了Docker)
一般来说,操作系统进行的"抽象"包括:
- Processor -> Thread
- Memory -> Address Space
- Disks, SSDs, ... -> Files
- Networks -> Sockets
- Machines -> Processes
## Thread of Control
Thread 是一个独一无二的context, 其包括程序计数器、寄存器、堆栈、CPU标志位、内存等等。
### Resident / Running 驻留(运行态)
当一个线程处于Resident状态时表示该线程正在被CPU执行。
Resident指: 寄存器当前保存了该线程的state (content)。
- 包括程序计数器(PC, program counter)、当前执行的指令的地址
- PC指向内存中下一条指令的地址
- 包括程序当前正在计算的数据
- 栈指针
- 剩下的、内存里的数据
### Suspended / Ready 挂起(就绪态)
当一个线程处于Suspended状态时表示该线程已经准备好运行但由于某些原因如等待资源、等待I/O操作完成等而暂时未执行。
与Resident状态具有很多相反的状态
- CPU的state不再是该线程的content而是另一个线程的content
- PC指向另一个线程的指令地址
- 大多数情况下线程的content被保存在内存中而不是寄存器中
### Content Switch 上下文切换
#### 使用场景
现代的操作系统通常支持多线程。电脑上可以同时运行很多线程但单个CPU核心一次只能执行一个线程。当操作系统需要切换正在运行的线程时就会发生上下文切换。
以图中为例:
![CPU上下文切换](/UCB-CS162/images/L02-content-switch.png)
- T1时刻vCPU1在CPU核心上vCPU2在内存中
- T2时刻vCPU1在内存中vCPU2在CPU核心上
在T1和T2之间操作系统进行了一次**context switch**,也就是上下文切换:
- 将vCPU1的状态保存到**Thread Control Block** (TCB, 位于内存) 中。
- 将vCPU2的状态从内存加载到CPU核心上
- 其他操作.....
上下文切换不是毫无开销的。一般来讲,它会耗费几微秒的时间。因此,操作系统会尽量减少上下文切换的次数,并使用各种手段减少上下文切换的开销,以提高系统性能。
有多种条件可以出发一次Content Switch: 计时器、I/O事件、系统调用、线程优先级变化等等。
#### Thread Control Block (TCB)
TCB是一些数据保存了线程的状态信息包括寄存器值、堆栈指针、程序计数器、PC等。
通常来讲TCB被存储在内存的kernel空间中。
#### Address Space 地址空间
![简化的地址空间](/UCB-CS162/images/L02-Simplified-Address-Space.png)
## Reliability 可靠性
简单的上下文切换不提供操作系统保护而我们显然不会希望一个user program崩掉整个系统因此操作系统需要提供一些保护机制。
- 可靠性:破坏操作系统通常会导致其崩溃
- 安全性:限制线程的操作范围
- 隐私性:限制每个线程仅能访问其被允许访问的数据
- 公平性每个线程应限制在其应得的系统资源份额内CPU时间、内存、I/O等
仅靠软件是不足以更好的保护系统的,因此我们有了一些硬件层面的保护机制:
### Base and Bound (B&B) 基址寄存器、边界寄存器
![Base & Bound image](/UCB-CS162/images/L02-Base-and-Bound.png)
如图所示,程序地址“看起来”位于 0 ~ 100 之间;但,当它被加载到内存内时,它被重新定位到 1000 ~ 1100 之间。
这是一种基于编译器、加载器的保护机制。它把操作系统和用户程序隔离,保护操作系统。
我们也可以通过添加一个加法器来实现更高效的B&B机制
![B&B Hardware Assistant 1](/UCB-CS162/images/L02-B&B-Hardware-Assistant-1.png)
这是一种硬件辅助的内存重定向。
B&B机制有很多缺点。最显著的缺点之一是它无法简单地处理用户程序allocate或者free内存的情况。由此我们有了一个更复杂的机制:
### Address Space Translation 地址空间转换
我们可以通过增添一个特定的“小盒子”,来实现更高效的地址空间转换:
![Address Space Translation](/UCB-CS162/images/L02-Address-Space-Translation-Overview.png)
这个“小盒子”被称为**Memory Management Unit** (MMU),它可以将虚拟地址转换为物理地址。
这个解决方案很好地解决了B&B机制的缺点。用户程序可以allocate或者free内存而不需要担心地址空间的重叠问题。其中有一个至今仍然在使用的机制: **Paging** (分页机制)。
### Paging 分页机制
分页机制将内存划分为固定大小的页通常是4KB。每个页都有一个对应的页表项记录了该页在物理内存中的位置。当程序访问一个虚拟地址时MMU会查找对应的页表项将虚拟地址转换为物理地址。
![Paged Memory](/UCB-CS162/images/L02-Paged-Memory.png)
我们还可以实现一点更“酷”的东西。为了节约内存我们甚至可以让一些Page实际上不在内存中对这些page的访问会触发一个**page fault**程序中断后操作系统将所需的page从磁盘加载到内存中然后继续执行程序。
## Processes 进程
进程是具有受限权限的执行环境。
- (受保护的)具有一个或多个线程的地址空间
- 拥有内存(地址空间)
- 拥有文件描述符、文件系统上下文...
- 封装一个或多个共享进程资源的线程
复杂的应用程序可以 fork/exec创建/执行)子进程。
某种意义上来说,进程是一些“城市”,而操作系统是管理他们的“政府”。
- 每个城市都有自己的居民(线程),有自己的资源(内存、文件描述符等)。程序假设它们是独立的,不会相互干扰。
- 政府负责管理这些城市确保城市A出问题不会波及到城市B、审查城市AB之间的通信、分配硬件资源给城市, etc.
### 为什么需要进程?
- 它让不同程序、组件之间相互隔离,不会一个崩溃连带其他崩溃。
- 操作系统免受它们的影响
- 提供内存保护
### 保护与效率之间的基本权衡
- 进程**内**通信更容易。
- 线程之间可以直接访问共享内存
- 进程**间**通信更难
- 需要使用IPC机制如管道、消息队列、共享内存等来进行通信
### 单线程与多线程
![Single Thread vs Multi Thread](/UCB-CS162/images/L02-Single-Thread-vs-Multi-Thread.png)
可以从途中看到,各个线程**共享资源**(同一个地址空间、文件描述符等),但每个线程都有**自己的程序计数器、寄存器、堆栈**等。
## privilege levels 权限级别
上边我们提到了页表。那如果应用A想要把页的映射地址改到一个它不应该访问的地址上怎么办我们有了权限级别来解决这个问题。
### Dual Mode Operation 双模式操作
CPU至少会提供以下两个模式:
- User mode (Ring 3) 用户模式:应用程序运行在这个模式下,权限受限,无法直接访问硬件资源。
- Kernel mode (Ring 0) 内核模式:操作系统运行在这个模式下,拥有完全的权限。
由此我们解决了问题当操作系统让其他应用程序运行时它会将CPU切换到User mode。这样子应用A就不能直接修改页表了。
如果应用程序需要从硬盘读取数据它会发出一个系统调用system call请求操作系统执行这个操作。操作系统会将CPU切换到Kernel mode执行相应的操作然后再切换回User mode。
由此我们得出了经典的UNIX系统架构:
![Simplified Unix System Architecture](/UCB-CS162/images/L02-Simplified-Unix-System-Architecture.png)
### How to switch? 如何切换当前模式?
从用户态到内核态:
- ```System call``` 系统调用
- 定义: 进程请求系统服务例如exit
- 系统调用类似于函数调用
- 没有要调用的系统函数的地址
- 类似于远程过程调用 (RPC)
- 将系统调用 ID 和参数整理到寄存器中,并执行系统调用
- ```Interrupt``` 中断
- 某些原因触发上下文切换
- 例如Timer 、I/O 设备
- ```Trap``` 陷阱或```Exception``` 异常
- 进程内部同步事件触发上下文切换
- 例如:```Protection Violation - Segmentation Fault```(段错误)、除以零, etc.
在一次,例如```Interrupt```发生后CPU会把当前正在运行的程序保存在```Thread Control Block (TCB)```中。TCB中保存了程序计数器、寄存器值、堆栈指针等并存储在Kernal memory中。