前言:
前面給大家分享過一個工作中用到的編譯拷貝腳本,其實工作中還有一些其他工具的使用,今天再來分享一個自己純手工的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中的程序是怎么運行起來的 必讀
本公眾號全部原創干貨已整理成一個目錄,回復[ 資源 ]即可獲得。