研究怎么分析CPU上下文切换的问题

1.怎么查看系统上下文切换情况

vmstat是一个常用的系统性能分析工具,主要用来分析系统内存使用情况,也用来分析CPU上下文切换和中断次数

1
2
3
4
5
6
# 每隔5s输出一组数据
$  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
 5  0      0 1376000  10384 335352   0    0     1     0   97    0  0  0 99  0  0
  • cs(context switch)是每秒上下文切换次数
  • in(interrupt)则是每秒中断的次数
  • r(running or runnable)是就绪队列的长度,也就是正在运行和等待CPU的进程数。
  • b(blocked)则是处于不可中断睡眠状态的进程数。

pidstat -w 可以查看每个进程上下文切换的情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 每隔5s输出一组数据
$ pidstat -w 5
# 打印
Linux 4.19.76-linuxkit (4f913ef1ae8f) 	05/27/20 	_x86_64_	(4 CPU)

16:01:26      UID       PID   cswch/s nvcswch/s  Command
16:01:31        0        27      0.20      0.00  pidstat

16:01:31      UID       PID   cswch/s nvcswch/s  Command
16:01:36        0        27      0.20      0.00  pidstat

cswch ,表示每秒自愿上下文切换 (voluntary context switches)的次数

nvcswch ,表示每秒非自愿上下文 切换(non voluntary context switches)的次数。

自愿上下文切换,是指进程无法获取所需资源,导致的上下文切换。比如说, I/O、内存等系统资源不足时,就会发生自愿上下文切换。

非自愿上下文切换,则是指进程由于时间片已到等原因,被系统强制调度,进而发生 的上下文切换。比如说,大量进程都在争抢 CPU 时,就容易发生非自愿上下文切换。

2.案例分析

工具: sysbench 用户权限: root

操作: 步骤1. 在第一个终端运行sysbench,模拟系统多线程调度瓶颈

1
2
# 以10个线程运行5min的基准测试,模拟多线程切换的问题
$ sysbench --threads=10 --max-time=300 threads run

步骤2. 在第二个终端运行vmstat,观察上下文切换情况

1
2
3
4
5
6
7
8
9
# 每隔1s输出1组数据
$ vmstat 1
# 打印
procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
 r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
12  0      0 1357028  10408 349740    0    0     1     0  105   40  0  1 99  0  0
 9  0      0 1357028  10408 349748    0    0     0     0 37666 880945 25 68  8  0  0
 7  0      0 1357028  10408 349748    0    0     0     0 37639 916386 25 68  8  0  0
 5  0      0 1357028  10408 349748    0    0     0     0 37563 866896 26 67  7  0  0

观察指标(本地实验结果) r:就绪队列长度达到12(远超本地cpu个数4个),增加cpu竞争 us(user)和sy(system):系统cpu使用率为67%,说明cpu主要被内核占用了 in:中断次数上升到3.7万 分析得知:系统的就绪队列过长,也就是正在运行和等待 CPU 的进 程数过多,导致了大量的上下文切换,而上下文切换又导致了系统 CPU 的占用率升高。

步骤3. 在第三个中断使用pidstat分析,CPU和进程上下文切换的情况

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
# 每隔 1 秒输出 1 组数据
# -w 参数表示输出进程切换指标,而 -u 参数则表示输出 CPU 使用指标
$ pidstat -w -u 1
# 打印
Linux 4.19.76-linuxkit (4f913ef1ae8f) 	05/27/20 	_x86_64_	(4 CPU)

17:10:52      UID       PID    %usr %system  %guest   %wait    %CPU   CPU  Command
17:10:53        0        82   87.13  271.29    0.00    0.00  358.42     1  sysbench

17:10:52      UID       PID   cswch/s nvcswch/s  Command
17:10:53        0        93      0.99      1.98  vmstat
17:10:53        0        95      0.99     28.71  pidstat

分析:可以看出sysbench对cpu使用率高达358.42%,上下文切换频率低(这是因为sysbench主线程的上下文切换并不高,但它的子线程的切换却很高)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# -wt 参数表示输出线程的上下文切换指标
$ pidstat -wt 1
# 打印
Linux 4.19.76-linuxkit (4f913ef1ae8f) 	05/28/20 	_x86_64_	(4 CPU)

03:28:01      UID      TGID       TID   cswch/s nvcswch/s  Command
03:28:02        0         -        97  10069.23  67911.54  |__sysbench
03:28:02        0         -        98   6050.00  78664.42  |__sysbench
03:28:02        0         -        99   6675.96  79503.85  |__sysbench
03:28:02        0         -       100   7087.50  59591.35  |__sysbench
03:28:02        0         -       101   7745.19  82932.69  |__sysbench
03:28:02        0         -       102   6235.58  79698.08  |__sysbench
03:28:02        0         -       103   7875.96  86739.42  |__sysbench
03:28:02        0         -       104   6096.15  88155.77  |__sysbench
03:28:02        0         -       105   6515.38  74141.35  |__sysbench
03:28:02        0         -       106   6939.42  93804.81  |__sysbench
03:28:02        0       107         -      0.96      1.92  vmstat
03:28:02        0         -       107      0.96      1.92  |__vmstat
03:28:02        0       108         -      0.96    173.08  pidstat
03:28:02        0         -       108      0.96    173.08  |__pidstat

分析:可以看出子线程的cswch和nvcswch很高

步骤4.查看中断次数

中断只发生在内核态中,pidstat只是进程的分析工具,所以要从/proc/interrupts文件中读取

1
2
3
4
5
6
7
8
# -d 参数表示高亮显示变化的区域
$ watch -d cat /proc/interrupts

# 打印
# 数值分别对应的是cpu0 cpu1 cpu2 cpu3
...
RES:    3404165    3087853    3195615    3123227   Rescheduling interrupts
...

观察一段时间RES(重调度中断)一直在快速上升