CPU上下文


Linux是一个多任务操作系统,它支持远大于CPU数量的任务同时运行。 这些任务实际上并不是真的在同时运行,而是因为系统在很短的时间内,将CPU轮流分配给它们,造成多任务同时运行的错觉。

每个任务运行前,CPU都需要知道任务从哪里加载、从哪里开始运行;也就是说,需要系统事先帮它设置好CPU寄存器和程序计数器(Program Counter,PC)。

CPU寄存器,是CPU内置的内存,程序计数器,是用来存储CPU正在执行的指令位置、或者即将执行的下一条指令位置。
CPU寄存器和程序计数器是CPU在运行任何任务前,必须的依赖环境,因此也被叫做CPU上下文。



CPU上下文切换


CPU的上下文切换就可以分为几个不同的场景,即进程上下文切换、线程上下文切换及中断上下文切换。

进程上下文切换

进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。
进程从用户态到内核态的转变,需要通过系统调用来完成;系统调用会造成CPU上下文切换:

  • 首先,保存CPU寄存器里原来用户态的指令位置。
  • 然后,更新CPU寄存器为内核态指令的新位置以执行内核代码。
  • 最后,跳转到内核态运行内核任务。

系统调用结束后,CPU寄存器需要恢复原来保存的用户态,再切换到用户空间,继续运行进程。
所以,一次系统调用的过程,其实是发生了两次CPU上下文切换。

进程的上下文切换比系统调用多了一步,在保存当前进程的内核状态和CPU寄存器之前,需要先把该进程的虚拟内存、栈等保存下来;而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。
保存和恢复的行为都需要内核在CPU上运行才能完成;而每次上下文切换都需要几十纳秒到数微秒的CPU时间。

进程调度到CPU上运行的时机:

  • 进程的时间片耗尽了,就会被系统挂起,切换到其它正在等待CPU的进程运行。
  • 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行;此时进程会被挂起,并由系统调度其他进程运行。
  • 进程通过睡眠函数sleep()这样的方法将自己主动挂起时,也会重新调度。
  • 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行。
  • 硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。

线程上下文切

线程是调度的基本单位,而进程则是资源拥有的基本单位。
所谓内核中的任务调度,实际上的调度对象是线程;而进程只是给线程提供了虚拟内存、全局变量等资源。

  • 当进程只有一个线程时,进程即等于线程。
  • 当进程拥有多个线程时,线程会共享相同的虚拟内存和全局变量等资源。

线程的上下文切换可以分为两种情况:

  • 前后两个线程属于不同进程;此时,因为资源不共享,切换过程等同于进程上下文切换。
  • 前后两个线程属于同一个进程;此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。

同进程内的线程切换,要比多进程间的切换消耗更少的资源;而这,也正是多线程代替多进程的一个优势。


中断上下文切

为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。
在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。

跟进程上下文不同,中断上下文切换并不涉及到进程的用户态。
所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。
中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等。

对同一个CPU来说,中断处理比进程拥有更高的优先级,所以中断上下文切换并不会与进程上下文切换同时发生。

文档更新时间: 2019-10-28 04:12   作者:闻骏