一、CPU使用率过高

1,CPU使用率

a>节拍率

  为了维护CPU时间,Linux通过事先定义的节拍率(内核中表示为HZ),触发时间中断,并使用全局变量Jiffies记录开机以来的节拍数。每发生一次时间中断,Jiffies的值就加1

  节拍率HZ是内核的可配置选项

#查看当前系统的节拍率为每秒钟250次时间中断
grep 'CONFIG_HZ=' /boot/config-$(uname -r)
CONFIG_HZ=250

  同时内核还提供了一个用户空间节拍率USER_HZ,固定值为100,也就是1/100秒

b>/proc虚拟文件系统

cpu 2032004 102648 238344 167130733 758440 15159 17878 0
cpu0 1022597 63462 141826 83528451 366530 9362 15386 0
cpu1 1009407 39185 96518 83602282 391909 5796 2492 0
intr 303194010 212852371 3 0 0 11 0 0 2 1 1 0 0 3 0 11097365 0 72615114 6628960 0 179 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 236095529
btime 1195210746
processes 401389
procs_running 1
procs_blocked 0

  第一行的数值表示的是CPU总的使用情况,所以我们只要用第一行的数字计算就可以了。下表解析第一行各数值的含义:

参数                    解析(单位:jiffies)
user(2032004) 从系统启动开始累计到当前时刻,用户态的CPU时间,不包含 nice值为负进程。
nice(102648)   从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间
system (238344) 从系统启动开始累计到当前时刻,核心时间
idle (167130733) 从系统启动开始累计到当前时刻,除IO等待时间以外其它等待时间
iowait (758440) 从系统启动开始累计到当前时刻,IO等待时间
irq (15159) 从系统启动开始累计到当前时刻,硬中断时间
softirq (17878) 从系统启动开始累计到当前时刻,软中断时间

c>CPU使用率

  CPU在t1到t2时间段即时利用率 =  1 - CPU空闲使用时间 / CPU总的使用时间

  CPU在t1到t2时间段空闲使用时间 = (idle2 - idle1)

  CPU在t1到t2时间段总的使用时间 = ( user2+ nice2+ system2+ idle2+ iowait2+ irq2+ softirq2) - ( user1+ nice1+ system1+ idle1+ iowait1+ irq1+ softirq1)

2,如何查看CPU使用率

a>top

  top默认使用3秒时间间隔,它显示了系统总体的CPU和内存使用情况,以及各个进程的资源使用情况

top - 09:52:06 up 2 days, 53 min,  1 user,  load average: 0.38, 0.69, 0.88
任务: 445 total, 1 running, 353 sleeping, 0 stopped, 4 zombie
%Cpu(s): 2.3 us, 1.4 sy, 0.0 ni, 95.8 id, 0.0 wa, 0.0 hi, 0.5 si, 0.0 st
KiB Mem : 16163124 total, 588480 free, 11530672 used, 4043972 buff/cache
KiB Swap: 2097148 total, 1243900 free, 853248 used. 2415292 avail Mem
PID USER      PR  NI    VIRT    RES    SHR �  %CPU %MEM     TIME+ COMMAND
11775 mi 20 0 4872384 499200 134812 S 11.5 3.1 42:41.12 gnome-shell
11318 mi 20 0 1355336 261508 226128 S 7.6 1.6 53:12.32 Xorg
21215 mi 20 0 3407932 604084 27340 S 6.6 3.7 135:50.48 WeChat.exe
21220 mi 20 0 10068 6080 684 S 6.2 0.0 120:01.52 wineserver.real
9586 mi 20 0 693000 36112 21156 S 4.6 0.2 0:19.49 gnome-terminal-
5907 mi 20 0 13.246g 3.962g 29032 S 3.9 25.7 632:20.02 java

  第三行%Cpu就是系统的CPU使用率,具体每列的含义与/proc类似

b>ps

   ps (英文全拼:process status)命令用于显示当前进程的状态,进程的整个生命周期

USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root 1 0.0 0.0 225968 8288 ? Ss 2月07 1:57 /lib/systemd/systemd --system --deserialize 19
root 2 0.0 0.0 0 0 ? S 2月07 0:00 [kthreadd]

  具体含义

USER: 行程拥有者
PID: pid
%CPU: 占用的 CPU 使用率
%MEM: 占用的记忆体使用率
VSZ: 占用的虚拟记忆体大小
RSS: 占用的记忆体大小
TTY: 终端的次要装置号码 (minor device number of tty)
STAT: 该行程的状态:D: 无法中断的休眠状态 (通常 IO 的进程); R: 正在执行中; S: 静止状态; T: 暂停执行; Z: 不存在但暂时无法消除; W: 没有足够的记忆体分页可分配; <: 高优先序的行程; N: 低优先序的行程; L: 有记忆体分页分配并锁在记忆体内 (实时系统或捱A I/O)
START: 行程开始时间
TIME: 执行的时间
COMMAND:所执行的指令

c>pidstat

pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]

  可以指定时间间隔来输出CPU使用率

#每隔 1 秒输出一组数据,共输出 5 组
mi@mi:~$ pidstat 1 5
10时08分51秒 UID PID %usr %system %guest %wait %CPU CPU Command
10时08分52秒 1000 641 0.99 0.00 0.00 0.00 0.99 9 chrome
10时08分52秒 1000 996 0.00 0.99 0.00 0.00 0.99 4 chrome
10时08分52秒 1000 4387 0.99 0.99 0.00 0.00 1.98 0 pidstat
10时08分52秒 1000 5907 3.96 0.00 0.00 0.00 3.96 2 java
...
平均时间: UID PID %usr %system %guest %wait %CPU CPU Command
平均时间: 0 11 0.00 0.20 0.00 0.00 0.20 - rcu_sched
平均时间: 1000 548 0.20 0.20 0.00 0.00 0.40 - chrome
平均时间: 0 1745 0.00 0.20 0.00 0.00 0.20 - kworker/u24:4-events_unbound
平均时间: 0 2568 0.00 0.20 0.00 0.00 0.20 - kworker/u24:2-i915
平均时间: 0 3545 0.00 0.20 0.00 0.00 0.20 - kworker/9:2-events
  • %usr 用户态CPU使用率

  • %system 内核态CPU使用率

  • %guest 运行虚拟机CPU使用率

  • %wait 等待CPU使用率

  • %CPU 总的CPU使用率

  • 最后的Average,则为计算了5组数据的平均值

3,CPU使用率过高怎么办?

  perf 火焰图

a>采用top、ps、pidstat等工具,找到CPU使用率较高的进程

b>使用perf top 展示热点函数

  

  输出结果中,第一行包含三组数据,分别是采样数(Samples)、事件类型(event)和事件总数量(Event count)。比如当前perf采集了833个CPU时钟事件,总事件数为97742399。

  采样数需要尽可能的多,这样才有参考价值。解析表格样式的数据为:

  • Overhead,是该符号的性能事件在所有采样中的比例,用百分比来表示
  • Shared,是该函数或指令所在的动态共享对象(Dynamic Shared Object),如内核、进程名、动态链接库名、内核模块名等。
  • Object,是动态共享对象的类型。比如 [.] 表示用户空间的可执行程序、或者动态链接库,而  [k] 则表示内核空间
  • Symbol,函数名。当函数名未知时,用十六进制的地址来表示

  从上图可以看出CPU时钟最多的是perf工具自身,其比例也只有7.28%,说明系统并没有CPU性能问题。

c>perf record 和 perf report

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

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

//按 ctrl + c 终止采样
root@xc:~# perf record
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 3.679 MB perf.data (26683 samples) ]
//展示类似于 perf top的报告
root@xc:~# perf report

  在实际使用中,我们经常使用perf top 和 perf record 加上 -g 参数,开启调用关系的采样,方便调用链的分析

