Linux CPU上下文切换
CPU上下文
在每个任务运行前,CPU 都需要知道任务从哪里加载、又从哪里开始运行,也就是说,需要系统事先帮它设置好 CPU 寄存器和程序计数器(Program Counter,PC)。
CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU 上下文。
CPU 上下文切换
- 就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来
- 加载新任务的上下文到这些寄存器和程序计数器
- 跳转到程序计数器所指的新位置,运行新任务。
根据任务的不同,CPU 的上下文切换就可以分为几个不同的场景,也就是进程上下文切换、线程上下文切换以及中断上下文切换。
进程和线程
进程是资源分配和执行的基本单位;线程是任务调度和运行的基本单位。线程没有资源,进程给指针提供虚拟内存、栈、变量等共享资源,而线程可以共享进程的资源。
进程上下文切换
Linux 按照特权等级,把进程的运行空间分为内核空间和用户空间。
CPU 特权等级的 Ring 0 和 Ring 3。
内核空间(Ring 0)具有最高权限,可以直接访问所有资源;用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入到内核中,才能访问这些特权资源。
进程既可以在用户空间运行,又可以在内核空间中运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。从用户态到内核态的转变,需要通过系统调用来完成。
一次系统调用的过程,其实是发生了两次 CPU 上下文切换。
线程上下文切换
线程的上下文切换其实就可以分为两种情况
- 前后两个线程属于不同进程。此时,因为资源不共享,所以切换过程就跟进程上下文切换是一样。
- 前后两个线程属于同一个进程。此时,因为虚拟内存是共享的,所以在切换时,虚拟内存这些资源就保持不动,只需要切换线程的私有数据、寄存器等不共享的数据。
中断上下文切换
为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将进程当前的状态保存下来,这样在中断结束后,进程仍然可以从原来的状态恢复运行。
对同一个 CPU 来说,中断处理比进程拥有更高的优先级,所以中断上下文切换并不会与进程上下文切换同时发生。同样道理,由于中断会打断正常进程的调度和执行,所以大部分中断处理程序都短小精悍,以便尽可能快的执行结束。
中断只会发生在内核态
查看中断类型
从/proc/interrupts 这个只读文件中读取。/proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况。
Vmstat
vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。
// 5s 输出间隔
[root@k8s ~]# vmstat 5
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r b swpd free buff cache si so bi bo in cs us sy id wa st
4 0 0 1150604 194172 2888576 0 0 4 238 77 35 61 3 36 0 0
2 0 0 1147236 194172 2888812 0 0 0 96 3977 4630 69 4 27 0 0
cs(context switch)是每秒上下文切换的次数。
in(interrupt)则是每秒中断的次数
r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
b(Blocked)则是处于不可中断睡眠状态的进程数。
Pidstat -w
pidstat给它加上 -w 选项,就可以查看每个进程上下文切换的情况了。
// -w参数表示输出进程切换指标,而-u参数则表示输出CPU使用指标
// pidstat 默认显示进程的指标数据,加上 -t 参数后,才会输出线程的指标。
pidstat 5 -w -u
Linux 3.10.0-1062.4.1.el7.x86_64 (instance-010oj085) 04/04/2020 _x86_64_ (2 CPU)
04:49:06 PM UID PID cswch/s nvcswch/s Command
04:49:11 PM 0 1 0.20 0.00 systemd
04:49:11 PM 0 6 1.20 0.00 ksoftirqd/0
cswch,表示每秒自愿上下文切换(voluntary context switches)的次数,nvcswch,表示每秒非自愿上下文切换(non voluntary context switches)的次数。
- 所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。
- 而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。
总结
- CPU 上下文切换,是保证 Linux 系统正常工作的核心功能之一,一般情况下不需要我们特别关注。
- 但过多的上下文切换,会把 CPU 时间消耗在寄存器、内核栈以及虚拟内存等数据的保存和恢复上,从而缩短进程真正运行的时间,导致系统的整体性能大幅下降。
上下文切换类型:
- 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题;
- 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈;
- 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。
学习笔记
来自极客时间:《Linux性能优化实战》