@yangwenbo
2023-02-10T17:39:06.000000Z
字数 10029
阅读 183
基础知识-CPU
你们好,可爱的小伙伴们^_^。
每当我们发现系统变慢时,通常做的第一件事,就是执行top或者uptime命令,来了解系统的负载情况。比如下面这样,我在命令行里输入了uptime命令,系统也随即给出了结果。
[root@localhost ~]# uptime
17:27:47 up 33 min, 2 users, load average: 0.63, 0.83, 0.88
但,我想问的是,你真的知道这里每列输出的含义吗?
我相信你对前面的几列比较熟悉,他们分别是当前时间,系统运行时间以及正在登陆的用户数
17:27:47 #系统当前时间
up 33 min #系统运行时间
2 users #正在登陆的用户数
而最后的三个数字呢,依次则是过去1分钟,5分钟,15分钟的平均负载值(Load Average)
平均负载值:
- 这个词对于很多人来说,可能既熟悉又陌生,我们每天的工作中,也都会提到这个词,但你真正理解它背后的含义吗?
- 我猜一定有人会说,平均负载不就是单位时间内的CPU使用率吗?上面的0.63,就代表CPU使用率是63%。
- 其实并不是如此,如果同学们方便的话,可以通过执行man uptime,来了解平均负载的详细解释。
- 简单讲:平均负载就是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数,也就是平均活跃进程数。它和CPU使用率并没有直接关系。
可运行状态的进程:指的是正在使用CPU或者正在等待CPU的进程,也就是我们常用ps命令看到的,处于R状态(Running 或 Runnable)的进程
[root@localhost ~]# ps aux | grep dd
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1160 103 0.1 106212 1724 pts/1 R+ 17:50 0:03 dd if=/dev/zero of=/tmp/test bs=1M count=1000
参数备注说明:
- [x] USER:该进程属于的用户。
- [x] PID:该进程的进程号。
- [x] %CPU:该进程使用掉的CPU资源百分比。
- [x] %MEM:该进程所占用的物理内存百分比。
- [x] VSZ:该进程使用掉的虚拟内存量(单位Kbytes)
- [x] RSS:该进程占用的固定的内存量(单位Kbytes)
- [x] TTY:该进程是在哪个终端机上面运作的,若与终端机无关,则显示“?”,另外,tty1-tty6是本机上面的登入者进程,若为pts/0等,则表示为由网络连接进主机的进程。
- [x] STAT:该进程目前的状态,主要的状态包括如下几种。
- R:正在运行,或者是可以运行。
- S:正在终端睡眠中,可以由某些信号唤醒。
- D:不可中断睡眠。
- T:正在侦测或者是停止了。
- Z:已经终止,但是其父进程无法正常终止它,从而变成zombie(僵尸)进程的状态
- +:前台进程。
- l:多线程进程。
- N:低优先级进程。
- <:高优先级进程。
- s:进程领导者。
- L:已将页面锁定到内存中。
- [x] START:该进程被触发启动的时间
- [x] TIME:该进程实际使用CPU运作的时间
- [x] COMMAND:该进程的实际命令。
不可中断状态的进程:
- 指的是正处于内核态关键流程中的进程,并且这些流程是不可打断的。
- 例如:等待硬件设备的I/O响应,也就是我们在ps命令中看到的D状态(Uninterruptible Sleep,也叫做Disk Sleep)的进程
比如,当一个进程向磁盘读写数据时,为了保证数据的一致性,在得到磁盘回复前,它是不能被其他进程或者中断打断的,这个时候的进程就处于不可中断状态。如果此时的进程被打断了,就容易出现磁盘数据与进程数据不一致的问题。 所以,不可中断状态实际上是系统对进程和硬件设备的一种保护机制。
平均活跃进程数:
- 直观的理解就是单位时间内的活跃进程数,但实际上是活跃进程数的指数衰减平均值。
- 这个指数衰减平均值的详细含义不用计较,这只是系统的一种更快速的计算方式,你把它直接当成活跃进程数的平均值也没问题。
既然,平均的是活跃进程数,那么最理想的,就是每个CPU上都刚好运行着一个进程,这样每个CPU都得到了充分利用。比如当平均负载为2时,意味着什么呢?
- 在只有2个CPU核心的系统上,意味着所有的CPU都刚好被完全占用。
- 在4个CPU的系统上,意味着CPU有50%的空闲
- 而在只有1个CPU核心的系统中,则意味着有一半的进程竞争不到CPU
说完了什么是平均负载值,现在我们再回到最开始的例子,不知道同学们能否判断出,在uptime命令的结果里,那三个时间段的平均负载数,多大的时候就说明系统负载高?或者是多小的时候就说明系统负载很低呢?
我们知道,平均负载最理想的情况是等于CPU个数。所以,在评判平均负载时,首先你要知道系统有几个CPU,这可以通过top命令或者从文件/proc/cpuinfo中读取,比如:
[root@localhost ~]# cat /proc/cpuinfo
processor : 0 #CPU的线程ID(逻辑CPU个数)
vendor_id : GenuineIntel
cpu family : 6
model : 94
model name : Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
stepping : 3
cpu MHz : 4007.997
cache size : 8192 KB
physical id : 0 #CPU的物理ID(物理CPU个数)
siblings : 2
core id : 0
cpu cores : 2 #单个物理CPU的总核心数
apicid : 0
initial apicid : 0
fpu : yes
fpu_exception : yes
cpuid level : 22
wp : yes
flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts mmx fxsr sse sse2 ss ht syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmon pebs bts xtopology tsc_reliable nonstop_tsc aperfmperf unfair_spinlock pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch ida arat epb xsaveopt pln pts dts fsgsbase bmi1 hle avx2 smep bmi2 invpcid rtm
bogomips : 8015.99
clflush size : 64
cache_alignment : 64
address sizes : 42 bits physical, 48 bits virtual
power management:
[root@localhost ~]# grep "processor" /proc/cpuinfo
processor : 0
processor : 1
processor : 2
processor : 3
[root@localhost ~]# grep "processor" /proc/cpuinfo | wc -l #该服务器的CPU一共有4个线程
4
[root@localhost ~]# egrep "physical id|cpu cores" /proc/cpuinfo
physical id : 0 #在物理ID为0的CPU上有两个CPU核心
cpu cores : 2
physical id : 0
cpu cores : 2
physical id : 1 #在物理ID为1的CPU上有两个CPU核心
cpu cores : 2
physical id : 1
cpu cores : 2
[root@localhost ~]# grep "physical id" /proc/cpuinfo | uniq | wc -l #该服务器一共有两个物理CPU
2
[root@localhost ~]# grep "processor" /proc/cpuinfo | wc -l #该服务器一共有8个逻辑CPU
8
有了CPU的核心数,我们就可以判断出,当平均负载值比CPU个数还大的时候,系统已经出现了过载。
不过,新的问题又来了。我们在例子中可以看到,平均负载有三个数值,到底该参考哪个呢?
实际上,都要看。三个不同时间间隔的平均值,其实给我们提供了,分析系统负载趋势的数据来源,让我们能更全面,更立体地理解目前的负载情况。
- 如果1分钟,5分钟,15分钟的三个值基本相同,或者相差不大,那就说明系统负载很平稳
- 但如果1分钟的值远小于15分钟的值,就说明系统最近1分钟的负载在减小,而过去15分钟内却有很大的负载。
- 反过来,如果1分钟的值远大于15分钟的值,就说明最近1分钟的负载在增加,这种增加有可能只是临时性的,也有可能还会持续增加下去,所以就需要持续观察。一旦1分钟的平均负载接近或超过了CPU的个数,就意味着系统正在发生过载的问题,这时就得分析调查是哪里导致的问题,并要想办法优化了。
这里我们再举个例子,假设我们在一个单CPU系统上看到平均负载为1.73,0.60,7.98,那么说明在过去的1分钟内,系统有73%的超载,而在15分钟内,有698%的超载,从整体趋势来看,系统的负载在降低。
那么,在实际生产环境中,平均负载多高时,需要我们重点关注呢?
在我看来,当平均负载高于逻辑CPU数量的70%的时候,你就应该分析排查负载高的问题了。一旦负载过高,就可能导致进程响应变慢,进而影响服务的正常功能。
但是,70%这个数字并不是绝对的,最推荐的方法,还是把系统的平均负载监控起来,然后根据更多的历史数据,判断负载的变化趋势。当发现负载有明显升高趋势时,比如说负载翻倍了,你再去做分析和调查。
现实工作中,我们经常容易把平均负载值和CPU使用率混淆,所以在这里,我也做一个区分。
可能你会疑惑,既然平均负载代表的是活跃进程数,那平均负载高了,不就意味着CPU使用率高吗?
我们还是要回到平均负载的含义上来,平均负载是指单位时间内,处于可运行状态和不可中断状态的进程数。所以,它不仅包括了正在使用CPU的进程,还包括等待CPU和等待I/O的进程。
而CPU使用率,是单位时间内CPU繁忙情况的统计,跟平均负载并不一定完全对应。比如:
- CPU密集型进程,使用大量CPU会导致平均负载升高,此时两者是一致的。
- I/O密集型进程,等待I/O也会导致平均负载升高,但CPU使用率不一定很高。
- 大量等待CPU的进程调度也会导致平均负载升高,此时的CPU使用率也会比较高。
下面,我们以三个示例分别来看这三种情况,并用iostat,mpstat,pidstat等工具,找出平均负载升高的根源。
机器配置:2CPU,8GB内存
预先安装stress和sysstat包
#系统环境示例
[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.5.1804 (Core)
[root@localhost ~]# uname -r
3.10.0-862.3.3.el7.x86_64
[root@localhost ~]# yum -y install epel-release
[root@localhost ~]# yum -y install stress sysstat
#更新sysstat版本(CentOS默认sysstat版本过低,不支持查看%wait)
[root@localhost ~]# ls
anaconda-ks.cfg sysstat-12.1.5-1.x86_64.rpm
[root@localhost ~]# yum -y localinstall sysstat-12.1.5-1.x86_64.rpm
[root@localhost ~]# pidstat -V
sysstat 版本 12.1.5
(C) Sebastien Godard (sysstat <at> orange.fr)
- stress:
- Linux系统压力测试工具,这里我们用作异常进程模拟平均负载升高的场景
- sysstat:
- 包含了常用的Linux性能工具,用来监控和分析系统的性能。
- 我们的案例会用到这个包的两个命令mpstat和pidstat
- mpstat:一个常用的多核CPU性能分析工具,用来实时查看每个CPU的性能指标,以及所有CPU的平均指标
- pidstat:一个常用的进程性能分析工具,用来实时查看进程的CPU,内存,I/O以及上下文切换等性能指标。
我们以下每个场景都要开三个终端,同时登陆到一台Linux机器中
下面所有命令,都是默认以root用户运行。所以,如果你是普通用户登陆的系统,一定要运行sudo su root命令切换到root用户。
[root@localhost ~]# uptime
22:46:07 up 26 min, 2 users, load average: 0.00, 0.06, 0.11
首先,我们在第一个终端运行stress命令,模拟一个CPU使用率100%的场景:
[root@localhost ~]# stress --cpu 1 --timeout 600
然后,在第二个终端运行uptime查看平均负载的变化情况:
#watch [参数] <command> 间歇执行命令
#-d 参数表示高亮显示变化的区域
[root@localhost ~]# watch -d uptime
20:49:05 up 53 min, 4 users, load average: 0.81, 0.57, 0.33
最后,在第三个终端运行mpstat查看CPU使用率的变化情况:
[root@localhost ~]# mpstat -P ALL 5
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
20时55分31秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
20时55分36秒 all 49.95 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 49.85
20时55分36秒 0 6.80 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 93.00
20时55分36秒 1 93.01 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 6.79
从终端二中可以看到,1分钟的平均负载会慢慢增加到趋近1.00,而从终端三中还可以看到,正好两个CPU的使用率之和趋近100%,但它的iowait只有0.这说明,平均负载的升高正是由于CPU使用率的升高
首先,还是运行stress命令,但是这次模拟I/O压力,即不停地执行sync:
但是,由于stress命令是通过刷新缓冲区内存到磁盘的方式来提高I/O压力;
可是,新安装的虚拟机的缓冲区并没有那么大,因此无法产生大的I/O压力;
所以,在这里我们利用stress的下一代stress-ng命令来实现模拟。
[root@localhost ~]# yum -y install stress-ng
[root@localhost ~]# stress-ng -i 1 --hdd 1 --timeout 6000
stress-ng: info: [49531] dispatching hogs: 1 hdd, 1 io
还是在第二个终端运行uptime查看平均负载的变化:
[root@localhost ~]# watch -d uptime
20:59:37 up 1:03, 4 users, load average: 2.74, 1.55, 0.97
然后在第三个终端运行mpstat查看CPU使用率的变化情况:
21时09分13秒 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
21时09分18秒 all 0.31 0.00 26.97 37.46 0.00 0.94 0.00 0.00 0.00 34.31
21时09分18秒 0 0.00 0.00 15.52 60.34 0.00 1.72 0.00 0.00 0.00 22.41
21时09分18秒 1 0.61 0.00 37.83 15.75 0.00 0.20 0.00 0.00 0.00 45.60
从这里可以看到,1分钟的平均负载慢慢增加到了2.74,其中两个CPU的使用率之和为68%,但是iowait高达76%,再加上系统调用增加的消耗为53%。
那么,到底是哪个进程,导致的iowait这么高呢?我们用pidstat来查询:
#间隔5秒输出1组数据,-d 查看进程指标
[root@localhost ~]# pidstat -d 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21时16分26秒 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
21时16分31秒 0 47719 0.00 0.00 0.00 311 kworker/u256:2
21时16分31秒 0 49532 0.00 1003121.76 486218.76 35 stress-ng-hdd
21时16分31秒 0 49533 0.00 0.00 0.00 62 stress-ng-io
平均时间: UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
平均时间: 0 47719 0.00 0.00 0.00 311 kworker/u256:2
平均时间: 0 49532 0.00 1003121.76 486218.76 35 stress-ng-hdd
平均时间: 0 49533 0.00 0.00 0.00 62 stress-ng-io
#间隔5秒输出1组数据,-u 查看CPU指标
[root@localhost ~]# pidstat -u 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21时17分18秒 UID PID %usr %system %guest %wait %CPU CPU Command
21时17分23秒 0 14 0.00 0.20 0.00 0.60 0.20 1 ksoftirqd/1
21时17分23秒 0 48943 0.00 9.40 0.00 0.60 9.40 0 kworker/u256:0
21时17分23秒 0 49532 0.80 49.20 0.00 0.20 50.00 0 stress-ng-hdd
21时17分23秒 0 49533 0.00 1.60 0.00 1.80 1.60 0 stress-ng-io
21时17分23秒 0 49538 0.00 2.40 0.00 1.00 2.40 0 kworker/0:7
21时17分23秒 0 49560 0.00 1.00 0.00 1.00 1.00 1 kworker/1:8
21时17分23秒 0 50294 0.00 0.20 0.00 0.00 0.20 1 pidstat
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 14 0.00 0.20 0.00 0.60 0.20 - ksoftirqd/1
平均时间: 0 48943 0.00 9.40 0.00 0.60 9.40 - kworker/u256:0
平均时间: 0 49532 0.80 49.20 0.00 0.20 50.00 - stress-ng-hdd
平均时间: 0 49533 0.00 1.60 0.00 1.80 1.60 - stress-ng-io
平均时间: 0 49538 0.00 2.40 0.00 1.00 2.40 - kworker/0:7
平均时间: 0 49560 0.00 1.00 0.00 1.00 1.00 - kworker/1:8
平均时间: 0 50294 0.00 0.20 0.00 0.00 0.20 - pidstat
可以发现,还是stress-ng进程导致的
当系统中运行进程超出CPU运行能力时,就会出现等待CPU的进程
比如,我们还是使用stress,但是这次模拟的是8个进程:
[root@localhost ~]# stress -c 8 --timeout 6000
stress: info: [60980] dispatching hogs: 8 cpu, 0 io, 0 vm, 0 hdd
由于系统只有两个CPU,明显比8个进程要少的多,因此,系统的CPU处于严重过载状态
[root@localhost ~]# uptime
21:26:49 up 1:31, 4 users, load average: 7.44, 4.37, 2.79
接着,再运行pidstat来看一下进程的情况:
[root@localhost ~]# pidstat -u 5 1
Linux 3.10.0-862.3.3.el7.x86_64 (localhost.localdomain) 2019年07月24日 _x86_64_ (2 CPU)
21时27分27秒 UID PID %usr %system %guest %wait %CPU CPU Command
21时27分32秒 0 60981 24.95 0.00 0.00 75.05 24.95 0 stress
21时27分32秒 0 60982 24.95 0.00 0.00 74.65 24.95 1 stress
21时27分32秒 0 60983 24.75 0.00 0.00 75.05 24.75 1 stress
21时27分32秒 0 60984 25.15 0.00 0.00 75.45 25.15 1 stress
21时27分32秒 0 60985 24.95 0.00 0.00 75.05 24.95 0 stress
21时27分32秒 0 60986 25.15 0.00 0.00 74.65 25.15 0 stress
21时27分32秒 0 60987 24.95 0.00 0.00 74.85 24.95 1 stress
21时27分32秒 0 60988 24.75 0.00 0.00 75.25 24.75 0 stress
21时27分32秒 0 60993 0.00 0.20 0.00 1.20 0.20 1 kworker/1:0
21时27分32秒 0 60998 0.00 0.20 0.00 0.00 0.20 1 pidstat
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 60981 24.95 0.00 0.00 75.05 24.95 - stress
平均时间: 0 60982 24.95 0.00 0.00 74.65 24.95 - stress
平均时间: 0 60983 24.75 0.00 0.00 75.05 24.75 - stress
平均时间: 0 60984 25.15 0.00 0.00 75.45 25.15 - stress
平均时间: 0 60985 24.95 0.00 0.00 75.05 24.95 - stress
平均时间: 0 60986 25.15 0.00 0.00 74.65 25.15 - stress
平均时间: 0 60987 24.95 0.00 0.00 74.85 24.95 - stress
平均时间: 0 60988 24.75 0.00 0.00 75.25 24.75 - stress
平均时间: 0 60993 0.00 0.20 0.00 1.20 0.20 - kworker/1:0
平均时间: 0 60998 0.00 0.20 0.00 0.00 0.20 - pidstat
可以看出,8个进程在争抢两个CPU,每个进程等待CPU的时间(也就是代码块中的%wait列)高达75%。这些超出CPU计算能力的进程,最终导致CPU过载。
平均负载值提供了一个快速查看系统整体性能的手段,反映了整体的负载情况。但,只看平均负载本身,我们并不能直接发现,到底是哪里出现了瓶颈。所以,在理解平均负载时,也要注意:
- 平均负载高有可能是CPU密集型进程导致的
- 平均负载高并不一定代表CPU使用率高,还有可能是I/O更繁忙了
- 当发现负载高的时候,你可以使用mpstat,pidstat等工具,辅助分析负载的来源