4,案例一

  使用两台虚拟机,其中一台用作Web服务器,来模拟性能问题;另一台用作Web服务器的客户端,来给Web服务器增加压力请求。使用两台虚拟机是为了相互隔离,避免“交叉感染”。

  

VM1上启动应用

  

VM2上测试并压测

  

  启动成功,进行性能压测

  

  从ab输出结果可以看到,Nginx能承受的每秒平均请求数只有11.63。为了方便分析,我们将请求数增到10000,继续压测

  

VM1上分析原因

  top,可以看到php-fpm的CPU使用率加起来将近200%;每个CPU的使用率均已超过98%

  

  perf top ,使用-g 开启调用关系分析, -p 指定 php-fpm的进程号 21515

  

  发现调用关系最终到sqrt和add_function出现占用的CPU时钟较高。

  

5,案例二(定位CPU升高的应用)

  使用两台虚拟机,其中一台用作Web服务器,来模拟性能问题;另一台用作Web服务器的客户端

   

VM1上启动应用

  

VM2上测试并压测:

  

  启动成功,进行压测

  

  从ab结果可以看到,Nginx能承受的每秒平均请求数,只有87左右。接下来,将并发请求数改成5,延长请求时长为10分钟,方便vm1定位分析问题

  

VM1上分析原因

  top分析,发现CPU使用最高的进程才2.7%,并不特别高。然而在CPU使用率(%Cpu),可以看到用户CPU使用率为80%,系统CPU为15.1%,而空闲CPU为2.8%

   

  pidstat,观察发现CPU的使用率也都不高

  

  

  再次采用top观察,发现Tasks中有个6个Running状态的进程,而观察咱们启动的php-fpm确都处于Sleep(S)状态,真正处于Running状态的时stress进程

  

  采用pidstat进一步观察其中的一个stress进程,发现无输出

  

  采用ps查看这个stress进程,任然无输出。

  

  再次采用top命令查看,发现stress进程的进程号都已经发生变化

  

  分析原因

  • 进程在不断地崩溃重启,比如因为配置错误等,进程在退出后可能又被监控系统自动重启
  • 进程为短时进程,也就是在其他应用内部通过exec调用的外部命令。这些命令一般都只运行很短的时间就会结束,也很难通过top这种间隔时间较长的工具发现

  通过pstree查找真实的stress进程的父进程,这里可以看到stress是被php-fpm调用的子进程,并且进程数量不止一个(这里是3个)

  

  采用perf record -g命令,等待一会儿之后按Ctrl+C退出,再用perf report查看报告

  

  

  发现 stress占用了CPU时钟事件的77%,而stress调用栈中比例较高的为函数random(),再进行具体的优化,就可以解决。

6,execsnoop

  execsnoop-专门用于为追踪短时进程(瞬时进程)设计的工具;它通过 ftrace 实时监控进程的 exec() 行为,并输出短时进程的基本信息,包括进程 PID、父进程 PID、命令行参数以及执行的结果。

  github地址: https://github.com/brendangregg/perf-tools/blob/master/execsnoop

  如何安装使用:将上面的github的内容复制,然后写入execsnoop文件,并且加上x权限即可;

  

  可以发现大量的stress进程在不停启动

二、僵尸进程

1,进程的状态

  以top为例,在top中有一列为S列(也就是Status列),可以查看到有T、D、Z、S、I等几个状态

  • 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的缩写,表示进程处于暂停或跟踪状态。
  • X是Dead的缩写,表示进程已经消亡,不能在top或ps中看到

  当一个进程创建了子进程后,它应该通过系统调用wait()或waitpid()等待子进程结束,回收子进程的资源;而子进程在结束时,会向它的父进程发送SIGCHLD信号,所以父进程也可以注册SIGCHLD信号的处理函数,异步回收资源。

  如果父进程没这么做,或子进程执行太快,父进程还没有来得及处理子进程状态,子进程就已经提前退出,那么这时的子进程就会变成僵尸进程。通常,僵尸进程持续的时间都比较短,在父进程回收它的资源后就会消亡;或者在父进程退出后,由init进程回收后也会消亡。

  一旦父进程没有处理子进程的终止,还一直保持运行状态,那么子进程就会处于僵尸状态。大量的僵尸进程会用尽PID进程号,导致新进程不能创建,所以这种情况需要避免。

