CPU使用率是单位时间内CPU使用情况的统计,以百分比方式展示。
$ top top - 11:46:45 up 7 days, 11:52, 1 user, load average: 0.00, 0.01, 0.00 Tasks: 198 total, 1 running, 197 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.2 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem : 4044232 total, 420136 free, 1061244 used, 2562852 buff/cache KiB Swap: 1046524 total, 1043128 free, 3396 used. 2619124 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 921 root 20 0 531332 138408 58972 S 0.7 3.4 41:31.24 Xorg
参数解析如下:
- %user(us),代表用户态 CPU 时间。不包括下面的 nice 时间,但包括了 guest 时间。
- %nice(ni),代表低优先级用户态 CPU 时间,也就是进程的 nice 值被调整为 1-19 之间时的 CPU 时间。nice 可取值范围是 -20 到 19,数值越大,优先级反而越低。
- %system(sys),代表内核态 CPU 时间。
- %idle(id),代表空闲时间。不包括等待 I/O 的时间(iowait)。
- %iowait(wa),代表等待 I/O 的 CPU 时间,处于不可中断状态。
- %irq(hi),代表处理硬中断的 CPU 时间。
- %softirq(si),代表处理软中断的 CPU 时间。
- %steal(st),代表当系统运行在虚拟机中的时候,被其他虚拟机占用的 CPU 时间。
- %guest(guest),代表通过虚拟化运行其他操作系统的时间,也就是运行虚拟机的 CPU 时间。
- %guest_nice(gnice),代表以低优先级运行虚拟机的时间。
%CPU,每个进程的实时 CPU 使用率,是用户态和内核态 CPU 使用率的总和。包括进程用户空间使用的 CPU、通过系统调用执行的的内核空间 CPU 、以及在就绪队列等待运行的 CPU。在虚拟化环境中,它还包括了运行虚拟机占用的 CPU。
查看CPU使用的命令
(1)top 默认使用 3 秒时间间隔,显示了系统总体的 CPU 和内存使用情况,以及各个进程的资源总体使用情况。vmstat、mpstat 提高更详细的分析多进程或者多线程应用。
(2)ps 使用的却是进程的整个生命周期,显示了每个进程的资源使用情况。
(3)pidstat 查看每个进程的详细CPU使用情况,包括用户态和内核态CPU。
CPU使用率是通过CPU时间计数计算而来
而我们通常所说的 CPU 使用率,就是除了空闲时间外的其他时间占总 CPU 时间的百分比:
事实上,性能工具是取间隔时间作差,得到这段时间的平均CPU使用率:
同样Linux也给每个进程统计信息,/proc/[pid]/stat,通过上述方式计算使用率。
为了维护 CPU 时间,Linux 通过事先定义的节拍率HZ,触发时间中断,并使用全局变量 Jiffies 记录了开机以来的节拍数。每发生一次时间中断,Jiffies 的值就加 1。节拍率 HZ 是内核的可配选项,可以设置为 100、250、、1000 等。不同的系统可能设置不同数值,你可以通过查询 /boot/config 内核选项来查看它的配置值。$ grep 'CONFIG_HZ=' /boot/conf-$(uname -r)
节拍率 HZ 是内核选项,用户空间程序并不能直接访问,为了方便用户空间程序,内核还提供了一个用户空间节拍率 USE_HZ,它总是固定为 100,也就是 1/100 秒。
Linux 通过 /proc 虚拟文件系统,向用户空间提供系统内部状态的信息,而 /proc/stat 提供的就是系统的 CPU 和任务统计信息,每一列则表示不同场景下 CPU 的累加节拍数,单位是 USER_HZ。
CPU使用率过高,如何进一步分析进程
perf 以性能事件采样为基础,不仅可以分析系统的各种事件和内核性能,还可以用来分析指定应用程序的性能问题。
(1)perf top,类似于 top,它能够实时显示占用 CPU.时钟最多的函数或者指令,中间不保存数据,因此可以用来查找热点函数。若定位到的函数是十六进制地址,说明没有找到待分析进程所依赖的库,解决方法是添加依赖库,或用perf record后在有依赖的环境中查看。
$ perf top Samples: 175 of event 'cpu-clock', Event count (approx.): 43750000 Overhead Shared Object Symbol 6.86% perf [.] 0x00000000000a34ac 5.71% [kernel] [k] kallsyms_expand_symbol.constprop.1 5.14% [kernel] [k] format_decode // 第一行 采样数(Samples)、事件类型(event)和事件总数量(Event count) // 采集了 175 个 cpu-clock 事件,而总事件数则为 43750000 // 采样数过少,只有十几个时,下面的排序和百分比没有实际参考价值 // Overhead 是该符号的性能事件在所有采样中的比例 // Shared 是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。 // Object 是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而 [k] 则表示内核空间。 // Symbol 是符号名,也就是函数名。当函数名未知时,用十六进制的地址来表示。
(2)perf record 和 perf report,perf record 提供了保存数据的功能,保存后的数据,需要用 perf report 解析展示,用于离线或者后续的分析。
$ sudo perf record -g --all-cpus // ^C 停止记录,数据保存在 perf.data 中,-g 开启调用关系的采样,--all-cpus 采集所有CPU事件 $ sudo perf report -g
CPU使用率过高的总体分析步骤
Step1:通过 top、pidstat 找到哪个进程CPU使用率过高;
Step2:通过 perf 找到该进程中具体哪个函数使用过高。$ perf top -g -p <pid_id>
Step3:通过 grep 查看该函数中的具体内容。$ grep -nr "<function_name>" <file_path>
性能问题:
(1)用户 CPU 和 Nice CPU 高,说明用户态进程占用了较多的 CPU,所以应该着重排查进程的性能问题。
(2)系统 CPU 高,说明内核态占用了较多的 CPU,所以应该着重排查内核线程或者系统调用的性能问题。
(3)I/O 等待 CPU 高,说明等待 I/O 的时间比较长,所以应该着重排查系统存储是不是出现了 I/O 问题。
(4)软中断和硬中断高,说明软中断或硬中断的处理程序占用了较多的CPU,所以应该着重排查内核中的中断服务程序。
系统的 CPU 使用率很高,但为啥却找不到高 CPU 的应用?
系统的 CPU 使用率,不仅包括进程用户态和内核态的运行,还包括中断处理、等待 I/O 以及内核线程等。所以,当你发现系统的 CPU 使用率很高的时候,不一定能找到相对应的高 CPU 使用率的进程。
如下系统总CPU使用率80.8%,而单个进程的CPU使用率都较小。需要通过 top 、pidstat 等交叉确认系统和各进程的CPU使用率。
并且仔细观察进程列表中的状态S,查看R、S等状态的进程是否正常。
$ top ... %Cpu(s): 80.8 us, 15.1 sy, 0.0 ni, 2.8 id, 0.0 wa, 0.0 hi, 1.3 si, 0.0 st ... PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 6882 root 20 0 8456 5052 3884 S 2.7 0.1 0:04.78 docker-containe 6947 systemd+ 20 0 33104 3716 2340 S 2.7 0.0 0:04.92 nginx 7494 daemon 20 0 336696 15012 7332 S 2.0 0.2 0:03.55 php-fpm
发生以上情况的原因:
(1)进程在不停地崩溃重启
,比如因为段错误、配置错误等等,这时,进程在退出后可能又被监控系统自动重启了,而启动过程的资源初始化,很可能会占用相当多的 CPU。
(2)这些进程都是短时进程
,也就是在其他应用内部通过 exec 调用的外面命令。这些命令一般都只运行很短的时间就会结束,你很难用 top 这种间隔时间比较长的工具发现。
发送上述问题的解决方法是找到父进程,从父进程入手,排查问题:
方法一:
(1)通过 top、pidstat 等找到可疑进程;
(2)通过 pstree 用树状形式显示该进程与其他进程的关系;
(3)通过 grep 找到具体调用代码
方法二:
(1)通过 perf record -g // 记录性能事件,等待大约 几秒后按 Ctrl+C 退出
(2)通过 perf report
方法三:
execsnoop 就是一个专为短时进程设计的工具,一般用于分析 Linux 内核的运行时行为。它通过 ftrace 实时监控进程的 exec() 行为,并输出短时进程的基本信息,包括进程 PID、父进程 PID、命令行参数以及执行的结果。
https://github.com/brendangregg/perf-tools/blob/master/execsnoop
系统中出现大量不可中断进程和僵尸进程怎么办?
进程状态
- R 是 Running 或 Runnable 的缩写,表示进程在 CPU 的就绪队列中,正在运行或者正在等待运行。
- D 是 Disk Sleep 的缩写,也就是不可中断状态睡眠(Uninterruptible Sleep),一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。
- Z 是 Zombie 的缩写,表示僵尸进程,也就是进程实际上已经结束了,但是父进程还没有回收它的资源(比如进程的描述符、PID 等)。
- S 是 Interruptible Sleep 的缩写,也就是可中断睡眠状态,表示进程因为等待某个事件而被系统挂起。当进程等待的事件发生时,它会被唤醒并进入 R 状态。
- I 是 Idle 的缩写,也就是空闲状态,用在不可中断睡眠的内核线程上。硬件交互导致的不可中断进程用 D 表示,但对某些内核线程来说,它们有可能实际上并没有任何负载,用 Idle 正是为了区分这种情况。要注意,D 状态的进程会导致平均负载升高, I 状态的进程却不会。
- T 或者 t,是 Stopped 或 Traced 的缩写,表示进程处于暂停或者跟踪状态。如(1)向一个进程发送 SIGSTOP 信号,它就会因响应这个信号变成暂停状态(Stopped);再向它发送 SIGCONT 信号,进程又会恢复运行(如果进程是终端里直接启动的,则需要你用 fg 命令,恢复到前台运行)。(2)调试器 gdb 调试一个进程时,在使用断点中断进程后,进程就会变成跟踪状态,这其实也是一种特殊的暂停状态,只不过你可以用调试器来跟踪并按需要控制进程的运行。
- X 是 Dead 的缩写,表示进程已经消亡,所以你不会在 top 或者 ps 命令中看到它。
不可中断状态,是为了保证进程数据与硬件状态一致,并且正常情况下,不可中断状态在很短时间内就会结束。所以,短时的不可中断状态进程,我们一般可以忽略。但如果系统或硬件发生了故障,进程可能会在不可中断状态保持很久,甚至导致系统中出现大量不可中断进程。这时系统可能出现了 I/O 等性能问题。
僵尸进程,这是多进程应用很容易碰到的问题。正常情况下,当一个进程创建了子进程后,它应该通过系统调用 wait() 或者 waitpid() 等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送 SIGCHLD 信号,所以,父进程还可以注册 SIGCHLD 信号的处理函数,异步回收资源。如果父进程没这么做,或是子进程执行太快,父进程还没来得及处理子进程状态,子进程就已经提前退出,那这时的子进程就会变成僵尸进程。
通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由 init 进程回收后也会消亡。但是一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会一直处于僵尸状态。大量的僵尸进程会用尽 PID 进程号,导致新进程不能创建。
$ ps aux | grep /app root 4009 0.0 0.0 4376 1008 pts/0 Ss+ 05:51 0:00 /app root 4287 0.6 0.4 37280 33660 pts/0 D+ 05:54 0:00 /app root 4288 0.6 0.4 37280 33668 pts/0 D+ 05:54 0:00 /app
s 表示这个进程是一个会话的领导进程,而 + 表示前台进程组。
进程组表示一组相互关联的进程,比如每个子进程都是父进程所在组的成员。在后台运行的命令,构成后台进程组;在前台运行的命令,构成前台进程组。
会话是指共享同一个控制终端的一个或多个进程组。
$ top top - 05:56:23 up 17 days, 16:45, 2 users, load average: 2.00, 1.68, 1.39 Tasks: 247 total, 1 running, 79 sleeping, 0 stopped, 115 zombie %Cpu0 : 0.0 us, 0.7 sy, 0.0 ni, 38.9 id, 60.5 wa, 0.0 hi, 0.0 si, 0.0 st %Cpu1 : 0.0 us, 0.7 sy, 0.0 ni, 4.7 id, 94.6 wa, 0.0 hi, 0.0 si, 0.0 st ... PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 4340 root 20 0 44676 4048 3432 R 0.3 0.0 0:00.05 top 4345 root 20 0 37280 33624 860 D 0.3 0.0 0:00.01 app 4344 root 20 0 37280 33624 860 D 0.3 0.4 0:00.01 app 1 root 20 0 160072 9416 6752 S 0.0 0.1 0:38.59 systemd // 第一行的平均负载( Load Average),平均负载正在升高, // 而 1 分钟内的平均负载已经达到系统的 CPU 个数,说明系统很可能已经有了性能瓶颈。 // 第二行,有 1 个正在运行的进程,但僵尸进程比较多,而且还在不停增加,说明有子进程在退出时没被清理。 // 第三、四行,用户 CPU 和系统 CPU 都不高,但 iowait 分别是 60.5% 和 94.6%,好像有点儿不正常。 // 最后,CPU 使用率都不高;但有两个进程处于 D 状态,它们可能在等待 I/O。
第一点,iowait 太高了,导致系统的平均负载升高,甚至达到了系统 CPU 的个数。
第二点,僵尸进程在不断增多,说明有程序没能正确清理子进程的资源。
iowait 分析
从系统整体了解 I/O 情况 -> 定位到具体进程 -> 定位到具体函数
(1)dstat 是一个新的性能工具,它吸收了 vmstat、iostat、ifstat 等几种工具的优点,可以同时观察系统的 CPU、磁盘 I/O、网络以及内存使用情况,本例需关注系统整体和 I/O 的问题。
$ dstat 1 10 // 输出间隔 1 秒,总共输出 10 组数据 You did not select any stats, using -cdngy by default. ----total-cpu-usage---- -dsk/total- -net/total- ---paging-- ---system-- usr sys idl wai hiq siq| read writ| recv send| in out | int csw 0 0 99 0 0 0|2224B 118k| 0 0 | 0 5B| 157 614 0 1 99 0 0 0| 0 0 |1914B 0 | 0 0 | 318 820 1 0 99 0 0 0| 0 196k| 208B 0 | 0 0 | 271 789 1 1 98 0 0 0| 0 0 | 0 0 | 0 0 | 292 772
(2)通过 top 找到 D 状态的进程。
(3)通过 pidstat 找到具体进程的 I/O 状态,-d 参数输出 I/O 使用情况,-p 参数指定具体进程号:pidstat -d -p 4344 1 3
(4)若上述 pidstat 找不到具体 I/O 原因,可以去掉 -p 参数,查看所有进程。
(5)通过 strace -p
僵尸进程分析
既然僵尸进程是因为父进程没有回收子进程的资源而出现的,那么,也就是找出父进程的问题。
(1)通过 pstree -aps
(2)查看父进程的代码,看看子进程结束的处理是否正确,比如有没有调用 wait() 或 waitpid() ,或是,有没有注册 SIGCHLD 信号的处理函数。