Linux CPU监控

2020-06-01 16:20:06 浏览数 (2)

1. CPU
1)uptime命令
代码语言:javascript复制
#uptime
08:26am up   7 min,  2 users, load average: 0.17, 0.16, 0.12

•08:26am。

当前时间是08:26am。

•up 7 min。

已经启动了7分钟。

•2 users。

当前有2个用户登录。

•load average:0.17, 0.16, 0.12。

最近1分钟、5分钟和15分钟的平均负载为0.17、0.16和0.12。

负载为1表示当前单核CPU全部占用,如果一台机器有3个CPU,每个CPU都是双核的,这是负载最大值为1×2×3=6。如果5分钟以及15分钟的负载指标的大于CPU个数×CPU核数×0.7,并且长时间比较高,说明CPU不够用。

•总核数 = 物理CPU个数×每颗物理CPU的核数。

•总逻辑CPU数 = 物理CPU个数×每颗物理CPU的核数×超线程数。

通过以下命令可以查看CPU个数、每个物理CPU中core的核数、逻辑CPU的个数和CPU信息(型号)。

•查看物理CPU个数。

代码语言:javascript复制
#cat /proc/cpuinfo| grep "physicalid"| sort| uniq| wc -l
2

2•查看每个物理CPU中core的核数。

代码语言:javascript复制
#cat /proc/cpuinfo| grep "cpu cores"|uniq
cpu cores: 2

•查看逻辑CPU的个数。

代码语言:javascript复制
#cat /proc/cpuinfo| grep "processor"|wc -l
4

4•查看CPU信息(型号)。

代码语言:javascript复制
#cat /proc/cpuinfo | grep name | cut -f2 -d: |uniq -c
     4  Intel(R) Core(TM) i5-6200U CPU@ 2.30GHz
案例:CPU负载分析

•Load average: 0.5,0.5,0.5。

表示最近15分钟内CPU负载没有变化。

•Load average: 0.5,2,5。

表示最近15分钟内CPU负载逐渐变小。

•Load average: 5,2,0.5。

表示最近15分钟内CPU负载逐渐变大。

2)top命令
代码语言:javascript复制
#top
top - 18:00:48 up 4 min,  1 user, load average: 0.88, 0.57, 0.26
Tasks: 343 total,   1 running, 229 sleeping,   0 stopped,  0 zombie
%Cpu(s): 2.6 us,  4.2 sy,  0.0 ni, 93.1 id,  0.0 wa, 0.0 hi,  0.2 si,  0.0 st
KiB Mem : 4312632 total,  1931028 free,  1392792 used,  988812 buff/cache
KiB Swap:  969960 total,   969960 free,        0 used. 2657976 avail Mem
PID USER     PR  NI    VIRT   RES    SHR S  %CPU %MEM    TIME  COMMAND
2179 jerry    20   0 3647552 177412  79692 S 14.2  4.1   0:10.86 gnome-shell
2049 jerry    20   0  474988 101148 47332 S   6.0  2.3  0:04.23
2531 jerry    20   0  800760 37440  27580 S   2.3 0.9   0:01.31 gnome-terminal-
2013 _apt     20   0   88940  8816   7840 S   1.7 0.2   0:02.26 http
…

可以按下键盘上的【1】键切换到显示各颗CPU的状态。

代码语言:javascript复制
top - 18:32:05 up 29 min,  1 user, load average: 0.91, 0.35, 0.13
Tasks: 304 total,   1 running, 225 sleeping,   0 stopped,  0 zombie
%Cpu0 :  2.0 us,  3.7 sy, 0.0 ni, 94.3 id,  0.0 wa,  0.0 hi, 0.0 si,  0.0 st
%Cpu1 :  0.7 us,  0.7 sy, 0.0 ni, 98.7 id,  0.0 wa,  0.0 hi, 0.0 si,  0.0 st
%Cpu2 :  0.3 us,  0.3 sy, 0.0 ni, 99.3 id,  0.0 wa,  0.0 hi, 0.0 si,  0.0 st
%Cpu3 :  0.3 us,  2.0 sy, 0.0 ni, 97.6 id,  0.0 wa,  0.0 hi, 0.0 si,  0.0 st

