NEW: finished CS162 Lecture 2 note.
Co-authored-by: Copilot <copilot@github.com>
@@ -8,4 +8,180 @@
|
||||
|
||||
- 复杂性会“泄露”出来。例如一些第三方驱动程序导致系统崩溃,或者某些功能无法正常工作。
|
||||
- 安全漏洞。例如2017年,Intel的CPU存在一个名为Meltdown的漏洞。攻击者甚至可以访问kernel模式的内存!
|
||||
- 不同版本的库会导致应用程序可能出问题。(现在我们有了Docker)
|
||||
- 不同版本的库会导致应用程序可能出问题。(现在我们有了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核心一次只能执行一个线程。当操作系统需要切换正在运行的线程时,就会发生上下文切换。
|
||||
|
||||
以图中为例:
|
||||

|
||||
|
||||
- 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 地址空间
|
||||
|
||||

|
||||
|
||||
## Reliability 可靠性
|
||||
|
||||
简单的上下文切换不提供操作系统保护,而我们显然不会希望一个user program崩掉整个系统!因此,操作系统需要提供一些保护机制。
|
||||
|
||||
- 可靠性:破坏操作系统通常会导致其崩溃
|
||||
- 安全性:限制线程的操作范围
|
||||
- 隐私性:限制每个线程仅能访问其被允许访问的数据
|
||||
- 公平性:每个线程应限制在其应得的系统资源份额内(CPU时间、内存、I/O等)
|
||||
|
||||
仅靠软件是不足以更好的保护系统的,因此我们有了一些硬件层面的保护机制:
|
||||
|
||||
### Base and Bound (B&B) 基址寄存器、边界寄存器
|
||||
|
||||

|
||||
如图所示,程序地址“看起来”位于 0 ~ 100 之间;但,当它被加载到内存内时,它被重新定位到 1000 ~ 1100 之间。
|
||||
|
||||
这是一种基于编译器、加载器的保护机制。它把操作系统和用户程序隔离,保护操作系统。
|
||||
|
||||
我们也可以通过添加一个加法器来实现更高效的B&B机制:
|
||||

|
||||
这是一种硬件辅助的内存重定向。
|
||||
|
||||
B&B机制有很多缺点。最显著的缺点之一是,它无法简单地处理用户程序allocate或者free内存的情况。由此,我们有了一个更复杂的机制:
|
||||
|
||||
### Address Space Translation 地址空间转换
|
||||
|
||||
我们可以通过增添一个特定的“小盒子”,来实现更高效的地址空间转换:
|
||||

|
||||
这个“小盒子”被称为**Memory Management Unit** (MMU),它可以将虚拟地址转换为物理地址。
|
||||
|
||||
这个解决方案很好地解决了B&B机制的缺点。用户程序可以allocate或者free内存,而不需要担心地址空间的重叠问题。其中,有一个至今仍然在使用的机制: **Paging** (分页机制)。
|
||||
|
||||
### Paging 分页机制
|
||||
|
||||
分页机制将内存划分为固定大小的页(通常是4KB)。每个页都有一个对应的页表项,记录了该页在物理内存中的位置。当程序访问一个虚拟地址时,MMU会查找对应的页表项,将虚拟地址转换为物理地址。
|
||||

|
||||
|
||||
我们还可以实现一点更“酷”的东西。为了节约内存,我们甚至可以让一些Page实际上不在内存中!对这些page的访问会触发一个**page fault**,程序中断后,操作系统将所需的page从磁盘加载到内存中,然后继续执行程序。
|
||||
|
||||
## Processes 进程
|
||||
|
||||
进程是具有受限权限的执行环境。
|
||||
|
||||
- (受保护的)具有一个或多个线程的地址空间
|
||||
- 拥有内存(地址空间)
|
||||
- 拥有文件描述符、文件系统上下文...
|
||||
- 封装一个或多个共享进程资源的线程
|
||||
|
||||
复杂的应用程序可以 fork/exec(创建/执行)子进程。
|
||||
|
||||
某种意义上来说,进程是一些“城市”,而操作系统是管理他们的“政府”。
|
||||
|
||||
- 每个城市都有自己的居民(线程),有自己的资源(内存、文件描述符等)。程序假设它们是独立的,不会相互干扰。
|
||||
- 政府负责管理这些城市,确保城市A出问题不会波及到城市B、审查城市AB之间的通信、分配硬件资源给城市, etc.
|
||||
|
||||
### 为什么需要进程?
|
||||
|
||||
- 它让不同程序、组件之间相互隔离,不会一个崩溃连带其他崩溃。
|
||||
- 操作系统免受它们的影响
|
||||
- 提供内存保护
|
||||
|
||||
### 保护与效率之间的基本权衡
|
||||
|
||||
- 进程**内**通信更容易。
|
||||
- 线程之间可以直接访问共享内存
|
||||
- 进程**间**通信更难
|
||||
- 需要使用IPC机制(如管道、消息队列、共享内存等)来进行通信
|
||||
|
||||
### 单线程与多线程
|
||||
|
||||

|
||||
可以从途中看到,各个线程**共享资源**(同一个地址空间、文件描述符等),但每个线程都有**自己的程序计数器、寄存器、堆栈**等。
|
||||
|
||||
## 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系统架构:
|
||||

|
||||
|
||||
### 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中。
|
||||
BIN
UCB-CS162/images/L02-Address-Space-Translation-Overview.png
Normal file
|
After Width: | Height: | Size: 148 KiB |
BIN
UCB-CS162/images/L02-B&B-Hardware-Assistant-1.png
Normal file
|
After Width: | Height: | Size: 377 KiB |
BIN
UCB-CS162/images/L02-Base-and-Bound.png
Normal file
|
After Width: | Height: | Size: 214 KiB |
BIN
UCB-CS162/images/L02-Paged-Memory.png
Normal file
|
After Width: | Height: | Size: 178 KiB |
BIN
UCB-CS162/images/L02-Simplified-Address-Space.png
Normal file
|
After Width: | Height: | Size: 300 KiB |
BIN
UCB-CS162/images/L02-Simplified-Unix-System-Architecture.png
Normal file
|
After Width: | Height: | Size: 496 KiB |
BIN
UCB-CS162/images/L02-Single-Thread-vs-Multi-Thread.png
Normal file
|
After Width: | Height: | Size: 134 KiB |
BIN
UCB-CS162/images/L02-content-switch.png
Normal file
|
After Width: | Height: | Size: 158 KiB |