CPU上下文切换

Posted galvin007

tags:

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

CPU上下文

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

CPU 寄存器,是 CPU 内置的容量小、但速度极快的内存。而程序计数器,则是用来存储 CPU 正在执行的指令位置、或者即将执行的下一条指令位置。它们都是 CPU 在运行任何任务前,必须的依赖环境,因此也被叫做 CPU 上下文。

CPU 上下文切换

  1. 就是先把前一个任务的 CPU 上下文(也就是 CPU 寄存器和程序计数器)保存起来
  2. 加载新任务的上下文到这些寄存器和程序计数器
  3. 跳转到程序计数器所指的新位置,运行新任务。

根据任务的不同,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 文件来分析具体的中断类型。

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

1.1多线程上下文切换

CPU上下文切换

java并发编程之上下文切换等并发编程的挑战

cpu上下文切换

使用 ThreadPoolExecutor 时看不到 CPU Bound 任务的上下文切换开销

CPU 上下文切换之理论篇(上)