这里的CPU颗数为4。可以看出第一行的内容就是uptime命令的内容。

top - 18:00:48 up 4 min, 1 user, load average: 0.88, 0.57, 0.26

表示当前时间为18:00:48,持续运行了4分钟,当前有1个用户登录,最近1分钟、5分钟和15分钟的平均负载为0.88,0.57和0.26。

top命令第二行显示的是各个CPU状态的进程数。

Tasks: 343 total, 1 running, 229 sleeping, 0 stopped, 0 zombie

上面显示结果表示当前共有现在共有343个进程,1个处于运行状态,229个处于睡眠状态,停止状态和僵尸状态的进程都为0。对于各个状态,如表1所示。

表1 CPU状态

标号

中文名

英文名

解释

D

不可中断的睡眠态

uninterruptible sleep

进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。通常出现在I/O阻塞

R

运行态

running or runnable

表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行。

S

可中断的睡眠态

Interruptible sleep

进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。

T

被跟踪或已停止

stooped

表示进程处于暂停或者跟踪状态(命令行调试)

Z

僵尸态

zombie

进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。

I

空闲状态

Idle

也就是空闲状态,用在不可中断睡眠的内核线程上。D 状态的进程会导致平均负载升高, I 状态的进程却不会。

X

死亡状态

dead

用Top、PS命令获取不到

top命令第三行表示CPU概览:%Cpu(s)表示CPU使用百分比,按照时间占用计算,单位s。其含义如表2所示。

表2 CPU概览

标记

缩写

含义

user

%us

代表用户态 CPU 时间。注意,它不包括下面的 nice 时间,但包括了 guest 时间。

system

%sy

代表内核态 CPU 时间。

nice

%ni

代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。这里注意,nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。

idle

%id

代表空闲时间。注意,它不包括等待 I/O 的时间(iowait)。

iowait

%wa

代表等待 I/O 的 CPU 时间。

irq

%hi

代表处理硬中断的 CPU 时间。

softirq

%si

代表处理软中断的 CPU 时间。

steal

%st

虚拟 CPU 等待实际 CPU 的时间的百分比

guest

代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。

guest_nice

gnice

代表以低优先级运行虚拟机的时间。

代码语言:javascript复制
%Cpu(s): 2.6 us,  4.2 sy,  0.0 ni, 93.1 id,  0.0 wa, 0.0 hi,  0.2 si,  0.0 st

上面显示结果表示2.6%占有用户态 CPU 时间、4.2%占有内核态 CPU 时间,93.1%是空闲时间,处理软中断的占0.2%。

3)平均负载和CPU使用率

CPU 使用率,是单位时间内CPU繁忙情况的统计,和平均负载并不一定完全对应。

•CPU 密集型进程。

使用大量CPU会导致平均负载升高,平均负载和CPU使用率是一致的。

•I/O 密集型进程。

等待I/O也会导致平均负载升高,但CPU使用率不一定很高。

平均负载是指单位时间内,系统处于可运行状态的R状态进程数 不可中断状态的D 状态(Disk Sleep)进程数之。而处于可运行状态R状态的进程又包括正在使用 CPU进程和正在等待CPU的进程,不可中断状态的D 状态(Disk Sleep)进程即正处于等待I/O的进程。

4)不可中断的睡眠态进程

不可中断的睡眠态的进程一般均为在运行过程中需要I/O提供数据。处于等待I/O状态的进程,由于这种是不可被打断的并且又处于睡眠态,所以叫做不可中断的睡眠态。如果系统中不可中断的睡眠态的进程比较多,可以确认系统在I/O上遇到了瓶颈。而这些I/O往往是磁盘I/O。由于与磁盘读写有关系,建议使用dstat 命令(同时看见CPU与I/O信息)来分析。