2,案例分析

  准备Ubuntu 18.04 机器配置 2CPU,8GB内存,预先安装docker、sysstat、dstat等工具

启动应用

  

ps查看应用为正常启动

  

  Ss+和D+。其中S表示可中断睡眠,D表示不可中断睡眠。s表示这个进程是一个会话的领导进程,+表示前台进程组

top查看

  

  • 第一行看平均负载(load average),过去1分钟、5分钟和15分钟的平均负载在依次缩小,说明平均负载在升高。1分钟内的平均负载为2,说明有可能已经达到性能瓶颈
  • 第二行看Tasks,有1个正在运行的进程,但是发现zombie僵尸进程比较多,而且在不停的增加,说明有子进程在退出时未被清理
  • 查看CPU的使用情况,发现用户CPU和系统CPU都不高,但是iowait分别时60.5%和94.6%

dstat分析iowait

  

  可以看到当iowait(wai)升高时,磁盘的读请求(read)都会很大。这说明iowait的升高跟磁盘的读请求有关,很可能就是磁盘读导致的。接下来需要进一步分析具体的哪个进程读磁盘呢?

pidstat采用-d分析I/O和进程之间的情况

  

  可以发现app进程每秒读的数据为32MB,基本可以断定是app应用导致。接下来继续追踪app进程在执行什么I/O操作?

strace追踪进程6082

  

ps查看6082进程情况,发现6082已经是僵尸进程。

  

目前iowait还在继续,但是top、pidstat这类工具已经不能给予帮助了,接下来采用perf查看

  

  

  其中swapper是内核中的调度进程,可以先忽略。可以看到在app中通过系统调用sys_read()读取数据。并且从new_sync_read和blkdev_read_iter能看出,进程正在对磁盘进行直接读,也就是绕过了系统缓存,每个读请求都会直接从磁盘读取,导致iowait升高。 定位到原因后就可以打开源码发现在app.c的文件出现了O_DIRECT选项直接打开磁盘,删除这个选项

  

重启后,再使用top查看,发现iowait已经很低了只有0.3%。但是查看僵尸进程的数量还是在不断的增加

  

采用pstree定位父进程,发现还是app应用导致的僵尸进程。进一步查看并修复。

  

