Linux性能优化实战(二)
一、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使用率过高怎么办?
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性能优化实战(二)的更多相关文章
- Linux性能优化实战(一)
一.优化方向 1,性能指标 从应用负载的视角出发,考虑"吞吐"和"延时" 从系统资源的视角出发,考虑资源使用率.饱和度等 2,性能优化步骤 选择指标评估应用程序 ...
- Linux性能优化实战学习笔记:第五十六讲
一.上节回顾 上一节,我带你一起梳理了,性能问题分析的一般步骤.先带你简单回顾一下. 我们可以从系统资源瓶颈和应用程序瓶颈,这两个角度来分析性能问题的根源. 从系统资源瓶颈的角度来说,USE 法是最为 ...
- Linux性能优化实战学习笔记:第三十二讲
一.上节总结 专栏更新至今,四大基础模块的第三个模块——文件系统和磁盘 I/O 篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,并且热情地留言与讨论. 今天是性能优化的第四期. ...
- Linux性能优化实战学习笔记:第五十二讲
一.上节回顾 上一节,我们一起学习了怎么使用动态追踪来观察应用程序和内核的行为.先简单来回顾一下.所谓动态追踪,就是在系统或者应用程序还在正常运行的时候,通过内核中提供的探针,来动态追踪它们的行为,从 ...
- Linux性能优化实战学习笔记:第十二讲
一.性能优化方法论 不可中断进程案例 二.怎么评估性能优化的效果? 1.评估思路 2.几个为什么 1.为什么要选择不同维度的指标? 应用程序和系统资源是相辅相成的关系 2.性能优化的最终目的和结果? ...
- Linux性能优化实战学习笔记:第四十五讲
一.上节回顾 专栏更新至今,四大基础模块的最后一个模块——网络篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,热情地留言和互动.还有不少同学分享了在实际生产环境中,碰到各种性能 ...
- Linux性能优化实战学习笔记:第四十六讲
一.上节回顾 不知不觉,我们已经学完了整个专栏的四大基础模块,即 CPU.内存.文件系统和磁盘 I/O.以及网络的性能分析和优化.相信你已经掌握了这些基础模块的基本分析.定位思路,并熟悉了相关的优化方 ...
- Linux性能优化实战学习笔记:第三十一讲
一.上节回顾 上一节,我们一起回顾了常见的文件系统和磁盘 I/O 性能指标,梳理了核心的 I/O 性能观测工具,最后还总结了快速分析 I/O 性能问题的思路. 虽然 I/O 的性能指标很多,相应的性能 ...
- Linux性能优化实战学习笔记:第三十六讲
一.上节总结回顾 上一节,我们回顾了经典的 C10K 和 C1000K 问题.简单回顾一下,C10K 是指如何单机同时处理 1 万个请求(并发连接 1 万)的问题,而 C1000K 则是单机支持处理 ...
- Linux性能优化实战学习笔记:第三十八讲
一.上节回顾 上一节,我们学习了 DNS 性能问题的分析和优化方法.简单回顾一下,DNS 可以提供域名和 IP 地址的映射关系,也是一种常用的全局负载均衡(GSLB)实现方法. 通常,需要暴露到公网的 ...
随机推荐
- Pandas系列(十七)-EDA(pandas-profiling)
对于探索性数据分析来说,做数据分析前需要先看一下数据的总体概况,pandas_profiling工具可以快速预览数据. 安装 pip install pandas-profiling 使用 impor ...
- HDU 2084 数塔 (动态规划DP)
原题链接:http://acm.hdu.edu.cn/showproblem.php?pid=2084 题目分析:此题采用动态规划自底向上计算,如果我们要知道所走之和最大,那么最后一步肯定是走最后一排 ...
- kafka时间轮的原理(一)
概述 早就想写关于kafka时间轮的随笔了,奈何时间不够,技术感觉理解不到位,现在把我之前学习到的进行整理一下,以便于以后并不会忘却.kafka时间轮是一个时间延时调度的工具,学习它可以掌握更加灵活先 ...
- 为什么JavaWeb要分层
首先bai让我们坐着时光机回到n年前的web开发.那个时候最早du都是静态的html页面,zhi后来有了数据库,有了所谓dao的动态页面,然后程序猿在编码的时候,会把所有的代码都写在页面上,包括数据库 ...
- window10教育版激活失败
问题 输入完key之后显示无法连接服务器 再次输入密钥无效,而且家庭版密钥激活也没了 使用命令行消除过去的key,使用新的教育版key后,显示运行在运行microsoft windows 非核心版本的 ...
- manjaro20WPS缺少字体
宋体等 sudo pacman -S ttf-wps-fonts Ariel和Times New Roman https://askubuntu.com/questions/651441/how-to ...
- CMake语法—缓存变量(Cache Variable)
目录 CMake语法-缓存变量(Cache Variable) 1 CMake缓存变量 2 定义缓存变量 2.1 定义格式 2.2 定义示例代码 2.3 运行结果 2.4 小结 3 CMakeCach ...
- Solon 1.6.18 发布,轻量级应用开发框架
关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...
- gin中设置和获取cookie
package main import ( "fmt" "github.com/gin-gonic/gin" ) func main() { router := ...
- Filter-完整的用户登录和权限检查
Filter过滤器的使用步骤: 1,编写一个类去实现Filter接口 2,实现拦截(过滤)方法doFilter() 3,到web.xml中配置Filter的拦截路径 补充login.jsp登录页面 编 ...