代码语言:javascript复制
#dstat 10 1
You did not select any stats, using -cdngy bydefault.
--total-cpu-usage-- -dsk/total- -net/total- ---paging-----system--
usr sys idl wai stl| read  writ | recv  send     |  in  out | int   csw
  1   1  99  0  0 | 9232k   470k|   0      0     |    0   0 | 129   192

从上面可以看见有9232K的数据处于磁盘读操作,性能低的瓶颈可能有进程在读磁盘。

一般而言直接读写磁盘,对 I/O 敏感型应用(比如数据库系统)是很友好的,因为可以在应用中,直接控制磁盘的读写。但在这种情况是非常消耗CPU资源的,最好还是通过系统缓存来优化磁盘 I/O的读写,换句话说,删除O_DIRECT 这个选项就可以了。下面代码就是直接读写磁盘。

open(disk,O_RDONLY|O_DIRECT|O_LARGEFILE, 0755)

5)僵尸进程

现在来介绍一下僵尸进程,僵尸进程是由于父进程建立子进程以后没有及时回收造成的。看一下正常父进程创建子进程,子进程结束后父进程收回的流程。

(1)父进程创建子进程。

(2)父进程调用wait()或者waitpid()等待子进程结束。

(3)子进程结束向父进程发送SIGCHLD 信号。

(4)父进程收到SIGCHLD 信号后注册SIGCHLD信号的处理函数。

(5)父进程异步回收子进程的资源。

如果父进程在创建子进程后没有调用wait()或者waitpid()等待子进程结束或者子进程结束后没有给父进程发SIGCHLD 信号就造成僵尸进程,如下代码。

代码语言:javascript复制
intstatus = 0;
  for (;;) {
    for (int sign = 0; sign < 5; sign   ) {
      if(f()==0) {
        sub_process();
      }
    }
    sleep(3);
  }
while(wait(&status)>0);

这段代码while语句放在了循环外面,这样wait()方法没有时机被调用,所以容易产生僵尸进程。在调试的时候可以使用pstree-aps <PID> 查看<PID>下的所有子进程(其中参数a表示输出命令行选项、p表示PID以及s表示指定进程的父进程)。

代码语言:javascript复制
#pstree -aps 3785
systemd,1 auto noprompt
  └─systemd,1894 --user
      └─gnome-terminal-,2416
         └─bash,3772
             └─bash,3782
                  └─python3,3783 manage.py runserver 0.0.0.0:8000 --insecure
                      └─python3,3785 manage.py runserver 0.0.0.0:8000 --insecure
                          └─{python3},3787
6)CPU状态转换

CPU状态转换如图1所示。

图1 CPU状态转换图

7)软中断与硬中断

假设现在一家公司就有一名客服人员,这个客服人员就有一台座机,这种情况下用户碰到问题只能打电话给这个客服人员,如果有多个用户同时打入只能凭运气,先打通电话的人得到回答,其他人只能依次等待。显然这种处理机制是非常低效的,小公司可能还可以,大一点的公司就不行了。于是现在共有4-5位客服人员,建立总分机架构,1位负责总机(也可以交给语音提示来操作),负责把问题分给4个分机,让4个分机人员来处理具体的问题,这样一来效率就明显提高了。如果客户来电,总机负责人接电话分给分机人员(或通过语音提示用户拨打分机号)叫做硬中断,而分机负责人处理具体问题叫做软中断。Linux的CPU正是采用硬中断与软中断结合的方式来处理问题的。比如现在网卡告诉CPU,有一批数据要从网络中过来,希望系统做好接收准备,CPU手头的工作被打断(中断),将网络上的数据存储在寄存器中,然后呼起一个进程来处理后续操作,就回头处理刚才中断之前的工作了。被呼起的进程可以在后台“慢慢地”地把寄存器中的数据按照规定格式写入数据库中。这里CPU处理的过程就为硬中断过程,而进程把数据写入数据库中过程为软中断过程。具体如图2所示。

图2 软中断与硬中断

硬中断可以用命令cat /proc/interrupts来查看,而软中断可以用cat /proc/softirqs来查看。由于硬中断比软中断过程短得多,所以作为性能监控往往需要监控软中断。

