如果是进程上下文切换,新进程的虚拟地址空间(VAS)是否已加载到CPU上下文(CPU的寄存器)中?

Har*_*rma 1 linux multithreading context-switch linux-kernel multitasking

我读过了:

进程切换是从一个进程到另一个进程的上下文切换。它涉及切换所有流程抽象和资源,以支持属于新流程的抽象和资源。最值得注意和最昂贵的是,这意味着切换存储器地址空间。这包括内存地址,映射,页表和内核资源,这是一个相对昂贵的操作。

也:

上下文是任何时间点的CPU寄存器和程序计数器的内容。

当内核(即操作系统的核心)针对CPU上的进程(包括线程)执行以下活动时,可以更详细地描述上下文切换:(1)暂停一个进程的进程并存储进程该进程在内存中某个位置的CPU状态(即上下文),(2)从内存中检索下一个进程的上下文,并将其恢复到CPU的寄存器中,(3)返回到程序计数器指示的位置(即,返回到中断该过程的代码行)以恢复该过程。

由于每个进程的VAS都是独立的,并且最大容量为4GB,如果进程进行上下文切换,进程的整个VAS是否会加载到CPU上下文中?

另外,由于每个进程都有单独的页表,因此如果进行上下文切换,该页表是否也被带入CPU上下文中?

如果否,那么为什么进程上下文切换比线程上下文切换慢(线程共享相同的VAS)?

Jon*_*art 5

由于每个进程的VAS都是独立的,并且最大容量为4GB,是否在进程上下文切换的情况下将进程的整个VAS加载到CPU上下文中?

另外,由于每个进程都有单独的页表,因此如果进行上下文切换,是否也在CPU上下文中购买了该页表?

这些问题是相关的。通过更改正在执行虚拟->线性转换的页表集,可以将一个虚拟地址空间换为另一个虚拟地址空间。这就是地址空间交换的完成方式。

让我们考虑一个非常简单的示例。

  • 假设我们有两个进程P 和P 。这两个进程都在虚拟地址0x1000处执行其程序映像。
  • 进程看不到的是一组页表,它们将虚拟地址空间映射到RAM的物理页:
    • Pagetable T A将虚拟地址0x1000映射到物理地址0x88000
    • Pagetable T B将虚拟0x1000映射到物理0x99000。
  • 假设理论上CPU有一个称为PP(pagetable指针)的寄存器

初始化进程后,在两者之间“交换虚拟地址空间”很简单。要加载P A的地址空间,只需将T A的地址放入中PP,现在该进程将“看到”内存为0x88000。同样,T B的地址为P B,因此他将“看到”存储器的0x99000。

在(同一进程的)线程之间切换时,不需要更改虚拟地址空间(因为给定进程的所有线程共享同一虚拟地址空间)。

当然,还需要交换其他内容(例如CPU寄存器),但是在此讨论中,我们仅关注虚拟内存。

在x86 CPU上,CR3寄存器是指向页表层次结构基址的指针。交换进程时,操作系统会更改此寄存器以更改地址空间。


当然,它比这更复杂。因为可能的虚拟地址空间很大(x86-32上为4 GiB,x86-64上为16 艾字节),所以页表本身将占用大量的空间(每4 KiB页一个条目)。为了减轻这种情况,在页表中添加了更多的间接级别,这就是为什么我将它们称为层次结构。在x86-64上,有4个级别。

现在,假设CPU是否必须为每个虚拟到物理的转换“遍历”这些分页结构。从虚拟内存中进行一次读取将总共需要5次内存访问!这将非常慢。

输入转换后备缓冲区或TLB。TLB会缓存这些翻译,因此给定的虚拟到物理翻译只需要将页表移动一次即可。之后,TLB会记住翻译内容,并且速度更快。(当然,TLB可以满载,但是缓存逐出是另一回事)。

假设P A正在运行,内核突然间在P B的地址空间中交换。现在,所有这些缓存的虚拟到物理转换对于新的虚拟地址空间不再有效!这意味着我们需要刷新 TLB或清除其所有条目。因此,CPU必须再次执行慢速页表移动,直到TLB缓存再次“变热”。

就是为什么交换虚拟地址空间被认为“昂贵”的原因。不是因为很难写CR3,而是因为我们每次都将TLB丢弃。