上下文切换
最近看的一篇论文中多次提到了上下文切换的问题,尤其是切换过程中的效率问题,涉及到了一些名词:进程、线程、上下文切换等。查阅一些资料,这里把学习到的东西整理总结一下,同时建立这些知识片之间的联系。希望能增加自己对这些内容的理解,同时给读者提供一些思路。
本文的主要内容如下:
- 进程和线程。包括两者的概念、性质;两者之间的区别和联系。
- 上下文切换。包括上下文切换概念和类型;触发条件;切换时的底层操作。
- 特权模式切换。包括模式切换的概念;与上下文切换的区别。
一、进程与线程
进程和线程是计算机操作、计算机编程过程中常用的实体单元。如我们在windows中的进程监控表中看到的进程信息,在Linux中使用kill -pid操作结束进程的操作,都涉及到进程的概念。当我们启用QQ程序,我们可以同时和多个用户聊天,发送消息的同时,还可以进行语音或视频通话;这里QQ程序启动之后就声称一个QQ进程,而我们同时进行的多个操作,可以由多个子进程完成、也可以由多个线程完成。下边理论的介绍这两个实体。
1.1 进程
进程的定义
狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
进程的结构特征
进程由数据、程序和进程控制块(PCB,Process Control Block)组成。
进程拥有代码和打开的文件资源、数据资源、独立的内存空间。
进程的特征
动态性:进程的实质是多道程序系统中的一次执行过程。进程是动态产生、动态消亡的。
并发性:任何进程都可以和其他进程并发运行。
独立性:进程是一个能够独立运行的基本单元,同时也是资源分配和调度的独立单元。
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按照各自独立的,不可预知的速度向前推进。
1.2 线程
线程的定义
线程,有时被称为轻量级进程(Light weight Process,LWP),是程序执行流的最小单元。
线程的结构特征
线程的实体包括程序、数据和线程控制块(TCB,Thread Control Block)。
线程拥有自己的栈空间。
线程的特征
并发性:一个进程内的多个线程可以并发执行;不同进程内的多个线程也可以并发执行。
共享进程资源:同一个进程中的线程具有相同的地址空间(进程的地址空间),所以线程可以访问该地址空间的每一个虚拟地址;线程也可以访问进程所拥有的已打开文件、信号量、定时器等。
1.3 进程、线程、程序之间的关系
- 进程可以认为是程序运行的一个实例。
- 一个进程可以包含一个或多个线程,并至少有一个线程。一个进程中的多个线程共享该进程的资源。
- 资源分配给进程。进程是最小的资源管理单元。
- 处理器分配给线程。真正在处理器上运行的是线程,线程是程序的实际执行者。线程是最小的执行单元。
二、上下文切换
考虑这样一个场景:进程A的功能是“从外部文件中读取大量的数字,存到数组中,之后对数组内容进行计算”;进程B的功能是“计算自己静态定义的数字之和”。此刻,进程A在CPU中处与运行状态;进程B处与阻塞状态。
如果进程A执行完之后才可以运行进程B,那么CPU在进程A执行大量的IO操作时,只能处与空闲状态。只有当进程A结束之后,才能执行进程B的计算操作。整体来看,CPU的利用率很低。进程的可并发执行解决了这个问题,也就是说,当进程A在执行IO密集型操作时,可以先将CPU调度给进程B,给进程B执行计算操作。之后再去解决进程A的计算操作。这种模式下,CPU的利用率变高,系统的吞吐率也变高。
在上边描述的场景中,涉及到CPU从一个进程切换到另一个进程的过程。那么如何保证进程在切换前后仍能保持自己原有的数据和执行状态不变呢?这里就涉及到CPU上下文切换。
2.1 上下文
CPU上下文
CPU上下文是:某一时间点的CPU寄存器和程序计数器。因为它们都是 CPU 在运行任何任务前,必须的依赖环境。
- CPU寄存器是CPU内置的容量小,但速度极快的内存。
- 程序计数器是CPU用来存储正在执行的指令的位置,或是即将执行的下一条指令的地址。
进程上下文
进程是由内核进行管理和调度的,进程的切换只能发生在内核态。这里我对进程上下文信息的理解为:进程上下文信息由两部分组成:进程用户态上下文、进程内核态上下文。
- 进程用户态上下文:
- 进程的虚拟地址空间。
- 进程的栈、全局变量等用户空间资源。
- 进程内核态上下文:
- 进程的内核堆栈等资源。
- 硬件上下文:CPU寄存器等硬件资源。
线程上下文
我没有找到具体的关于线程上下文的内容,根据个人理解,斗胆认为线程上下文内容是和进程上下文内容相同的。然而与进程上下文不同的是,各个进程上下文基本是独立存在的;但是同一进程的不同线程上下文共享相同的地址空间。
中断上下文
中断上下文具体包括:
- 包括内核态中断服务程序执行所必需的状态,包括 CPU 寄存器、内核堆栈、硬件中断参数等。
2.2 上下文切换
上下文切换(有时也称作进程切换和任务切换)是指CPU从一个进程或线程切换到另一个进程或线程的过程。
根据任务的不同,上下文切换包括以下四种。本文重点关注前三种,并分析他们进行上下文切换的本质。
- 进程上下文切换:A进程切换到B进程。
- 线程上下文切换:A线程切换到B线程。
- 中断上下文切换—进程/线程和中断之间的上下文切换:进程A被中断打断。
- 中断上下文切换—中断之间的上下文切换:低级别中断被高级别中断打断。
进程上下文切换
如下图,进程上下文切换包括进程1的上下文保存和进程2的上下文加载。这里上下文切换的细节操作如下:
-
保存该进程的用户态资源(虚拟内存空间、栈等)保存下来。
-
保存内核态资源(当前进程的内核状态和 CPU 寄存器)。
-
加载下一进程的内核态。
-
刷新进程的虚拟内存空间和用户栈。
![](https://i-blog.csdnimg.cn/blog_migrate/8770e9d94bd0346ed20e19156853c5df.png)
线程上下文切换
线程上下文切换可以分为两种:
- 属于同一进程的线程进行上下文切换。
- 不同进程的线程之间进行上下文切换。
对于第一种,由于同一进程的线程共享用户态资源,如虚拟内存空间,因此在这种情况下,线程切换是不需要切换页目录以使用新的地址空间,而线程拥有自己独立的栈。因此,此时的线程上下文切换过程如下:
-
保存该进程的用户态资源(栈、线程私有数据等)保存下来。
-
保存内核态资源(当前进程的内核状态和 CPU 寄存器)。
-
加载下一进程的内核态。
-
刷新进程的用户栈。
对于第二种情况,线程的切换,就是进程的切换,其过程与进程上下文切换相同,这里不做赘述。
中断上下文切换
这里只讨论进程被中断打断的情形。为了快速相应硬件事件,硬件中断会打断进程的正常执行,转而执行中断处理程序。这个过程中,需要保存进程的运行状态,保证中断程序之后,可以返回进程正常执行。
中断处理程序和用户进程不同,前者只在内核态运行。因此中断上下文切换不会影响用户态资源,故中断上下文切换过程如下:
-
保存进程的内核态资源(当前进程的内核堆栈、CPU 寄存器等)。
-
加载中断处理程序的内核态资源。包括 CPU 寄存器、内核堆栈、硬件中断参数等。
三、系统调用—特权模式切换
提到上下文切换,就不得不提一下特权模式切换。一般来说,用户态程序不能直接执行内核态的代码,但可以通过系统调用执行内核态代码。这个过程就是典型的用户态到内核态的切换。
那么问题来了,CPU的执行状态从用户态到内核态的过程也属于上下文切换吗?需要做类似于进程上下文切换的操作吗?
系统调用过程和进程切换有以下两点不同:
- 系统调用过程中,不会涉及到进程用户态的资源。
- 系统调用过程中没有涉及到进程的切换,调用前后都是在同一进程中。
因此,系统调用过程通常被称为特权模式切换,而不是上下文切换。但是系统调用过程中,仍然需要进行CPU上下文切换,即切换CPU寄存器信息和程序计数器。我们可以认为系统调用过程是同一进程内的CPU上下文切换。