CPU使用率
CPU使用率是最常見的監控指標之一,用于衡量CPU的繁忙程度。
一方面,如果某個系統CPU使用率越高,意味著應用進程得不到調度的概率越大,應用響應速度將受負面影響; 另一方面,某個應用CPU使用率過高,意味著其消耗太多CPU資源,很可能存在優化的空間。
指標
大多數系統管理員或開發人員對CPU使用率或多或少有所了解,但未必準確。
那么,操作系統如何衡量CPU的繁忙程度呢?
最簡單的方式是,統計CPU在執行任務的時間以及空閑的時間,并計算這兩部分時間的占比。 如果執行任務時間占比很高,則說明CPU非常繁忙;反之亦然。 因此,CPU使用率是一個百分比也就非常好理解了。
通常,操作系統對CPU時間的統計更為細化。 以Linux為例,內核進一步將執行時間和空閑時間進行分類,形成更為細致指標:
| 指標名 | 含義 |
|---|---|
| user | 用戶態 |
| nice | 低優先級用戶態 |
| system | 內核態 |
| idle | 空閑 |
| iowait | IO等待 |
| irq | 中斷處理 |
| softirq | 軟中斷處理 |
| steal | 被其他虛擬化系統占用 |
| guest | 運行客戶機系統 |
| guest_nice | 運行低優先級客戶機系統 |
user
user表示CPU運行在用戶態的時間占比。
應用進程執行分為用戶態以及內核態:CPU在用戶態執行應用進程自身的代碼邏輯,通常是一些邏輯或數值計算;CPU在內核態執行進程發起的系統調用,通常是響應進程對資源的請求。
如果應用為計算密集型(包含大量計算很少系統調用),則CPUuser狀態使用率很高。
nice
nice表示CPU運行在低優先級用戶態的時間占比,低優先級意味著進程nice值小于0。
system
user表示CPU運行在內核態的時間占比。
一般而言,內核態CPU使用率不應過高,除非應用進程發起大量系統調用。 如果該值較高,需要著手分析原因,重新審視程序設計是否存在缺陷。
idle
idle表示CPU在空閑狀態的時間占比,該狀態下CPU沒有任何任務可執行。
iowait
iowait表示“等待I/O”的時間。 大部分人對此有誤解,認為CPU此時不能工作。
這是不正確的,CPU在等待I/O時,可以切換到其他就緒任務執行,只是當時剛好沒有就緒任務可以運行。 準確講,iowait是CPU空閑并且系統有I/O請求未完成的時間。
另一個誤解是:iowait升高時便認為系統存在I/O瓶頸。 同種I/O條件下,如果系統還有其他計算密集型任務,iowait將明顯降低。
因此,iowait是一個非常模糊的指標,并不足以說明問題。 大部分情況下,還需要檢查I/O量,等待隊列等更加明確的指標。 如果只是iowait升高,其他指標沒有明顯變化,便無需擔心。
注解
idle和iowait都說明CPU很空閑,iowait還說明系統有未完成的I/O請求。
irq
irq表示CPU處理硬件中斷的時間占比。
網卡中斷是一個典型的例子:網卡接到數據包后,通過硬件中斷通知CPU進行處理。 如果系統網絡流量非常大,則可觀察到irq使用率明顯升高。
通常,網卡中斷只由一個CPU來響應。 如果網絡處理上不去并觀察到單個CPUirq指標較高,則可以考慮通過irqbalance將中斷處理平衡到更多CPU上。
softirq
對應地,softirq表示CPU處理軟件中斷的時間占比。
steal
steal是指在虛擬化環境中,被其他系統占用的時間。 這體現為物理CPU沒有辦法為當前系統服務,通常正在為另一個系統服務。 在虛擬機超賣比較嚴重的場景,這個數值非常明顯。 這部分時間顯然不是當前系統所用,而是被其他系統占用了。
total
總CPU時間片數是各種狀態時間的和,計算公式如下:
total=user+nice+system+idle+iowait+irq+softirq+stealtotal=user+nice+system+idle+iowait+irq+softirq+steal
注意到,guest以及guest_nice不參與求和計算,因為這兩種時間分別作為user以及nice的一部分統計在其中了。
utilized
CPU用于執行任務的時間將是6種執行狀態時間的總和:
utilized=user+nice+system+irq+softirq+stealutilized=user+nice+system+irq+softirq+steal
除此之外,還有另外一種計算方法,只包含5種執行狀態:
utilized=user+nice+system+irq+softirqutilized=user+nice+system+irq+softirq
兩種計算方式區別只在于:steal狀態占用的時間是否參與計算。 前者反應了系統的實際負載,steal雖不是本系統占用,但也制約了系統對CPU資源的進一步使用; 后者則反映了系統的真實負載,也就是系統的實際開銷。
算法
Linux內核為CPU各個核心維護了自系統啟動以來各種狀態的時間,并暴露在在proc偽文件系統中,路徑為/proc/stat。 通過以下命令可以窺探一二:
$ cat /proc/stat
注解
/proc/stat中,CPU時間單位為jiffy,即USER_HZ分之一秒。 其中,USER_HZ是內核計時時鐘的頻率,表示時鐘每秒產生多少次中斷。 時鐘每中斷一次,內核jiffies自增1。
很顯然,CPU使用率可以由內核提供的計數器(counters)計算而來。
首先,在t1時間點采集一次/proc/stat數據并計算總CPU時間:
totalt1=usert1+nicet1+systemt1+idlet1+iowaitt1+irqt1+softirqt1+stealt1totalt1=usert1+nicet1+systemt1+idlet1+iowaitt1+irqt1+softirqt1+stealt1
在t2時間點再采集一次,同樣計算總CPU時間:
totalt2=usert2+nicet2+systemt2+idlet2+iowaitt2+irqt2+softirqt2+stealt2totalt2=usert2+nicet2+systemt2+idlet2+iowaitt2+irqt2+softirqt2+stealt2
那么,從t1到t2的CPU時間為:
deltat1,t2=totalt2?totalt1deltat1,t2=totalt2?totalt1
其中,用戶態時間占比為:
user_percent=usert2?usert1totalt2?totalt1×100%user_percent=usert2?usert1totalt2?totalt1×100%
這便是用戶態CPU使用率,其他狀態使用率計算方式以此類推。
很顯然,所有狀態CPU使用率加起來剛好就是100%(同樣不包括guest系列):
user_percent+nice_percent+?+steal_percent=100%user_percent+nice_percent+?+steal_percent=100%
采集
接下,看看如何讀取/proc/stat文件并計算CPU使用率。直接上代碼:
cpu_usage.py
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
import time
from tabulate import (
tabulate,
)
# sample interval
INTERVAL = 1
# table header
TABLE_HEADER = (
'device',
'utilized',
'user',
'nice',
'system',
'idle',
'iowait',
'irq',
'softirq',
'steal',
'guest',
'guset_nice',
)
def cpu_counters():
records = []
# open /proc/stat to read
with open('/proc/stat') as f:
# iterate all lines
for line in f.readlines():
if not line.startswith('cpu'):
continue
# split to fields
fields = line.strip().split()
# cpu name
name = fields[0]
# convert all counters to int
counters = tuple(map(int, fields[1:]))
# calculate total cpu time
total = sum(counters[:8])
records.append((name, counters, total))
return records
def sample_forever():
last_records = None
while True:
# sample cpu counters
records = cpu_counters()
if last_records:
table_data = []
# iterate counters for every cpu core
for (device, last_counters, last_total), (_, counters, total) in
zip(last_records, records):
# calculate cpu usage
delta = total - last_total
percents = list(map(
lambda pair: 100. * (pair[0]-pair[1]) / delta,
zip(counters, last_counters),
))
utilized_percent = sum(percents[:3] + percents[5:8])
table_data.append([device, utilized_percent] + percents)
# make table
table_data = tabulate(
table_data,
TABLE_HEADER,
tablefmt='simple',
floatfmt='6.2f',
)
# print table
print(table_data)
print()
last_records = records
time.sleep(INTERVAL)
def main():
try:
sample_forever()
except KeyboardInterrupt:
pass
if __name__ == '__main__':
main()
|
cpu_counters函數負責讀取/proc/stat文件并解析所有CPU時間計數器:
第31行,打開/proc/stat文件;
第33行,讀取所有文本行;
第34-35行,跳過所有非cpu開頭的行;
第38行,切分字段;
第41行,取出CPU名字段;
第43行,將所有CPU時間計數器轉換成整數類型;
第45行,累加總CPU時間;
第47行,記錄解析結果;
sample_forever函數不斷采集并計算CPU使用率,計算部分邏輯如下:
第62-63行,遍歷每個CPU設備,分別取出CPU名、計數器以及總CPU時間;
第66行,計算兩次采集間的總CPU時間;
第67-70行,計算兩次采集間CPU每種狀態執行時間的占比(百分比);
第71行,計算CPU繁忙時間占比,包括steal部分;
注意到,例子程序使用tabulate模塊格式化表格,不再贅述。
總結
- 上一篇: 睡眠监测原理
- 下一篇: a7r2和a7r3区别(2021年底的索