分享

CPU眼里的上下文Context

 山峰云绕 2023-08-06 发布于贵州

https://m.toutiao.com/is/iJYxVQE9/?= 


上下文是什么?用抽象去解释抽象,往往让人晕头转向‍”

01

提出问题

线程切换、或任务切换是一个老生常谈的话题,拥有着旺盛的生命力。因为它是操作系统的核心,是软件和CPU硬件的完美结合体。当然,也正是因为有 CPU 这层硬件阻隔,让我们很难看清:线程切换的庐山真面目。

02

代码实验

阿布找到了一块“透明”的单核、没有cache的CPU和内存,希望能帮你一窥其中的奥秘,如图所示:

方便起见,假设:线程Thread 1的代码、数据、堆栈,被操作系统分配在上边的内存颗粒上;线程Thread 2的代码、数据、堆栈,被分配在下边的内存颗粒上;中间的内存颗粒,则存放着操作系统内核的代码、数据。当然,内核不是我们今天讨论的重点。

好,系统开机,一切从内核开始,先创建两个线程的初始上下文(也就是初始的CPU寄存器信息),并存入到相应的内存颗粒上,如图所示:

经过内核的线程调度,决定线程Thread1运行,也就是用线程 Thread1 的上下文,来设置 CPU 寄存器,其中eip寄存器让CPU跳转到函数 Thread1上运行,如图所示:

线程Thread 1 的代码非常简单,就是作一次加法运算。考虑到我们的重点是CPU操作,所以只关心下面的汇编指令部分。

先运行前3条CPU指令,如图所示:

其中头2条指令是设置函数“栈帧”,也就是设置CPU寄存器 ebp、esp的值。具体意图,可以参看上篇文章“CPU眼里的{函数括号}”;第3条mov指令,则是将变量 a 的值:1,存入寄存器 eax。

此时,发生时钟中断,CPU跳转到内核代码,内核将当前的CPU上下文,也就是线程 1 的上下文,保存到线程 1 所在的内存颗粒上,如图所示:

这次,调度器决定让线程Thread 2运行,同样,也就是用线程Thread 2 的上下文,来设置 CPU 寄存器,如图所示:

其中 eip 寄存器让CPU跳转到函数Thread 2上运行,线程Thread 2是一个空函数,但依然对应了4条CPU指令。先运行第一条 push 指令,如图所示:

如你所见,它会改变CPU寄存器esp的值。这时,再次发生时钟中断,CPU再次跳转到内核代码。内核又将当前的CPU上下文,也就是线程Thread 2 的上下文,保存到线程Thread 2所在的内存颗粒上,如图所示:

这次,调度器决定让线程Thread 1继续运行,还是老办法,通过用线程Thread 1的上下文,来设置 CPU 寄存器,如图所示:

这样,eip寄存器让我们再次回到了:函数刚刚被打断的地方;寄存器 eax ,也恢复到:获得变量a的值(1)的状态。万事俱备,线程Thread 1又可以继续运行了,就好像Thread 1从未被打断、休眠一样。

好了,到这里,一个完整的上下文保存、恢复和切换的过程就完成了!如你所见,保存和恢复上下文,不过是在保存和恢复CPU的寄存器。

当然,根据被打断的程序主体和时机的不同,还会把上下文细分成:线程上下文、进程上下文、中断上下文。名称虽多,但它们都是:一样的配方、一样的味道。

03

总结

1. 简单的说:上下文,就是CPU当前的寄存器信息。保存、恢复上下文的过程,就像是保存和恢复一个人的记忆;通过恢复CPU过去的记忆,让CPU回到过去的状态。

2. 除了中断、任务调度会强行保存、恢复:上下文;程序也可以主动保存上下文。例如:通过调用:sleep、mutex、semaphore主动放弃线程或进程的运行机会,迫使操作系统保存上下文。

3. 不同线程的代码、数据、堆栈也可以放在同一个内存颗粒上,一般来说,不同线程的代码、数据,是不会被混淆的。当然为了安全,也可以进程的形式运行,因为MMU可以实现进程间,在内存空间上的隔离。

最后,实际情况下,程序用到的寄存器不止我们提到的4个,需要对更多的寄存器进行保存和恢复工作。不同的操作系统、CPU在实现上下文上面,也会有所差异。

04

热点问题

Q1:如果有多个线程的情况,是不是需要多个CPU寄存器?

A1:不是的。CPU寄存器是CPU的固有硬件,一个CPU核心只有一套CPU寄存器。如你所见,在本文的实例中,有2个线程,但CPU只有1个,对应的CPU寄存器也只有一套。

但因为每个线程的运行状况,往往是不同的,所以它们的上下文也往往是不同的。因此每个线程,都需要保存各自运行时的上下文。

通常,操作系统会把上下文保存在每个线程、进程的任务控制块TCB中。

Q2:多核的CPU,是不是意味着有多套寄存器?

A2:是的。每一个CPU对应着一套寄存器,多个(多核)CPU就对应着多套寄存器。由于每个CPU都可以独立运行,所以可以有效提高整个系统的运行效率。特别是在单个CPU核心频率很难提升的情况,多核CPU是一个不错的解决方案。

但在处理线程、任务、进程切换时,多核操作系统需要考虑的事情也就更多,实现上也会复杂一些。

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多