这次介绍的是四大基本OS概念:Thread, Address Space, Process, Dual Mode Operation。这次的Note内容是对上一次Note的一些补充和扩展,比如对于Thread等概念进行了阐述。
Thread
在原课件中的定义是:
Thread: Single unique execution context: Program Counter, Registers, Execution Flags, Stack, Memory State
这里就是在描述,线程是程序执行时的上下文,它完整描述着程序的状态:
- Program Counter( PC )
- Registers
- Execution Flags
- Stack
知道上面这四样东西,就知道程序本身就正在处于什么状态。这也是为什么
thread的核心不是代码本身,而是当前的执行状况。 当一个thread正在某个core上执行的时候,它的状态是resident in processor registers,也就是寄存器保存了线程的根状态( 上下文 ): - PC在寄存器里
- 寄存器内容就是它的执行状态
- stack pointer也在寄存器里
- CPU正在按照这个线程的上下文执行
下面这张图展现了程序执行时真正的流程

Core单核跑多线程原理
原理很简单,因为OS在做`Time Multiplexing Time_Multiplexing


如果我们以足够细的颗粒度来执行每个thread,就会看起来像是有很多虚拟CPU跑线程。在这个过程中也传递了PC,SP还有registers:
- OS 保存 vCPU1 的 PC、SP、registers 到它的 TCB
- OS 从 vCPU2 的 TCB 恢复 PC、SP、registers
- CPU 跳转到 vCPU2 的 PC 开始执行 OS通过时间复用( Time Multiplexing )让多个线程轮流占用物理core,于是每个线程都看起来像有一个自己的core。
需要提醒的是: thread本身就是virtual CPU
TCB
当thread不执行的时候,它的上下文就不会驻留在CPU里,而是保存到内存中,通常会保存在一个专门的数据结构里面:
- TCB = Thread Control Block 这也让线程可能处于两种位置:
- 在物理核心上执行
- 在内存中的TCB里被保存 这也是线程是虚拟CPU的根本原因
Address Space
在原课件中的定义是:
Address space = the set of accessible addresses + associated state: 32 位: 地址
64 位:地址
这句话表明的是地址空间是程序能够访问的地址集合,以及与这些地址相关的状态。注意不是简单的内存区域,与地址相关的状态涉及到了:
- 哪些地址程序能读,哪些地址程序能写
- 对这些地址访问有什么效果 典型的地址空间布局如下图所示


地址空间这个概念存在的必要性,正是体现在限制程序可见和可访问的内存范围这一方面,确保每个程序只看到属于自己的那部分地址。
我们先前提及到的 Time_Multiplexing,每个vCPU都有权限访问非CPU资源,每个线程都可以读写内存,这就导致了线程很有可能篡改OS的内容?也有可能篡改别的线程的内容?我们就思考硬件该怎么帮助OS免受进程的非法篡改?这就引出了B&B,是一种限制线程访问内存权限的方法

Base And Bound
这是一种很基础的机制,我们有两个register,分别是base register和bound register。程序发出的地址不会直接拿去访问物理内存,而是会先检查:
- 是否在合法范围内
- 或者和base做转换后是否落在允许的区域内( 段基址+偏移地址 )
Virtural Address Space
这是一种更一般的思想:
程序运行在一个和物理内存不同的地址空间里: 程序看到的是virtual address,真正访问的是physical address,中间要经过translation
这带来了两个巨大好处:
- 程序看不到不该看的内存
- 程序不再需要连续放在物理内存里
Paged Virtual Address Space
这是一种分页思想,把整个虚拟地址空间分成固定大小的块,同时把整个物理内存也切成同样大小的块,然后由硬件通过page table完成映射。
需要注意的是:
- 每个 virtual page 可以映射到任意 physical frame
- 页面大小固定,管理更方便
- 如果某页不在内存里,访问会触发 page fault
其中虚拟地址可以看成
<Page #> + <Page Offset>,其中<Page #>用来查page table,<Page Offset>直接保留,最后组合出了真实的physical address 这是进程隔离最重要的底层机制之一
Process
原课件的定义:
execution environment with Restricted Rights > - (Protected) Address Space with One or More Threads > - Owns memory (address space) > - Owns file descriptors, file system context, … > - Encapsulate one or more threads sharing process resources
为什么需要Process这个定义?
- Process彼此隔离
- OS也需要被保护
- 进程提供内存保护边界
还需要注意的一点是进程内通信容易,进程间通信更难,原因也很简单:同一进程的多个线程共享很多资源;不同进程彼此隔离不共享
Address Space,要通信就必须通过OS提供的机制
Single and Multithreaded Processes
为什么一个进程要有多个线程?
- Parallelism: 利用多核硬件并行执行
- Concurrrency: 更容易处理 I / O和多个同时发生的事件
PCB
每个Process有一个PCB,这可以看成thread在内核中的官方档案,TCB则更偏向于线程执行状态档案。TCB may be inside PCB PCB包含了:
- 状态( running, ready, blocked )
- registers状态
- Process ID( PID ), User, Executbable, Priority
- 执行时间
- 内存空间和地址翻译信息
Protection And Isolation
我们再次回顾一下刚才放的一张图,这就是我们采取保护和隔离的目的
但是仅有地址空间隔离还不够,即使有page table/ address translation,为什么进程不能自己改page table ptr?为什么不能直接发I/O指令绕过OS?我们就必须再加上权限控制,这就引入了:Dual Mode Operation
Dual Mode Operation
Dual Mode Operation的含义是硬件至少提供两种模式: Kernel Mode和User Mode。User Mode权限受限,不能做敏感操作;Kernel Mode拥有完整硬件访问能力。
课件中列出的User mode不能做的东西包括:
- changing the page table pointer
- disabling interrupts
- interacting directly with hardware
- writing to kernel memory
我们可以通过Unix System Structure的结构来理解
Dual Mode Operation,下图所示的便是Unix。

User → Kernel 的三种方式
Syscall
进程主动请求系统服务,比如:
- exit
- open
- read
- write
Interrupt
外部的异步事件触发,比如:
- timer interrupt
- I/O device interrupt
Trap/Exception
进程内部的同步异常触发,比如:
- divide by 0
- protection violation( segment fault ) 这是一种因为硬件/异常机制强制切换控制流
OS加载进程的真实过程
第一步: OS在kernel mode下准备进程
OS会在这一步设置一些基本的Base, Bound, user PC,other oregisters
需要注意的是B&B只有在sysmode = 1的时候才可以填写
第二步: RTU( Return to User )
OS执行返回User mode,开始从用户PC执行用户程序
第三步: 执行用户代码
此时程序运行在受保护的地址空间里,内核暂时放手
第四步: 内核重新拿回控制权
通过timer interrupt还有I/O request / interrupt还有exception / trap来重新拿回控制权,这在User → Kernel是提及到过的
第五步: 发生中断后
硬件会:
- 保存用户 PC
- 切换到 kernel mode
- 跳到 interrupt vector 对应的 handler
第六步: OS可能进行调度
如果决定换一个线程/进程来运行:
- 就会选择保存当前
thread到TCB - 恢复下一个
thread的TCB - 再
RTU