代码语言:javascript复制
#cat /proc/softirqs
          CPU0       CPU1
HI:       0          0
TIMER:    811613     1972736
NET_TX:   49         7
NET_RX:   1136736    1506885
BLOCK:    0          0
IRQ_POLL: 0          0
TASKLET:  304787     3691
SCHED:    689718    1897539
HRTIMER:   0         0
RCU:       1330771   1354737

其中。

•TIMER。

定时产生的软中断。

•NET_RX。

网络接收产生的软中断。

•NET_TX。

网络发送产生的软中断。

•SCHED。

内核调度产生的软中断。

•RCU。

RCU产生的软中断。

扩展阅读:RCU[32]RCU(Read-Copy Update),顾名思义就是读/拷贝/修改。对于被RCU保护的共享数据结构,不需要获得任何锁就可以访问它,但写者在访问它时首先拷贝一个副本,然后对副本进行修改,最后使用一个回调(callback)机制在适当的时机把指向原来数据的指针重新指向新的被修改的数据。这个时机就是所有引用该数据的CPU都退出对共享数据的操作

另外也经常使用ps aux | grep softirq命令来查看产生中断进程。

代码语言:javascript复制
#ps aux | grep softirq
root        7  0.0  0.0     0     0 ?        S   Oct10   0:01 [ksoftirqd/0]
root       16  0.0  0.0     0     0 ?        S   Oct10   0:01 [ksoftirqd/1]

注意:有[]为内核进程,可见软中断进程属于内核进程。

在top命令中也可以看到软中断进程。

代码语言:javascript复制
#top
top - 10:50:58 up 1 days, 22:10,  1 user, load average: 0.00, 0.00, 0.00
Tasks: 122 total,   1 running, 71 sleeping,   0 stopped,   0 zombie
%Cpu0 :  0.0 us,  0.0 sy, 0.0 ni, 96.7 id,  0.0 wa,  0.0 hi, 3.3 si,  0.0 st
%Cpu1 :  0.0 us,  0.0 sy, 0.0 ni, 95.6 id,  0.0 wa,  0.0 hi, 4.4 si,  0.0 st
...
 
  PIDUSER      PR  NIVIRT    RES    SHR S %CPU %MEM     TIME       COMMAND
  7    root     20  0  0      0       0 S     0.3 0.0      0:01.64   ksoftirqd/0
  16   root     20  0  0      0       0 S     0.3  0.0     0:01.97   ksoftirqd/1
 2663root      20  0 923480 28292  13996S  0.3 0.3      4:58.66   docker-containe
 3699root      20  0 0        0     0 I      0.3 0.0      0:00.13   kworker/u4:0
 3708root      20  0 44572   4176   3512 R 0.3  0.1      0:00.07  top
    1root      20 0  225384   9136 6724 S  0.0  0.1      0:23.25   systemd
    2root      20  0 0         0     0    S   0.0 0.0      0:00.03  kthreadd

除了可以使用cat /proc/softirqs查看当前软中断状态,在实际工作中也常常使用watch -d cat /proc/softirqs来动态显示实时软中断状态。

案例:小包问题
代码语言:javascript复制
#watch -d cat /proc/softirqs
          CPU0        CPU1
HI:       0           0
TIMER:   1083906   2368646
NET_TX:   53       9
NET_RX:   1550643  1916776
BLOCK:   0           0
IRQ_POLL:0           0
TASKLET: 333637     3930
SCHED:   963675     2293171
HRTIMER: 0           0
RCU:      1542111   1590625

上面结果中可以看出,网络发送产生了大量软中断。然后通过sar -n DEV 1命令来进一步分析。

代码语言:javascript复制
#sar -n DEV 1
15:03:46 IFACE        rxpck/s  txpck/s   rxkB/s    txkB/s   rxcmp/s txcmp/s rxmcst/s   %ifutil
15:03:47eth0          35645.00 6304.00   857.43    358.11    0.00     0.00   0.00      0.01
15:03:47docker0       6302.00  12604.00  270.79    664.66    0.00     0.00   0.00      0.00
15:03:47 lo           0.00     0.00      0.00      0.00      0.00     0.00   0.00      0.00
15:03:47 veth9f6bbcd  6302.00  12604.00  356.95    664.66    0.00     0.00   0.00      0.05

