系统优化-CPU-理解CPU的上下文切换

什么是CPU的上下文切换,程序又是如何运行的?

程序在运行的时候,CPU会将一部分数据加载到CPU的寄存器,它是属于CPU专属的高速缓存,并且将程序运行的地址加载到程序计数器中,而CPU寄存器+程序计数器统称为CPU的上下文?
我们的系统一般可以并行运行多个程序,儿多个进程并不是真正的并行运行的,CPU会同时在多个程序中进行切换,造成这种并行运行的“错觉”,而切换之前就要现将CPU的上下文保存起来从而下次执行是偶继续执行,这个叫做CPU的上下文切换。

CPU上下文切换的几个场景

  1. 进程之间的上下文切换
  2. 线程之间的上下文切换
  3. 响应系统中断的上下文切换

    注: 还有一种场景就是系统调用的CPU上下文切换,我们的程序是运行在用户态的,而系统的资源是系统的内核态。一次操作往往需要多次系统调用,会发生多次CPU上下文切换比如:读取一个文件是以下3步。先open()打开一个文件,再read()读取磁盘数据,再write()写到标准数据流中。每一次系统调用会先保存用户态的数据,然后加载内核态数据,然后切换到内核态,操作完成后,在加载用户态数据,切换回用户态继续之前的操作。(俩次切换过程)不过我们一般称呼他们是特权切换,他们是不会发生进程切换的。

    如图:
    avator

    进程间切换:需要刷新系统的虚拟内存、栈等用户态信息,再保存CPU上下文。
    进程切换的主要原因有:分配的运行时间片已到;系统资源不够【比如内存不够】等待资源时候会被挂起;调用Sleep函数;有更高优先级的进程进来等

    线程间切换:线程是CPU调度的最小单位,进程是CPU的最小的资源单位,所谓的CPU上下文切换实际是线程的CPU上下文切换分一下俩种情况:

    1. 如果俩个线程属于同一个进程:不需要切换用户态信息,再切换CPU上下文
    2. 如果俩个线程不属于同一个进程:需要切换用户态信息,再切换CPU上下文。
      所以,现在程序大都用多线程取代多进程。

    响应系统中断:为了响应硬件事件,往往回中断当前正在运行的进程。响应中断往往比进程优先级高。

如何定位CPU切换的场景

工具介绍

vmstat使我们常用的分析io的工具他也能分析cpu的上下文切换,使用方法如下:其中

  • r 是runable或者runnaing的进程数
  • b 是block的进程数
  • system的in是中断数
  • cs是切换数
1
2
3
4
5
vmstat

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 78018360 299560 30894864 0 0 0 7 0 0 1 0 99 0 0

pidstat,和上期不一样的是如果我们想看cpu上下文切换数需要加上-w,-t是查看线程

  • cswch/s是每秒自愿切换上下文的次数,比如:无法获取资源被挂起。
  • nvcswch/s是每秒非自愿切换上下文的次数,运行时间片到了被挂起。
1
2
3
4
pidstat -w -t  -p <pid>

08:30:23 PM UID PID cswch/s nvcswch/s Command
08:30:28 PM 669 27440 0.00 0.00 java

查看中断原因watch -d cat /proc/interrupts

1
2
3
4
5
6
7
$ watch -d cat /proc/interrupts
CPU0 CPU1
...
RES: 2450431 5279697 Rescheduling interrupts
...

res是指cpu调度中断,中断原因是cpu在频繁的切换线程

分析问题路径

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

具体排查路径如下:

  1. 我们先运行vmstat,一般cs在1w左右都是正常的如果超过1w,我们就要去调用pidstat查看;
  2. pidstat -w -u查看cpu的占用和cswch,如果是Java或者golang等多线程的程序要加-t看线程之间的cswch/s值
  3. 之后通过watch -d cat /proc/interrupts查看cpu的中断原因【watch -d ‘cat /proc/interrupts | sort -nr -k 2 ‘】