性能文章>怒啃 24 小时,终于搞懂上下文切换!>

怒啃 24 小时,终于搞懂上下文切换!原创

331505

大家好,我是树哥。

对于服务器系统来说,上下文切换也是影响系统性能的一个重要因素。深入理解上下文切换的原理,有利于我们做好性能优化工作。今天我将带大家了解下上下文切换的几种情形,以及其背后发生切换的具体信息,接着介绍一些监测上下文切换指标的工具,最后总结一些上下文切换异常可能得场景。
image1.png

1. 什么是上下文切换?

我们知道 Linux 是一个多任务操作系统,它能支持远大于 CPU 数量的任务同时运行。但实际上同一时刻只会有 CPU 数量的进程在运行,等 CPU 时间片到了之后,进程调度器就会把 CPU 资源分配给其他进程。
在这个过程中就会涉及到进程之间的切换,这时候就需要将当前进程的上下文信息保存下来,随后加载被调度进程的上下文信息,这就是上下文切换。
这里所说的上下文信息,既包括虚拟内存、栈、全局变量等用户态的资源,也包括内核堆栈、寄存器等内核态的资源。不同类型的上下文切换,会涉及到不同类型资源的切换,例如:同一进程不同线程的切换,只需要切换内核态的资源,而不需要切换用户态的资源。

2. 上下文切换类型

上下文还分为了三种类型,分别是:

  • 进程上下文切换
  • 线程上下文切换
  • 中断上下文切换

进程上下文切换

进程上下文切换指的是不同进程之间发生切换。一般来说,进程被调度有如下几个时机:

  1. 某个进程时间片耗尽,会被系统挂起,切换到其他等待 CPU 的进程。
  2. 进程所需系统资源不足,需要等到资源满足时才可运行,此时会被挂起,其他进程会被调度。
  3. 进程通过 sleep 方法主动挂起,其他进程就有机会被调度。
  4. 有更高优先级的进程,当前进程会被挂起,高优先级进程会被调度。
  5. 硬件中断时,CPU 上的进程会被中断挂起,转而执行内核中的中断服务程序。

当发生如上几种情况的时候,就会发生进程调度,进而发生进程上下文切换。

线程上下文切换

我们都知道进程是资源分配的基本单位,线程是调度的基本单位,进程只是给线程提供了虚拟内存等资源。而线程上下文切换,就可以分为两种情况:

  1. 进程调度前后的两个线程,属于同一进程。此时因为资源共享,所以在切换的时候虚拟内存等这些资源就不需要变化,只需要切换线程的私有数据、寄存器等不共享的数据。
  2. 进程调度前后的两个线程,不属于同一进程。这时候因为资源部共享,所以切换过程和进程上下文切换是一样的。

所以你会发现同进程内的线程切换,要比多进程间的切换消耗更少的资源,这其实就是多线程比起多进程的一个优势。

中断上下文切换

中断上下文切换指的是为了响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将当前的状态保存下来。这样在中断结束后,进程仍然可以从原来的状态恢复运行。
中断上下文切换,并不需要保存和恢复进程的虚拟内存等用户态资源,只需要处理 CPU 寄存器、内核堆栈等内核态的资源即可。

分析工具

查看系统的上下文切换情况,有三个工具可以使用,分别是:vmstat 命令、pidstat 命令、/proc/interrupts 文件。

vmstat 命令

vmstat 是一个常用的系统性能分析工具,主要用来分析系统的内存使用情况,也常用来分析 CPU 上下文切换和中断的次数。该命令的语法格式为:

vmstat <选项> <时间间隔> <报告次数>

其中常用的选项有:
-a:显示活动内页;-f:显示启动后创建的进程总数;-m:显示 slab 信息;-n:头信息仅显示一次;-s:以表格方式显示事件计数器和内存状态;-d:报告磁盘状态;-p:显示指定的硬盘分区状态;-S:输出信息的单位。

我们执行 vmstat 5 命令后,会每隔 5 秒输出一次结果,如下所示。

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
 0  0      0 6893400   2352 563768    0    0     2   425  153   32  1  3 96  0  0

其中与上下文相关的 4 列内容如下:

  • cs(context switch)是每秒上下文切换的次数。
  • in(interrupt)则是每秒中断的次数。
  • r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待 CPU 的进程数。
  • b(Blocked)则是处于不可中断睡眠状态的进程数。

可以看到上面输出中上下文切换次数 cs 是 32 次,而系统中断次数 in 是 153 次,而就绪队列长度 r 和不可中断状态进程数 b 都是 0。

pidstat 命令