在这里。

•15:03:46。

表示报告的时间。

•IFACE。

表示网卡名。

•rxpck/s和txpck/s。

分别表示每秒接收、发送的网络帧数,也就是PPS。

•rxkB/s和txkB/s

分别表示每秒接收、发送的千字节数,也就是BPS。

在上面结果中,857(txpck/s)×1024/35645(rxpck/s)= 24字节,说明平均每个网络帧只有24字节,这显然是很小的网络帧,也就是通常所说的小包问题。确定是小包是问题后,就可以使用类似tcpdump工具进行进一步排查了。

8)CPU使用率

CPU使用率=1-CPU空闲时间/CPU总时间。

平均CPU使用率=1- (CPU空闲时间New- CPU空闲时间Old)/ (CPU总时间New- CPU总时间Old)

top命令显示了系统总体的CPU和内存使用情况,以及各个进程的资源使用情况。而ps命令则只显示了每个进程的资源使用情况。

9)CPU节拍率

CPU节拍率指每秒钟CPU切换的次数,单位为HZ。一般为:100HZ、250HZ、1000HZ,如果CPU节拍率为250HZ,表示:每秒钟触发250次切换,即每次切换持续1/250s。CPU节拍率可以通过grep 'CONFIG_HZ='/boot/config-$(uname -r)命令得之。

代码语言:javascript复制
#grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250

如图3所示,当前Memory中有“0”“1”“2”“3”“4”5个请求等待CPU处理。在当前时间段内CPU处理第“2” 号请求,过了1/250s(假设CPU节拍率为250),处理第“3”号请求,然后依次循环处理“4”“0”“1”…号请求。

图3 CPU节拍率

10)上下文切换

CPU依次处理上述任务的调度方法是切换。切换分为“进程切换”“线程切换”和“中断切换”。中断切换即在本节“软中断与硬中断”中提及的,当系统中有非常重要的请求来临,CPU停止手头工作,触发硬中断。“进程切换”和“线程切换”,在切换前都要调取上次保存的信息,在切换后都要保存当前的信息。“进程切换”和“线程切换”合在一起叫做上下文切换(context switches)。图4为当前仅有2个任务等待CPU处理下的进程下文切换。

图4 进程上下文切换

由此可见上下文切换的优点在于,每个进(线)程具有同等的CPU处理权利,缺点是进(线)程的保存和载入消耗资源。由于线程信息比进程信息要少,所以线程上下文切换优于进程上下文切换。等待的进(线)程,位于CPU的最外层Ring3,而当前正在处理的进(线)程位于CPU内核,即Ring0,如图3-5所示。

图5 等待CPU处理的进程和正在CPU处理的进程所处CPU位置

可以通过命令vmstat interval count来查看CPU的中断数和进线程上下文切换数。在这里interval为多长描述输出一次,count为总共输出的次数。比如:“vmstat5 3”表示每5s输出一次,总共输出3次。

代码语言:javascript复制
#vmstat 1 1
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      7005360  91564 818900 0    0     0    0    25   33  0    0  1000  0

这里的输出含义见表3所示。

表3 vmstat命令输出详解

总标识

标识

意义

process

r

展示了正在执行和等待CPU资源的任务个数。当这个值超过了CPU个数,就会出现CPU瓶颈

b

每秒VMM等待队列的核心线程平均数

system

in

在某一段时间间隔中观察到的每秒设备中断数

cs

在某一段时间间隔中观察到的每秒上下文切换数

CPU

us

用户方式下花费的百分比

sy

系统方式下执行一个进程花费的百分比

id

没有使用本地磁盘I/O时CPU空闲或等待时间百分比

wa

等待I/O CPU时间百分比

