一、优化方向
1,性能指标
从应用负载的视角出发,考虑“吞吐”和“延时”
从系统资源的视角出发,考虑资源使用率、饱和度等
2,性能优化步骤
- 选择指标评估应用程序和系统的性能;
- 为应用程序和系统设置性能目标;
- 进行性能基准测试;
- 性能分析定位瓶颈;
- 优化系统和应用程序;
- 性能监控和告警。
3,Linux性能工具图谱
二、平均负载
1,stress
安装命令:apt install stress
#模拟一个CPU使用率 100%stress -c N
会让stress生成N个工作进程进行开方运算,以此对CPU产生负载。
stress --cpu 1 --timeout 600
#模拟I/O密集进程stress -i N
会产生N个进程,每个进程反复调用sync()将内存上的内容写到硬盘上, --timeout 600 表示600秒后退出 stress -i 1 --timeout 600
2,sysstat
安装命令:apt install sysstat
- mpstat 是一个常用的多核cpu性能分析工具,用来实时查看每个CPU的性能指标,以及所有CPU的平均指标
mpstat -P ALL 5
其中-P ALL 表示监控所有CPU
数字5,表示每间隔5秒输出一组数据- pidstat 是一个常用的进程性能分析工具,用来实时查看进程的CPU、内存、IO以及上下文切换等性能指标
pidstat -u 5 1
每间隔5秒输出一组数据
3,场景模拟
a>CPU密集
stress
#模拟一个CPU使用率 100% stress -c N 会让stress生成N个工作进程进行开方运算,以此对CPU产生负载。--timeout 600 表示600秒后退出
stress --cpu 1 --timeout 600
监控uptime,负载在升高
mpstat,发现一个CPU的使用率高达100%,但是iowait为0,说明平均负载的升高由于CPU的使用率为100%
pidstat,发现是stress进程导致CPU使用率升高
b>I/O密集
stress
#模拟I/O密集进程 stress -i N 会产生N个进程,每个进程反复调用sync()将内存上的内容写到硬盘上
stress -i 1 --timeout 600
监控uptime,负载在升高
mpstat,发现一个系统CPU使用率升至23.87%,iowait高达67.53% 。说明平均负载的升高由于iowait的升高
pidstat,发现是由于stress进程导致
c>大量进程的场景
stress
stress -c 8 --timeout 600
uptime
pidstat,发现8个进程在争抢2个CPU,每个进程等待CPU的时间高达75%,导致CPU过载
三、CPU的上下文切换
1,基本概念
CPU寄存器:CPU内置的容量小、但速度极快的内存
程序计数器:用于存储CPU正在执行的指令位置、或者即将执行的下一条指令位置
CPU上下文:CPU寄存器和程序计数器所必须的依赖环境
CPU上下文切换:先把前一个任务的CPU上下文(也就是CPU寄存器和程序计数器)保存起来,然后加载新任务的上下文到这些寄存器和程序计数器,最后再跳转到程序计数器所指的新位置,运行新任务。
2,分类
a>进程上下文切换
Linux按照特权等级,把进程的运行空间分为内核空间和用户空间,分别对应CPU特权等级的 Ring 0 和Ring 3
- 内核空间(Ring 0)具有最高权限,可以直接访问所有资源
- 用户空间(Ring 3)只能访问受限资源,不能直接访问内存等硬件设备,必须通过系统调用陷入内核中,才能访问这些特权资源
换个角度,也就是进程既可以在用户空间运行,又可以在内核空间运行。进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的内核态。
在进程切换时才需要切换上下文,也就是进程调度时。Linux为每个CPU都维护了一个就绪队列,将活跃进程按照优先级和等待CPU的时间排序,然后选择最需要CPU的进程,也就是优先级最高和等待CPU时间最长的进程运行。涉及到的场景包括:
- 为了保证所有进程可以得到公平调度,CPU时间被划分为一段段的时间片,这些时间片再被轮流分配给各个进程。这样,当某个进程的时间片耗尽了,就会被系统挂起,切换到其他正在等待CPU的进程运行
- 进程在系统资源不足(比如内存不足)时,要等到资源满足后才可以运行,这个时候进程也会被挂起,并有系统调度其他进程运行
- 当进程通过睡眠函数sleep这样的方法将自己主动挂起时,自然也会重新调度
- 当有优先级更高的进程运行时,为了保证高优先级进程的运行,当前进程会被挂起,由高优先级进程来运行
- 当发生硬件中断时,CPU上的进程会被中断挂起,转而执行内核中的中断服务进程
b>线程上下文切换
线程是调度的基本单位,而进程则是资源拥有的基本单位。
- 当进程只有一个线程时,可以认为进程就等于线程
- 当进程拥有多个线程时,这些线程会共享相同的虚拟内存和全局变量等资源。这些资源在上下文切换时不需要修改
- 线程也有自己的私有数据,比如栈和寄存器,这些在上下文切换时也需要保存
线程上下文切换:1,前后两个线程属于不同进程,由于资源不同就是进程上下文切换;2,前后两个线程属于同一个进程,此时虚拟内存共享,切换时只需要切换线程的私有数据、寄存器等不共享的数据。
c>中断上下文切换
- 为了快速响应硬件的事件,中断处理会打断进程的正常调度和执行,转而调用中断处理程序,响应设备事件。而在打断其他进程时,就需要将进程当前的状态保存起来,这样在中断结束后,进程仍然可以从原来的状态恢复运行
- 与进程上下文切换不同,中断上下文切换并不涉及进程的用户态。所以,即便中断过程打断了一个正处在用户态的进程,也不需要保存和恢复这个进程的虚拟内存、全局变量等用户态资源。中断上下文,其实只包括内核态中断服务程序执行所必需的状态,包括CPU寄存器、内核堆栈、硬件中断参数等
3,CPU上下文切换实战
a>vmstat
- cs(context switch)是每秒上下文切换的次数
- in(interrupt)是每秒中断的次数
- r(Running or Runnable)是就绪队列的长度,也就是正在运行和等待的CPU的进程数‘
- b(Blocked)是处于不可中断睡眠状态的进程数
b>pidstat
vmstat只给出了系统总体的上下文切换情况,要想查看每个进程的详细情况,就需要使用我们pidstat。加上-w选项,则可以查看每个进程上下文切换的情况
- cswch(voluntary context switches),每秒自愿上下文切换次数。指进程无法获取所需要资源,导致的上下文切换。比如:I/O、内存等系统资源不足时,就会发生自愿上下文切换。
- nvcswch(non voluntary context switches),每秒非自愿上下文切换。指进程由于时间片已到等原因,被系统强制调度,进程发生的上下文切换。比如:大量进程都在争抢CPU时,就容易发生非自愿上下文切换。
c>案例实操
查看系统的上下文切换次数
采用sysbench进行压测
再次查看vmstat
发现cs列的上下文切换数量从之前的35骤然上升到了139万多。同时,r列:就绪队列的长度为8,远超CPU的个数2,所以判定有大量的CPU竞争;us(user)和sy(system)列:这两列的CPU使用率加起来上升到了100%,其中系统CPU使用率,也就是sy列高达84%,说明CPU主要是被内核占用了;in列:中断次数也上升到1万左右,说明中断处理也是个潜在的问题。综合这几个指标,系统的就绪队列过长,也就是正在运行和等待CPU的进程数过多,导致大量的上下文切换,而上下文切换又导致系统的CPU的占用率升高。
采用pidstat分析
- CPU使用率升高果然是sysbench导致的,已达100%
- 非自愿上下文切换最高的为pidstat,自愿上下文切换频率最高的线程为kworker和sshd
- 问题:pidstat输出的上线文切换明显小于vmstat输出的上下文切换。采用man pidstat 发现,pidstat默认显示进程的指标数据,加上-t参数后,才会输出线程指标
结合两次的pidstat可以看出,sysbench(主线程)的上下文切换次数看起来并不太多,但它的子线程的上下文切换次数却很多。
采用watch观察interrupts中断
观察发现,变化速度最快的是重调度中断(RES),表示唤醒空闲状态的CPU来调度新的任务运行。这是多处理器系统(SMP)中,调度器用来分散任务到不同CPU的机制,通常也被称为处理器间中断(Inter-Processor Interrupts, IPI)。
d>总结
如果系统的上下文切换次数比较稳定,那么从数百到一万内,都应该算是正常的。当上下文切换次数超过一万次,或者切换次数出现数量级的增长时,都很可能已经出现了性能问题:
- 自愿上下文切换变多,说明进程都在等待资源,有可能发生了I/O等其他问题
- 非自愿上下文切换变多,说明进程都在被强制调度,也就是都在争抢CPU,说明CPU的确成了瓶颈
- 中断次数变多了,说明CPU被中断处理程序占用,需要查看/proc/interrupts文件来分析具体的中断类型