Linux性能优化实战(二)的更多相关文章

  1. Linux性能优化实战(一)

    一.优化方向 1,性能指标 从应用负载的视角出发,考虑"吞吐"和"延时" 从系统资源的视角出发,考虑资源使用率.饱和度等 2,性能优化步骤 选择指标评估应用程序 ...

  2. Linux性能优化实战学习笔记:第五十六讲

    一.上节回顾 上一节,我带你一起梳理了,性能问题分析的一般步骤.先带你简单回顾一下. 我们可以从系统资源瓶颈和应用程序瓶颈,这两个角度来分析性能问题的根源. 从系统资源瓶颈的角度来说,USE 法是最为 ...

  3. Linux性能优化实战学习笔记:第三十二讲

    一.上节总结 专栏更新至今,四大基础模块的第三个模块——文件系统和磁盘 I/O 篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,并且热情地留言与讨论. 今天是性能优化的第四期. ...

  4. Linux性能优化实战学习笔记:第五十二讲

    一.上节回顾 上一节,我们一起学习了怎么使用动态追踪来观察应用程序和内核的行为.先简单来回顾一下.所谓动态追踪,就是在系统或者应用程序还在正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从 ...

  5. Linux性能优化实战学习笔记:第十二讲

    一.性能优化方法论 不可中断进程案例 二.怎么评估性能优化的效果? 1.评估思路 2.几个为什么 1.为什么要选择不同维度的指标? 应用程序和系统资源是相辅相成的关系 2.性能优化的最终目的和结果? ...

  6. Linux性能优化实战学习笔记:第四十五讲

    一.上节回顾 专栏更新至今,四大基础模块的最后一个模块——网络篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,热情地留言和互动.还有不少同学分享了在实际生产环境中,碰到各种性能 ...

  7. Linux性能优化实战学习笔记:第四十六讲

    一.上节回顾 不知不觉,我们已经学完了整个专栏的四大基础模块,即 CPU.内存.文件系统和磁盘 I/O.以及网络的性能分析和优化.相信你已经掌握了这些基础模块的基本分析.定位思路,并熟悉了相关的优化方 ...

  8. Linux性能优化实战学习笔记:第三十一讲

    一.上节回顾 上一节,我们一起回顾了常见的文件系统和磁盘 I/O 性能指标,梳理了核心的 I/O 性能观测工具,最后还总结了快速分析 I/O 性能问题的思路. 虽然 I/O 的性能指标很多,相应的性能 ...

  9. Linux性能优化实战学习笔记:第三十六讲

    一.上节总结回顾 上一节,我们回顾了经典的 C10K 和 C1000K 问题.简单回顾一下,C10K 是指如何单机同时处理 1 万个请求(并发连接 1 万)的问题,而 C1000K 则是单机支持处理 ...

  10. Linux性能优化实战学习笔记:第三十八讲

    一.上节回顾 上一节,我们学习了 DNS 性能问题的分析和优化方法.简单回顾一下,DNS 可以提供域名和 IP 地址的映射关系,也是一种常用的全局负载均衡(GSLB)实现方法. 通常,需要暴露到公网的 ...

随机推荐

  1. Pandas系列(十七)-EDA(pandas-profiling)

    对于探索性数据分析来说,做数据分析前需要先看一下数据的总体概况,pandas_profiling工具可以快速预览数据. 安装 pip install pandas-profiling 使用 impor ...

  2. HDU 2084 数塔 (动态规划DP)

    原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2084 题目分析:此题采用动态规划自底向上计算,如果我们要知道所走之和最大,那么最后一步肯定是走最后一排 ...

  3. kafka时间轮的原理(一)

    概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...

  4. 为什么JavaWeb要分层

    首先bai让我们坐着时光机回到n年前的web开发.那个时候最早du都是静态的html页面,zhi后来有了数据库,有了所谓dao的动态页面,然后程序猿在编码的时候,会把所有的代码都写在页面上,包括数据库 ...

  5. window10教育版激活失败

    问题 输入完key之后显示无法连接服务器 再次输入密钥无效,而且家庭版密钥激活也没了 使用命令行消除过去的key,使用新的教育版key后,显示运行在运行microsoft windows 非核心版本的 ...

  6. manjaro20WPS缺少字体

    宋体等 sudo pacman -S ttf-wps-fonts Ariel和Times New Roman https://askubuntu.com/questions/651441/how-to ...

  7. CMake语法—缓存变量(Cache Variable)

    目录 CMake语法-缓存变量(Cache Variable) 1 CMake缓存变量 2 定义缓存变量 2.1 定义格式 2.2 定义示例代码 2.3 运行结果 2.4 小结 3 CMakeCach ...

  8. Solon 1.6.18 发布,轻量级应用开发框架

    关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...

  9. gin中设置和获取cookie

    package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { router := ...

  10. Filter-完整的用户登录和权限检查

    Filter过滤器的使用步骤: 1,编写一个类去实现Filter接口 2,实现拦截(过滤)方法doFilter() 3,到web.xml中配置Filter的拦截路径 补充login.jsp登录页面 编 ...