在System中cs就表述在某一段时间间隔内每秒上下文切换的个数。上下文切换又分为自愿上下文切换(voluntary context switches)和非自愿上下文切换(non voluntary context switches)两种。自愿上下文切换是指到了切换时间点,进(线)程由于所需的资源不足,比如没有获得进(线)程处理所需的数据而资源CPU让出去,去处理其他进(线)程而非自愿上下文切换是指指到了切换时间点,没有数据资源不足的情形发生,此进(线)程不得不把CPU资源让出来去处理其他正在等待的进(线)程。所以当自愿上下文切换比较多,说明I/O或者内存存在瓶颈;而非自愿上下文切换比较多,说明目前有很多进(线)程需要CPU处理。可以通过命令pidstat(需要安装sysstat插件)查看自愿上下文切换和非自愿上下文切换。

代码语言:javascript复制
#每隔 5 秒输出 1 组数据
#pidstat -w 5
Linux 4.15.0 (ubuntu)  09/23/18 _x86_64_  (2 CPU)
 
08:18:26     UID       PID   cswch/snvcswch/s  Command
08:18:31       0         1    0.20     0.00     systemd
08:18:31       0         8    5.40     0.00     rcu_sched
...

这里的cswch/s和nvcswch/s就表示每秒自愿上下文切换和非自愿上下文切换个数。

11)perf top和perf record命令

perf top命令可以显示占用 CPU 时钟最多的函数或者指令,因此可以用来查找热点函数。如图6所示。

图6 perf top

perf top 虽然实时展示了系统的性能信息,但它的缺点是并不保存数据,也就无法用于离线或者后续进行分析。

perf record 则提供了保存数据的功能,保存后的数据,需要用 perf report 解析展示。

注意:并不是所有的函数或指令都可以用perf top或perf record获得的。

12)短时进程

对于一些仅存在几毫秒的进程,当数量很大的时候也会给CPU带来很大的负载,而这些进程基本上用top或ps命令很难被获取到,这个时候就需要使用execsnoop命令了。这个命令工具可以通过https://github.com/brendangregg/perf-tools/blob/master/execsnoo获得。

13)显示10个消耗CPU最多的进程

可以通过ps aux|sort -rnk 3|head -10查看10个消耗CPU最多的进程。

代码语言:javascript复制
#ps aux|sort -rnk  3|head -10
USER       PID  %CPU %MEM    VSZ  RSS   TTY      STAT START   TIME COMMAND
root        99   0.0  0.0     0    0?     S        08:18         0:00 [scsi_eh_20]
root        98   0.0  0.0    0     0?     S         08:18         0:00 [scsi_eh_19]
root        97    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_18]
root        96    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_17]
root        95    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_16]
root        94    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_15]
root        93    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_14]
root        92    0.0 0.0     0    0?     S         08:18         0:00 [scsi_eh_13]
root        91    0.0  0.0   0     0?     S         08:18         0:00 [scsi_eh_12]
14)在多CPU的系统里,查看所有CPU的信息

可以使用mpstat查看多CPU的系统里中的信息。

代码语言:javascript复制
#mpstat
Linux 4.15.0-46-generic(ubuntu) 10/30/2019 _x86_64_(4 CPU)
02:59:04 AM CPU  %usr  %nice   %sys   %iowait %irq   %soft  %steal %guest  %gnice   %idle
02:59:04 AM  all 15.29 4.91    17.24  2.76    0.00   1.08   0.00   0.00    0.00     58.71
15)小结

本节所涉及的概念有CPU负载、CPU使用率、不可中断的睡眠态进程、僵尸进程、CPU状态转换、软中断与硬中断、CPU节拍率和上下文切换。涉及到的命令有uptime、/proc/cpuinfo、top、dstat、pstree、/proc/softirqs、ps aux | grep softirq、watch、sar -n DEV 1、grep 'CONFIG_HZ=' /boot/config-$(uname -r)、vmstat、pidstat、perf top、perf record、execsnoop、ps aux|sort -rnk 3|head -10 和 mpstat。

0 人点赞