切换进程/线程前后发生了什么?


该问题是进程与线程的区别中的一个核心问题。

进程有什么

一个进程需要存放若干数据,以及若干指令,那么首先其会有一片内存,在分页机制下,所有进程都必须有其独立的页表(PT, Page Table),从而完成虚拟地址到物理地址(用户地址空间)的转换。

有页表,就肯定需要一个访问机制。目前操作系统一般都采用二级页表,也就是访页时最先进入页目录(PD, Page Directory),再根据里面的页目录项获取页表物理地址。需要一个寄存器来存储该目录的物理地址,即页目录表寄存器(PDBR, Page Directory Base Register)(x86_64 下使用 CR3)通过向该寄存器赋予页目录基地址(物理),便可随时访问页目录。

以及页表缓存(TLB, Translation Lookaside Buffer),用于加快虚拟地址->物理地址的转换。

此外,进程在执行程序时,必须要有一个指令指针寄存器(IP, Instruction Pointer),来告诉 CPU 即将执行的指令在代码段中的偏移量,和一个代码段寄存器(CS, Code Segment),用来存放内存代码段区域的入口地址。这两个寄存器合在一起为我们指示了 CPU 当前要读取指令的地址(虚拟),最后根据页表对应到实际的物理地址进行取指令操作。

物理地址 = CS << 4 + IP

程序的运行必然是由若干函数组合起来运行的。当一个函数在运行时,需要为它在栈中创建一个栈帧用来记录运行时产生的相关信息,因此每个函数在执行前都会创建一个栈帧,在它返回时会销毁该栈帧。栈由高地址向低地址拓展,为了访问栈,就必须要有栈基址(BP, Base Pointer),以及栈顶指针(SP, Stack Pointer)

线程有什么

线程与进程的关系,就好比函数与程序的关系。同一进程下的所有线程都共享同一地址空间,包括全局变量与代码段。但每个线程有各自的空间与 CS:IP,就好比不同函数对应的参数与返回地址都不同,所执行的指令所处位置也不一定相同。

Linux 中进程与线程都被抽象为 task_struct,各自拥有 pid,但其内存所指 mm_struct 是相同的。

进程与线程切换开销

进程的切换,必然伴随着上文提到的种种寄存器的保存与加载。除了这些,最大的开销来源于:从一个进程切换到另一个进程时,对应的地址空间也将切换,那么 TLB 是需要被清空的,这样就导致 TLB miss 以及访页速率降低,当然,Cache miss 率也会提高。

然而,同一进程内的线程切换并不涉及地址空间的切换,仅仅保存其 BP, SP 以及若干寄存器即可。

如果是不同进程之间的线程切换,那依然会涉及进程切换。

这也就是为什么,进程切换的开销比线程切换大得多。


 上一篇
进程间通信(IPC) 进程间通信(IPC)
为了保护操作系统中进程互不干扰,需要使用进程隔离技术,以防不同进程能够修改其他进程数据。但进程之间又不能完全隔离,需要一定的通信手段,于是开发出了进程间通信(IPC, InterProcess Communication)技术。
下一篇 
点击 Url 到显示网页之间发生了什么? 点击 Url 到显示网页之间发生了什么?
经典网络问题,贯穿整个计算机网络学习始终,每个阶段拿出来回味都有不同的感受与收获。
  目录