vmstat 只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们前面提到过的 pidstat 了。给它加上 -w 选项,你就可以查看每个进程上下文切换的情况了。 例如执行如下命令,我们可以得到每个进程的上下文切换情况了。

// 每隔 5 秒输出一次结果
// -w 表示显示每个进程的上下文切换情况
[root@iZwz92ezhi90syoqbgjgn1Z ~]# pidstat -w 5
Linux 4.18.0-348.7.1.el8_5.x86_64 (iZwz92ezhi90syoqbgjgn1Z)  23/08/22  _x86_64_ (4 CPU)

15:04:39      UID       PID   cswch/s nvcswch/s  Command
15:04:44        0         1      0.20      0.00  systemd
15:04:44        0        11     27.94      0.00  rcu_sched
15:04:44        0       497     19.56      0.00  xfsaild/vda3
15:04:44        0       603      0.20      0.00  systemd-journal
15:04:44        0       829      0.40      0.00  sssd_be
15:04:44        0       831      1.60      0.00  sssd_nss
15:04:44        0      3931      0.40      0.00  kworker/2:0-mm_percpu_wq
15:04:44        0      3998      0.40      0.00  kworker/1:0-mm_percpu_wq
15:04:44        0      4005      0.20      0.00  kworker/u8:0-flush-253:0
15:04:44        0      4021      0.80      0.00  kworker/3:2-mm_percpu_wq
15:04:44        0      4037      5.99      0.00  kworker/0:0-events

上述结果中的 cswch 与 nvcswch 是重点关注的对象。cswch 表示每秒自愿上下文切换(voluntary context switches)的次数,nvcswch 表示每秒非自愿上下文切换(non voluntary context switches)的次数。

所谓自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。
而非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。

/proc/interrupts 文件

我们可以通过 vmstat 获取中断的次数,但是我们却无法获取中断类型。实际上我们可以通过 /proc/interrupts 文件获取中断的详细信息。/proc 实际上是 Linux 的一个虚拟文件系统,用于内核空间与用户空间之间的通信。/proc/interrupts 就是这种通信机制的一部分,提供了一个只读的中断使用情况。
我们可以通过如下命令,动态观察中断的变化情况:

# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts
           CPU0       CPU1
...
RES:    4385721    4430589    3732298    4259089   Rescheduling interrupts
...

通过这种方式,我们就可以知道具体是哪种中断类型出现异常,从而定位到具体的资源。

如何排查异常?

今天我们深入了解了一下上下文切换这个指标,但每秒上下文切换多少次才算正常呢?

这个数值取决于系统本身的 CPU 性能。如果系统的上下文切换次数比较稳定,那么从数百到一万以内,都应该算是正常的。但当上下文切换次数超过一万次,或者切换次数出现数量级的增长时,就很可能已经出现了性能问题。
具体遇到问题的时候,需要根据变化的上下文切换类型,再做具体的分析。例如:

  • 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题。
  • 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈。
  • 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。

总结

首先,我们先介绍了上下文切换的概念,以及上下文切换可能会包含用户态资源和内核态资源。

接着,我们介绍了三种上下文切换类型,即:进程上下文切换、线程上下文切换、中断上下文切换,并分析其切换包括的资源类型。

接着,我们介绍了 vmstat 命令、pidstat 命令、/proc/interrupts 三种分析工具。其中 vmstat 命令用户查看系统整体上下文切换情况,pidstat 命令用户查看进程的上下文切换情况,/proc/interrupts 文件用户查看中断类型及详细情况。

最后,解释了合理的上下文切换次数应该是数百到一万每秒以内。但具体的问题排查,还需要根据上下文切换类型去分析。

点赞收藏
树哥聊编程
请先登录,感受更多精彩内容
快去登录吧,你将获得
  • 浏览更多精彩评论
  • 和开发者讨论交流,共同进步

为你推荐

线上问题排查,一不小心踩到阿里的 arthas坑了

线上问题排查,一不小心踩到阿里的 arthas坑了

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

【全网首发】一次想不到的 Bootstrap 类加载器带来的 Native 内存泄露分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

解读JVM级别本地缓存Caffeine青出于蓝的要诀 —— 缘何会更强、如何去上手

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

【全网首发】一次疑似 JVM Native 内存泄露的问题分析

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

解读JVM级别本地缓存Caffeine青出于蓝的要诀2 —— 弄清楚Caffeine的同步、异步回源方式

【全网首发】从源码角度分析一次诡异的类被加载问题

【全网首发】从源码角度分析一次诡异的类被加载问题

5
0