上下文切换到底是什么?

Posted 张维鹏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了上下文切换到底是什么?相关的知识,希望对你有一定的参考价值。

1、内核空间的用户空间:

        用户态和内核态是操作系统的两种运行状态,划分为这两种空间状态主要是为了对应用程序的访问能力进行限制,防止应用程序随意进行一些危险的操作导致系统崩溃,比如设置时钟、内存清理,这些都需要在内核态下完成

  • 内核态:内核态运行的程序可以访问计算机的任何数据和资源,不受限制,比如外围设备网卡、硬盘等,而在用户态状态下的线程是没有权限访问这些资源的。处于内核态的 CPU 可以从一个程序切换到另外一个程序,并且所占用的 CPU 不会发生抢占情况。
  • 用户态:用户态运行的程序只能受限地访问内存空间,只能直接读取用户程序的数据,不允许访问外围设备,用户态下所占有的 CPU 能够被其他程序抢占,不允许独占

        如果应用程序需要使用到内核空间的资源,则需要通过系统调用来完成,从用户空间切换到内核空间,直到完成相关的操作后再切合用户空间,两种状态间的切换,就涉及到 CPU 的上下文切换

2、系统调用的 CPU 上下文切换:

系统调用过程中是如何发生 CPU 上下文的切换的呢?我们再了解两个概念:

(1)CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。

(2)程序计数器,则是用来存储 CPU 正在执行的指令位置以及即将执行的下一条指令位置。

        CPU 寄存器和程序计数器都是 CPU 在运行任何任务时必须的依赖环境,因此也被叫做 CPU 上下文。而 CPU 上下文切换,就是先把前一个任务的 CPU 上下文保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。而这些保存下来的上下文,会存储在系统内核中,并在任务重新调度执行时再次加载进来。这样就能保证任务原来的状态不受影响,让任务看起来还是连续运行。

        回到系统调用的问题上,为了切换到内核态,需要先保存 CPU 寄存器中用户态的指令位置,然后更新 CPU 寄存器为内核态指令的新位置,最后跳转到内核态运行内核任务。而系统调用结束后,CPU 寄存器需要恢复原来保存的用户态,然后再切换到用户空间,继续运行进程。所以,一次系统调用的过程,其实是发生了两次 CPU 上下文切换。

3、系统调用的上下文切换与进程的上下文切换:

        系统调用过程中,跟我们常说的进程上下文切换是不一样的:进程上下文切换是指从一个进程切换到另一个进程运行,而系统调用过程中一直是同一个进程在运行,不会切换线程。那么两者的具体区别呢?首先,进程是由内核来管理和调度的,进程的切换只能发生在内核态。所以,进程的上下文不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态。因此,进程的上下文切换就比系统调用时多了一步,在保存当前进程的内核状态和 CPU 寄存器之前,需要先把该进程的虚拟内存、栈等保存下来,而加载了下一进程的内核态后,还需要刷新进程的虚拟内存和用户栈。

        上下文的保存和恢复过程是有成本的,需要内核在 CPU 上完成,每次切换都需要几十纳秒到数微秒的 CPU 时间,在进程上下文切换次数较多的情况下,很容易导致 CPU 将大量时间耗费在寄存器、内核栈以及虚拟内存等资源的保存和恢复上。

4、线程上下文切换:

        线程与进程最大的区别在于,线程是调度的基本单位,而进程则是资源拥有的基本单位。内核中的任务调度,实际上的调度对象是线程;同一个进程中的所有线程共享进程的虚拟内存、全局变量等资源。这么一来,线程的上下文切换其实就可以分为两种情况:

(1)第一种,前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样,不仅包括了虚拟内存、栈、全局变量等用户空间的资源,还包括了内核堆栈、寄存器等内核空间的状态的修改

(2)第二种,前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存、全局变量这些资源就保持不动,只需要切换线程的私有数据,比如栈和寄存器等不共享的数据。

        所以虽然同为上下文切换,但同进程内的线程切换,要比多进程间的切换消耗更少的资源,而这,也正是多线程代替多进程的一个优势。

以上是关于上下文切换到底是什么?的主要内容,如果未能解决你的问题,请参考以下文章

Epoll 到底是什么?“不” 简单的网络I/O模型?

Java实用面试题及参考答案分享

内核栈有啥用?

Linux IO模式

IO多路复用

Linux什么是CPU上下文切换?