什么是CPU的上下文切换,程序又是如何运行的?
程序在运行的时候,CPU会将一部分数据加载到CPU的寄存器,它是属于CPU专属的高速缓存,并且将程序运行的地址加载到程序计数器中,而CPU寄存器+程序计数器统称为CPU的上下文?
我们的系统一般可以并行运行多个程序,儿多个进程并不是真正的并行运行的,CPU会同时在多个程序中进行切换,造成这种并行运行的“错觉”,而切换之前就要现将CPU的上下文保存起来从而下次执行是偶继续执行,这个叫做CPU的上下文切换。
CPU上下文切换的几个场景
- 进程之间的上下文切换
- 线程之间的上下文切换
响应系统中断的上下文切换
注: 还有一种场景就是系统调用的CPU上下文切换,我们的程序是运行在用户态的,而系统的资源是系统的内核态。一次操作往往需要多次系统调用,会发生多次CPU上下文切换比如:读取一个文件是以下3步。先open()打开一个文件,再read()读取磁盘数据,再write()写到标准数据流中。每一次系统调用会先保存用户态的数据,然后加载内核态数据,然后切换到内核态,操作完成后,在加载用户态数据,切换回用户态继续之前的操作。(俩次切换过程)不过我们一般称呼他们是特权切换,他们是不会发生进程切换的。
如图:
进程间切换:需要刷新系统的虚拟内存、栈等用户态信息,再保存CPU上下文。
进程切换的主要原因有:分配的运行时间片已到;系统资源不够【比如内存不够】等待资源时候会被挂起;调用Sleep函数;有更高优先级的进程进来等线程间切换:线程是CPU调度的最小单位,进程是CPU的最小的资源单位,所谓的CPU上下文切换实际是线程的CPU上下文切换分一下俩种情况:
- 如果俩个线程属于同一个进程:不需要切换用户态信息,再切换CPU上下文
- 如果俩个线程不属于同一个进程:需要切换用户态信息,再切换CPU上下文。
所以,现在程序大都用多线程取代多进程。
响应系统中断:为了响应硬件事件,往往回中断当前正在运行的进程。响应中断往往比进程优先级高。
如何定位CPU切换的场景
工具介绍
vmstat使我们常用的分析io的工具他也能分析cpu的上下文切换,使用方法如下:其中
- r 是runable或者runnaing的进程数
- b 是block的进程数
- system的in是中断数
- cs是切换数
1 | vmstat |
pidstat,和上期不一样的是如果我们想看cpu上下文切换数需要加上-w,-t是查看线程
- cswch/s是每秒自愿切换上下文的次数,比如:无法获取资源被挂起。
- nvcswch/s是每秒非自愿切换上下文的次数,运行时间片到了被挂起。
1 | pidstat -w -t -p <pid> |
查看中断原因watch -d cat /proc/interrupts
1 | watch -d cat /proc/interrupts |
分析问题路径
- 自愿上下文切换变多了,说明进程都在等待资源,有可能发生了 I/O 等其他问题;
- 非自愿上下文切换变多了,说明进程都在被强制调度,也就是都在争抢 CPU,说明 CPU 的确成了瓶颈;
- 中断次数变多了,说明 CPU 被中断处理程序占用,还需要通过查看 /proc/interrupts 文件来分析具体的中断类型。
具体排查路径如下:
- 我们先运行vmstat,一般cs在1w左右都是正常的如果超过1w,我们就要去调用pidstat查看;
- pidstat -w -u查看cpu的占用和cswch,如果是Java或者golang等多线程的程序要加-t看线程之间的cswch/s值
- 之后通过watch -d cat /proc/interrupts查看cpu的中断原因【watch -d ‘cat /proc/interrupts | sort -nr -k 2 ‘】