分享自己做的一个指定进程以及线程长时间cpu监控的工具
前言:
前面给大家分享过一个工作中用到的编译拷贝脚本,其实工作中还有一些其他工具的使用,今天再来分享一个自己纯手工的CPU监控的脚本。大家可以结合上篇文章与本篇文章一起学习shell。
主要实现功能:
- 1.监控指定进程是否运行
- 2.读取该进程所在当前CPU的占用率,5s一次的执行频率计算当前进程 5分钟 10分钟 15分钟的平均cpu占用率
- 3.计算该进程下用PID排序的前十个线程的 5分钟 10分钟 15分钟的平均cpu占用率
作者:良知犹存
转载授权以及围观:欢迎关注微信公众号:羽林君
或者添加作者个人微信:become_me
情节介绍:
在工作中,我们会对调试的进程以及线程进行性能分析并进行调优,通常我们使用linux下很多的工具包例如,perf 性能分析工具,以及剖析工具 GNU profiler(gprof 可以为 Linux平台上的程序精确分析性能瓶颈。gprof精确地给出函数被调用的时间和次数,给出函数调用关系)。
当然现在运维以及自动驾驶里面工作对性能分析工具熟悉也要很多要求,举例展示一个自动驾驶相关的系统工程师需要掌握的一些性能分析工具,包含speccpu、fio、iperf、stream、mlc、lmbench、erf、emon/Vtune等工具及相关调优手段,以后有时间再给大家一一介绍这些使用的性能分析工具。
今天呢,没有过多描述这些工具,因为我遇到的情况是没有这些工具,所以为了实现一个进程监控,我自己写了一个脚本,今天主要给大家分享,如果你工作中,需要一个性能监控的要求,但是你使用的环境中没有这些工具,此外你的环境支持shell脚本,那么这篇文章应该对你有所帮助。
好了,言归正传,接下来我给大家分享我写这些脚本使用的技术,以及最终实现的情况。
下图是脚本执行的流程图:
脚本内容:
#!/bin/bash
#一共11个数据 第0个是总的cpu计算 第1-10是线程前十个的排序
# 0 1 2 3 4 5 6 7 8 9 a b
cpu_sum_info=(0 0 0 0 0 0 0 0 0 0 0 0)
cpu_5mi_info=(0 0 0 0 0 0 0 0 0 0 0 0)
cpu_10m_info=(0 0 0 0 0 0 0 0 0 0 0 0)
cpu_15m_info=(0 0 0 0 0 0 0 0 0 0 0 0)
cnt=0
function GetPID #User #Name
{
PsUser=$1
PsName=$2
PID=`ps -u $PsUser|grep $PsName|grep -v grep|grep -v vi|grep -v dbx\n|grep -v tail|grep -v start|grep -v stop |sed -n 1p |awk '{print $1}'`
#echo $PID
return $PID
}
function GetCpu
{
# CpuValue=`ps -p $1 -o pcpu |grep -v CPU | awk '{print $1}' | awk -F. '{print $1}'`
CpuValue=`top -p $1 -bn 1 | awk 'NR == 8 {print $9}'| awk -F. '{print $1}'`
# echo "cpu all use "$CpuValue "%"
return $CpuValue
}
function SumCpuAverage
{
sum_value=$1
cnt=$2
# echo " "$sum_value $cnt
((aver=sum_value/cnt))
# echo "aver="${aver}
return $aver
}
function float() {
bc << EOF
num = $1;
base = num / 1;
if (((num - base) * 10) > 1 )
base += 1;
print base;
EOF
echo ""
return $base
}
while true
do
date
GetPID root exe #修改对应的用户和进程名
echo $?
if [ -n "$PID" -a -e /proc/$PID ]; then
echo "process exists"
else
exit 0
fi
if ps -p $PID > /dev/null
then
echo "$PID is running"
# Do something knowing the pid exists, i.e. the process with $PID is running
else
exit 0
fi
GetCpu $PID
single_value=$?
echo -e "\033[0;42m cpu used process=" $single_value "% \033[0m"
((cpu_sum_info[0]=cpu_sum_info[0]+single_value))
echo "sum 0 = " ${cpu_sum_info[0]}
echo "all cpu used"
# ps -Tp $PID -o pcpu,pid,lwp | awk 'NR>2{print line}{line=$0} END{print line}' | sort -rn -k1 | head -n 10
top -Hp $PID -bn 1 | awk 'NR>8{print line}{line=$0} END{print line}' | sort -rn -k1
# index=1
for loop in 1 2 3 4 5 6 7 8 9 10
do
var="NR == $((${loop}+8)) {print \$9}"
# echo "loop" $loop $var
# single_value=$(ps -Tp $PID -o pcpu,pid,lwp| awk 'NR>2{print line}{line=$0} END{print line}' | sort -rn -k1 | head -n 10 | awk "$var" | awk -F. '{print $1}')
single_value=$(top -Hp $PID -bn 1 | awk 'NR>8{print line}{line=$0} END{print line}' | sort -rn -k1 | awk "$var" | awk -F. '{print $1}')
# single_value=$(ps -Tp $PID -o pcpu,pid,lwp| awk 'NR>2{print line}{line=$0} END{print line}' | sort -rn -k1 | head -n 10 | awk "$var")
# float $single_value
# single_value=$?
# cpu_sum_info[$loop] = `expr ${cpu_sum_info[$loop]} + ${single_value}`
# ((cpu_sum_info[$loop]+=single_value))
cpu_sum_info[$loop]=$((cpu_sum_info[$loop]+single_value))
echo "sum" $loop "=" ${cpu_sum_info[$loop]}
# let index+=1
done
((cnt+=1))
echo "cnt = "$cnt
if [ $((cnt%60)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_5mi_info[$loop]=$?
echo -e "\033[0;41m cpu_5mi_info" $loop "="${cpu_5mi_info[$loop]} "\033[0m"
done
echo -e " "
fi
if [ $((cnt%120)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_10m_info[$loop]=$?
echo -e "\033[0;42m cpu_10m_info="${cpu_10m_info[$loop]} "\033[0m"
done
echo -e " "
fi
if [ $((cnt%180)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_15m_info[$loop]=$?
echo -e "\033[0;43m cpu_15m_info="${cpu_15m_info[$loop]} "\033[0m"
done
echo -e " "
fi
sleep 5
done
以上是monitor.sh 的内容,主要分为几块:
通过进程名称查询对应该进程的PID:
function GetPID #User #Name
{
PsUser=$1
PsName=$2
PID=`ps -u $PsUser|grep $PsName|grep -v grep|grep -v vi|grep -v dbx\n|grep -v tail|grep -v start|grep -v stop |sed -n 1p |awk '{print $1}'`
#echo $PID
return $PID
}
GetPID root exe #修改对应的用户和进程名
这个部分是的核心是一条组合命令,
- ps -u $PsUser
进行指定用户的进程查询
- grep $PsName
进行指定名称进程搜索
- grep -v grep 等
去除掉grep等其他的搜索命令的影响
- sed -n 1p
1p 打印第一行,p 功能为打印
-n 表示静默模式,一般sed都有把所有读到的行打印出来,如果不加这个参数,它将一行行打印读到的,并且由于 1p 会重复打印第一行;
- awk '{print $1}'
把第一列的参数打印出来
查询指定进程是否存在:
if [ -n "$PID" -a -e /proc/$PID ]; then
echo "process exists"
else
exit 0
fi
if ps -p $PID > /dev/null
then
echo "$PID is running"
# Do something knowing the pid exists, i.e. the process with $PID is running
else
exit 0
fi
这里面使用了proc里面查询以及ps命令进行查询 第一步得到的PID是否存在。
其中 > /dev/null
表示一个黑洞位置,代表linux的空设备文件,所有往这个文件里面写入的内容都会丢失,表示不打印输出数据。
获得存在的进程的总cpu占用率:
function GetCpu
{
CpuValue=`top -p $1 -bn 1 | awk 'NR == 8 {print $9}'| awk -F. '{print $1}'`
return $CpuValue
}
GetCpu $PID
single_value=$?
echo -e "\033[0;42m cpu used process=" $single_value "% \033[0m"
重要命令介绍:
top -p $1 -bn 1
使用top命令进行 查询指定的PID CPU 占用率信息,并且只执行一次之后退出(top命令默认是交互模式,无法进行退出,所以此处设置执行之后退出)。awk 'NR == 8 {print $9}'
这条命令是输出数据的第8行 第9列,至于为什么要这么输出。这个需要大家去手工去看你需要PID CPU数据的具体行列。这里我的第8行是指定PID下的CPU详细数据的一行,所以我输出了第8行,记住中间的空行也算一行。
这里我执行了,第9列的输出,同时也测试了10列的输出,大家配合输出数据的不同,也可以得出自己需要的对应那一列。
- awk -F. '{print $1}'
这个也很熟悉,-F指定分割符号,这里我选择了.
,去除这个符号的原因是shell脚本执行浮点计算比较麻烦,需要另写函数转换,上面大家应该看到我脚本里面写了这个转换函数,但是我的设备里面没有支持里面的一下操作特性,所以最终无法实现,只是在PC端实现,在我实际设备中,我计算的CPU占用率是去掉小数点的数据,损失了计算精度。
获得存在的进程的各个线程cpu占用率:
for loop in 1 2 3 4 5 6 7 8 9 10
do
var="NR == $((${loop}+8)) {print \$9}"
single_value=$(top -Hp $PID -bn 1 | awk 'NR>8{print line}{line=$0} END{print line}' | sort -rn -k1 | awk "$var" | awk -F. '{print $1}')
cpu_sum_info[$loop]=$((cpu_sum_info[$loop]+single_value))
echo "sum" $loop "=" ${cpu_sum_info[$loop]}
done
重要命令介绍:
- top -Hp $PID -bn 1
指定进程下面所有的线程打印输出一次并退出top命令。
- awk 'NR>8{print line}{line=$0} END{print line}'
配合这张图大家应该会容易看一些,这个部分是使用 awk命令,把top命令头信息去除掉,从第8行开始打印剩余数据。
- sort -rn -k1
这个部分是进行排序,其中-r 以相反的顺序来排序,-n 依照数值的大小排序,-k1是使用第一排数据进行排列。
var="NR == $((${loop}+8)) {print \$9}"
awk "$var"
这个部分因为有特殊字符,所以就用黑色代码进行引用。这个部分就是使用awk命令,打印输出排好序的前10行每一行的线程cpu数据。
- awk -F. '{print $1}'
这个部分参考上一处介绍。
进行变量操作,并进行5分钟、10分钟、15分钟平均值计算
function SumCpuAverage
{
sum_value=$1
cnt=$2
((aver=sum_value/cnt))
return $aver
}
((cnt+=1))
echo "cnt = "$cnt
if [ $((cnt%60)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_5mi_info[$loop]=$?
echo -e "\033[0;41m cpu_5mi_info" $loop "="${cpu_5mi_info[$loop]} "\033[0m"
done
echo -e " "
fi
if [ $((cnt%120)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_10m_info[$loop]=$?
echo -e "\033[0;42m cpu_10m_info="${cpu_10m_info[$loop]} "\033[0m"
done
echo -e " "
fi
if [ $((cnt%180)) -eq 0 ]
then
for loop in 0 1 2 3 4 5 6 7 8 9 10
do
SumCpuAverage ${cpu_sum_info[$loop]} ${cnt}
cpu_15m_info[$loop]=$?
echo -e "\033[0;43m cpu_15m_info="${cpu_15m_info[$loop]} "\033[0m"
done
echo -e " "
fi
sleep 5
这里主要就是进行存储变量以及变量进行时间上的平均计算,我选择了5分钟、10分钟、15分钟的计算。大家也可以选择自己合适的时间进行分配计算。其中有个sleep 5秒的操作,因为top命令本身也会占用cpu资源,如果过快查询,也会导致CPU占用率很高,所以这个部分休眠时间,大家可以按照自己CPU实际性能进行添加。
最终效果
这是在该进程存在的情况下执行:
这是最开始打印的信息
这是五分钟 十分钟 以及 十五分钟后计算打印的数据
如果输入的进程查询之后没有,则脚本直接退出:
结语
这就是我在工作中使用shell自己动手做的一个进程以及该进程包含的线程cpu占用率的监控,适合在大家使用的设备里面没有对应的性能分析工具的应用,大家也可以拿过去直接使用。如果大家有更好的想法和需求,也欢迎大家加我好友交流分享哈。
作者:良知犹存,白天努力工作,晚上原创公号号主。公众号内容除了技术还有些人生感悟,一个认真输出内容的职场老司机,也是一个技术之外丰富生活的人,摄影、音乐 and 篮球。关注我,与我一起同行。
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
推荐阅读
【3】CPU中的程序是怎么运行起来的 必读
本公众号全部原创干货已整理成一个目录,回复[ 资源 ]即可获得。
分享自己做的一个指定进程以及线程长时间cpu监控的工具的更多相关文章
- Linux进程或线程绑定到CPU
Linux进程或线程绑定到CPU 为了让程序拥有更好的性能,有时候需要将进程或线程绑定到特定的CPU,这样可以减少调度的开销和保护关键进程或线程. 进程绑定到CPU Linux提供一个接口,可以将进程 ...
- 分享学做的一个jsp注册页面
分享一个自己学习时,用bootstrap,多方搜索做的注册页面,包括页面的非空验证.导入相关的bootstrap的js和css文件就可以了.背景很丑,可以自己换一个.后面进一步完善<( ̄︶ ̄)↗ ...
- 【进程/作业管理】篇章一:Linux进程及管理(专用内存监控类工具)------【vmstat、pmap】
主要讲解专用内存监控工具的使用:vmstat.pmap命令的使用. 命令概览: vmstat 显示虚拟内存状态 pmap 报告进程与内存映射关系 vmstat命令是最常见的Linux/Unix监控工具 ...
- logback 指定每隔一段时间创建一个日志文件
我使用的logback版本是1.2.3 目前logback支持根据时间来配置产生日志文件,但是只支持每周,每天,每个小时,每分钟等创建一个文件,配置如下: <appender name=&quo ...
- python之进程与线程
什么是操作系统 可能很多人都会说,我们平时装的windows7 windows10都是操作系统,没错,他们都是操作系统.还有没有其他的? 想想我们使用的手机,Google公司的Androi ...
- Windbg 进程与线程 《第三篇》
Windbg既可以显示进程和线程列表,又可以显示指定进程或线程的详细信息.调试命令可以提供比taskmgr更详尽的进程资料,在调试过程中不可或缺. 一.进程命令 进程命令包括这些内容:显示进程列表.进 ...
- 线程概念( 线程的特点,进程与线程的关系, 线程和python理论知识,线程的创建)
参考博客: https://www.cnblogs.com/xiao987334176/p/9041318.html 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运 ...
- python 全栈开发,Day41(线程概念,线程的特点,进程和线程的关系,线程和python 理论知识,线程的创建)
昨日内容回顾 队列 队列 : 先进先出.数据进程安全 队列实现方式: 管道 + 锁 生产者消费者模型 : 解决数据供需不平衡 管道 双向通信 数据进程不安全 EOFError: 管道是由操作系统进行引 ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
随机推荐
- Codeforces 983E - NN country(贪心+倍增优化)
Codeforces 题面传送门 & 洛谷题面传送门 一道(绝对)偏简单的 D1E,但是我怕自己过若干年(大雾)忘了自己的解法了,所以过来水篇题解( 首先考虑怎么暴力地解决这个问题,不难发现我 ...
- C++ and OO Num. Comp. Sci. Eng. - Part 4.
命名空间与文件(Namespaces and Files) 在 C++ 中,命名空间为包含相关声明与定义的逻辑单元. 将一个大程序分割为不同部分并且将其储存在不同的文件中可以实现模块化编程. 未命名的 ...
- 有限元边界 Dirichlet 条件处理
参考自百度文档,这里只考虑 Dirichlet 边界条件情况. 有限元法基本方法就是是构造线性方程组 \[\begin{equation} Au = f \end{equation}\] 进行求解.其 ...
- dlang 读取gz压缩文件
没找到打开gz压缩文件的标准库,暂时调用系统命令打开gz压缩文件(参考:https://dlang.org/phobos/std_process.html#.Redirect.stdoutToStde ...
- web性能测试工具——http_load
http_load是一款基于Linux平台的web服务器性能测试工具,用于测试web服务器的吞吐量与负载,web页面的性能. http_load是基于linux.unix平台的一种性能测工具 它以并行 ...
- seqtk抽取测序数据
做数据比较的时候,由于同一个样本测序数据量不一致,需要抽取数据,控制数据量基本一致. 自己写脚本速度较慢,后面发现一个不错的工具:seqtk 原始数据抽取 如果只控制原始数据量一致,过滤低质量数据后直 ...
- 自定义char类型字符,django中事务
自定义char类型字符 # 自定义char类型,继承Field父类 class MyCharField(Field): def __init__(self, max_length, *args, ** ...
- Excel-姓名列中同一个人汇总金额列,得出总金额
8.姓名列中同一个人求和金额列,得出总金额. 方法一: P2处公式=SUMPRODUCT(($M$2:$M$20=$M2)*($N$2:$N$20)) 解释函数: 引用:https://zhinan. ...
- vs2019 16.8更新之后的 C++20 协程co_yield用法
由于搜索出来的帖子,都是老版本的实验协程,很多老的代码已经失去参考性,并且很复杂,所以就自己研究了一下. 1 #include <iostream> 2 #include <coro ...
- fastjson转换数字时,格式化小数点
使用fastjson类库转换java对象时,对于BigDecimal类型,有时需要特殊格式,比如: 1.0,转为json时候,要求显式为1,因此需要在转换时做处理.步骤如下: 1.新建类,实现Valu ...