理解 CPU 利用率

從 top 命令說起

在 Linux shell 上執(zhí)行 top 命令拂募,可以看到這樣一行 CPU 利用率的數(shù)據(jù):

%Cpu(s):  0.1 us,  0.0 sy,  0.0 ni, 99.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

這里引用一下 top 命令的 Linux man-pages 里面的介紹:

us, user: time running un-niced user processes

sy, system: time running kernel processes

ni, nice: time running niced user processes

id, idle: time spent in the kernel idle handler

wa, IO-wait: time waiting for I/O completion

hi: time spent servicing hardware interrupts

si: time spent servicing software interrupts

st: time stolen from this vm by the hypervisor

/proc/stat

簡單介紹一下 Linux 計(jì)算 CPU 利用率的基本方法。

/proc/stat 存儲的是系統(tǒng)的一些統(tǒng)計(jì)信息汽烦。在我的機(jī)器上的某一時(shí)刻线脚,內(nèi)容如下:

[linjinhe@localhost ~]$ cat /proc/stat 
cpu  117450 5606 72399 476481991 1832 0 2681 0 0 0
cpu0 31054 90 19055 119142729 427 0 1706 0 0 0
cpu1 22476 3859 18548 119155098 382 0 272 0 0 0
cpu2 29208 1397 19750 119100548 462 0 328 0 0 0
cpu3 34711 258 15045 119083615 560 0 374 0 0 0
intr 41826673 113 102 0 0 0 0 0 0 1 134 0 0 186 0 0 0 81 0 0 256375 0 0 0 29 0 602 143 16 442 94859 271462 25609 4618 8846 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 58634924
btime 1540055659
processes 5180062
procs_running 1
procs_blocked 0
softirq 49572367 5 22376247 238452 3163482 257166 0 4492 19385190 0 414733

我們只關(guān)注第一行 cpu (下面第二行是我加上的注釋)搓劫。

cpu  117450 5606   72399   476481991  1832     0    2681   0      0         0
      (us)  (ni)    (sy)     (id)      (wa)   (hi)  (si)  (st) (guest) (guest_nice)

前面一節(jié)畅买,對于 CPU 利用率描述,Linux man-pages 用的都是 time( time running倚舀, time spent,time stolen)這個(gè)單詞忍宋。這里的統(tǒng)計(jì)數(shù)據(jù)痕貌,其實(shí)就是 CPU 從系統(tǒng)啟動(dòng)至當(dāng)前,各項(xiàng)(us, sy, ni, id, wa, hi, si, st)占用的時(shí)間糠排,單位是 jiffies舵稠。通過 sysconf(_SC_CLK_TCK) 可以獲得 1 秒被分成多少個(gè) jiffies 。一般是 100入宦,即 1 jiffies == 0.01 s哺徊。(st、guest乾闰、guest_nice 和虛擬化/虛擬機(jī)相關(guān)落追,如果這些值太高,說明虛擬化的實(shí)現(xiàn)或者宿主機(jī)有問題涯肩。不是本文關(guān)注的重點(diǎn)轿钠。)

計(jì)算 CPU 使用率的基本原理就是從 /proc/stat 進(jìn)行采樣和計(jì)算。最簡單的方法病苗,一秒采樣一次 /proc/stat疗垛,如:

第 N 秒采樣得到 cpu_total1 = us1 + ni1 + sy1 + id1 + wa1 + hi1 + si1 + st1 + guest1 + guest_nice1
第 N+1 秒采樣得到 cpu_total2 = us2 + ni2 + sy2 + id2 + wa2 + hi2 + si2 + st2 + guest2 + guest_nice2
us 的占比為 (us2 - us1) / (cpu_total2 - cpu_total1)

nice

nice - run a program with modified scheduling priority

nice 是一個(gè)可以修改進(jìn)程調(diào)度優(yōu)先級的命令硫朦,具體可以參考 man-pages贷腕。在 Linux 中,一個(gè)進(jìn)程有一個(gè) nice 值,代表的是這個(gè)進(jìn)程的調(diào)度優(yōu)先級泽裳。

越 nice (nice 值越大)的進(jìn)程芽世,調(diào)度優(yōu)先級越低。怎么理解這句話诡壁?進(jìn)程調(diào)度本質(zhì)上是進(jìn)程間對 CPU 這一有限資源的爭搶济瓢,越 nice 的進(jìn)程,越會“謙讓”妹卿,所以它的獲得 CPU 的機(jī)會就越低旺矾。

上面的 CPU 利用率里面,將用戶態(tài)進(jìn)程使用的 CPU 分成 niced 和 un-niced 兩部分夺克,沒什么本質(zhì)差別箕宙。平時(shí)很少遇到要使用 nice 命令的場景(我個(gè)人從來沒遇到過)。

理解 us

知道了 us 代表的意義后铺纽,簡單寫個(gè)代碼控制一下 us柬帕。

#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include <vector>
#include <string>

void* CpuUsWorker(void* arg)
{
    uint64_t i = 0;
    while (true)
    {
        i++;
    }   
    return nullptr;
}

void CpuUs(int n)
{
    std::vector<pthread_t> pthreads(n);
    for (int i = 0; i < n; i++)
    {
        assert(pthread_create(&pthreads[i], nullptr, CpuUsWorker, nullptr) == 0);
    }

    for (const auto& tid : pthreads)
    {
        assert(pthread_join(tid, nullptr) == 0);
    }
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s threads\n", argv[0]);
        return -1;
    }   
    CpuUs(std::stoi(argv[1]));
    return 0;
}

測試的機(jī)器是 4 個(gè)核。代碼比較簡單狡门,一個(gè)線程可以跑滿一個(gè)核陷寝。下面是我的測試結(jié)果:

./cpu_us 1
%Cpu(s): 25.0 us,  0.0 sy,  0.0 ni, 75.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
./cpu_us 2
%Cpu(s): 50.0 us,  0.1 sy,  0.0 ni, 49.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
./cpu_us 3
%Cpu(s): 75.1 us,  0.0 sy,  0.0 ni, 24.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

理解 ni

  1. ni 代表的是 niced 用戶態(tài)進(jìn)程消耗的 CPU。
  2. nice 可以調(diào)整運(yùn)行程序的 nice 值其馏。

下面是我的測試結(jié)果凤跑,可以看出 ni 變成 25%,符合預(yù)期叛复。

nice ./cpu_us 1
%Cpu(s):  0.1 us,  0.0 sy, 25.0 ni, 74.9 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st

下面是 top 命令顯示的 nice ./cpu_us 1 的進(jìn)程信息仔引,NI 這一列就是 nice 值,其值為 10褐奥。

PID    USER    PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND          
6905 linjinhe  30  10   23024    844    700 S 100.0  0.0   0:03.06 cpu_us 

下面是 ./cpu_us 1 的進(jìn)程信息咖耘,其值為 0。

PID   USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND  
6901 linjinhe   20   0   23024    844    700 S 100.0  0.0   0:12.36 cpu_us  

理解 sy

一般情況下撬码,如果 sy 過高儿倒,說明程序調(diào)用 Linux 系統(tǒng)調(diào)用的開銷很大。這個(gè)也可以簡單寫個(gè)程序驗(yàn)證一下耍群。

#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include <vector>
#include <string>

void* NoopWorker(void* arg)
{
    return nullptr;
}

void* CpuSyWorker(void* arg)
{
    while (true)
    {
        pthread_t tid;
        assert(pthread_create(&tid, nullptr, NoopWorker, nullptr) == 0);
        assert(pthread_detach(tid) == 0);
    }
}

void CpuSy(int n)
{
    std::vector<pthread_t> pthreads(n); 
    for (int i = 0; i < n; i++)
    {
        assert(pthread_create(&pthreads[i], nullptr, CpuSyWorker, nullptr) == 0);
    }
    for (const auto& tid : pthreads)
    {
        assert(pthread_join(tid, nullptr) == 0);
    }
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s threads\n", argv[0]);
        return -1;
    }

    CpuSy(std::stoi(argv[1]));
}

測試結(jié)果:

./cpu_sy 1
%Cpu(s):  8.8 us, 59.3 sy,  0.0 ni, 31.3 id,  0.0 wa,  0.0 hi,  0.6 si,  0.0 st

大量的系統(tǒng)調(diào)用讓 sy 飆升义桂。不同的系統(tǒng)調(diào)用開銷不一樣,pthread_create 的開銷比較大蹈垢。

理解 wa

wa 這一項(xiàng)慷吊,連相關(guān)的 Linux man-pages 都說它不太靠譜。所以千萬不要看到 wa 很高就覺得系統(tǒng)的 I/O 有問題曹抬。

  1. The CPU will not wait for I/O to complete; iowait is the time that a task is waiting for I/O to complete. When a CPU goes into idle state for outstanding task I/O, another task will be scheduled on this CPU.
  2. On a multi-core CPU, the task waiting for I/O to complete is not running on any CPU, so the iowait of each CPU is difficult to calculate.
  3. The value in this field may decrease in certain conditions.

說一下我的理解:

  1. 假設(shè)有個(gè)單核的系統(tǒng)溉瓶。CPU 并不會真的“死等” I/O。此時(shí)的 CPU 實(shí)際是 idle 的,如果有其它進(jìn)程可以運(yùn)行堰酿,則運(yùn)行其它進(jìn)程疾宏,此時(shí) CPU 時(shí)間就不算入 iowait。如果此時(shí)系統(tǒng)沒有其它進(jìn)程需要運(yùn)行触创,則 CPU 需要“等”這次 I/O 完成才可以繼續(xù)運(yùn)行坎藐,此時(shí)“等待”的時(shí)間算入 iowait。
  2. 對于多核系統(tǒng)哼绑,如果有 iowait岩馍,要算給哪個(gè) CPU?這是個(gè)問題抖韩。
  3. wa 高蛀恩,不能說明系統(tǒng)的 I/O 有問題。如果整個(gè)系統(tǒng)只有簡單任務(wù)不停地進(jìn)行 I/O,此時(shí)的 wa 可能很高,而系統(tǒng)磁盤的 I/O 也遠(yuǎn)遠(yuǎn)沒達(dá)到上限掠械。
  4. wa 低,也不能說明系統(tǒng)的 I/O 沒問題顽馋。假設(shè)機(jī)器進(jìn)行大量的 I/O 任務(wù)把磁盤帶寬打得慢慢的,同時(shí)還有計(jì)算任務(wù)把 CPU 也跑得滿滿的驹尼。此時(shí) wa 很低趣避,但系統(tǒng) I/O 壓力很大。
#include <pthread.h>
#include <stdio.h>
#include <assert.h>
#include <vector>
#include <string>
#include <fcntl.h>
#include <unistd.h>

void* CpuWaWorker(void* arg)
{
    std::string filename = "test_" + std::to_string(pthread_self());
    int fd = open(filename.c_str(), O_CREAT | O_WRONLY);
    assert(fd > 0);
    while (true)
    {
        assert(write(fd, filename.c_str(), filename.size()) > 0);
        assert(write(fd, "\n", 1) > 0);
        assert(fsync(fd) == 0);
    }
    return nullptr;
}

void CpuWa(int n)
{
    std::vector<pthread_t> pthreads(n);
    for (int i = 0; i < n; i++)
    {
        assert(pthread_create(&pthreads[i], nullptr, CpuWaWorker, nullptr) == 0);
    }

    for (const auto& tid : pthreads)
    {
        assert(pthread_join(tid, nullptr));
    }
}

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s threads\n", argv[0]);
        return -1;
    }   
    CpuWa(std::stoi(argv[1]));
    return 0;
}
./cpu_wa 10
%Cpu(s):  0.3 us,  6.3 sy,  0.0 ni, 50.0 id, 41.1 wa,  0.0 hi,  2.3 si,  0.0 s

在上面這個(gè)例子中新翎,我用多個(gè)線程不停地進(jìn)行小 I/O 把 wa 的值刷上去了,但是其實(shí)占用的 I/O 帶寬很小住练,我的測試機(jī)是 SSD 的地啰,此時(shí)的 I/O 壓力并不大。

再看一個(gè)例子:

./cpu_wa 10
./cpu_us 3
%Cpu(s): 75.3 us,  3.5 sy,  0.0 ni,  8.2 id, 10.3 wa,  0.0 hi,  2.7 si,  0.0 st

可以看到讲逛,明明同樣執(zhí)行了 ./cpu_wa 10亏吝, wa 竟然因?yàn)橥瑫r(shí)進(jìn)行 ./cpu_us 3 而降下來!U祷臁蔚鸥!參考上面第 4 點(diǎn)。

理解 si 和 hi

系統(tǒng)調(diào)用會觸發(fā)軟中斷许赃,所以在上面的一些例子執(zhí)行時(shí)止喷,si 也會有所變化,如:

./cpu_wa 10
%Cpu(s):  0.3 us,  6.3 sy,  0.0 ni, 50.0 id, 41.1 wa,  0.0 hi,  2.3 si,  0.0 s

網(wǎng)卡收到數(shù)據(jù)包后混聊,網(wǎng)卡驅(qū)動(dòng)會通過軟中斷通知 CPU弹谁。這里用 iperf 網(wǎng)絡(luò)性能測試工具做一下實(shí)驗(yàn)。

$ iperf -s -i 1  # 服務(wù)端

$ iperf -c 192.168.1.4 -i 1 -t 60 # 客戶端,可以開幾個(gè) terminal 執(zhí)行多個(gè)客戶端预愤,這樣 si 的變化才會比較明顯
%Cpu(s):  1.7 us, 74.1 sy,  0.0 ni,  8.0 id,  0.0 wa,  0.0 hi, 16.2 si,  0.0 st

硬件中斷的話沟于,暫時(shí)找不到什么測試方法,實(shí)際應(yīng)用中應(yīng)該也比較少遇到植康。

理解 st

st 和虛擬化相關(guān)旷太,這里說說我的理解。

利用虛擬化技術(shù)销睁,一臺 32 CPU 核心的物理機(jī)供璧,可以創(chuàng)建出幾十上百個(gè)單 CPU 核心的虛擬機(jī)。這在公有云場景下榄攀,簡稱“超賣”嗜傅。
大部分情況下,物理服務(wù)器的資源有大量是閑置的檩赢。此時(shí)吕嘀,“超賣”并不會造成明顯影響。
當(dāng)很多虛擬機(jī)的 CPU 壓力變大贞瞒,此時(shí)物理機(jī)的資源明顯不足偶房,就會造成各個(gè)虛擬機(jī)之間相互競爭、相互等待军浆。
st 就是用來衡量被 Hypervisor “偷去” 給其它虛擬機(jī)使用的 CPU棕洋。這個(gè)值越高,說明這臺物理服務(wù)器的資源競爭越激烈乒融。

(云廠商會不會把他們的內(nèi)核給改了掰盘,把 st 改成 0 不讓你發(fā)現(xiàn)這種情況?)

理解 id

CPU 空閑赞季,感覺這個(gè)從應(yīng)用層的角度沒什么難理解的愧捕。

這里推薦一篇文章 What's a CPU to do when it has nothing to do?,有興趣的可以看一下申钩。

(本文完)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末次绘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子撒遣,更是在濱河造成了極大的恐慌邮偎,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件义黎,死亡現(xiàn)場離奇詭異禾进,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)轩缤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進(jìn)店門命迈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贩绕,“玉大人,你說我怎么就攤上這事壶愤∈缜悖” “怎么了?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵征椒,是天一觀的道長娇哆。 經(jīng)常有香客問我,道長勃救,這世上最難降的妖魔是什么碍讨? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮蒙秒,結(jié)果婚禮上勃黍,老公的妹妹穿的比我還像新娘。我一直安慰自己晕讲,他們只是感情好覆获,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著瓢省,像睡著了一般弄息。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上勤婚,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天摹量,我揣著相機(jī)與錄音,去河邊找鬼馒胆。 笑死缨称,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的祝迂。 我是一名探鬼主播具钥,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼液兽!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起掌动,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤四啰,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后粗恢,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柑晒,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年眷射,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了匙赞。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片佛掖。...
    茶點(diǎn)故事閱讀 40,030評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖涌庭,靈堂內(nèi)的尸體忽然破棺而出芥被,到底是詐尸還是另有隱情,我是刑警寧澤坐榆,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布拴魄,位于F島的核電站,受9級特大地震影響席镀,放射性物質(zhì)發(fā)生泄漏匹中。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一豪诲、第九天 我趴在偏房一處隱蔽的房頂上張望顶捷。 院中可真熱鬧,春花似錦屎篱、人聲如沸服赎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽专肪。三九已至,卻和暖如春堪侯,著一層夾襖步出監(jiān)牢的瞬間嚎尤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工伍宦, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留芽死,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓次洼,卻偏偏與公主長得像关贵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子卖毁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 這篇文章主要介紹一些常用的linux服務(wù)器性能監(jiān)控命令揖曾,包括命令的常用參數(shù)、指標(biāo)的含義以及一些交互操作亥啦。 幾個(gè)問題...
    dancingking閱讀 8,350評論 6 22
  • linux系統(tǒng)性能監(jiān)視命令炭剪,經(jīng)過百度谷歌,加上各渠道獲得翔脱,查找到相關(guān)命令匯集奴拦,望能學(xué)有所收獲 ...
    _王子_閱讀 1,348評論 3 13
  • ¥開啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個(gè)線程,因...
    小菜c閱讀 6,424評論 0 17
  • 這篇文章是對優(yōu)秀第三方微博安卓APP的推薦绿鸣。 所有應(yīng)用裏最毒瘤的當(dāng)屬微博。自啓的喪心病狂暂氯,而且大量地喚醒其他應(yīng)用潮模,...
    離娮閱讀 651評論 3 0
  • 演員荒!演員荒株旷!現(xiàn)在的小鮮肉放眼海內(nèi)外演技在線的屈指可數(shù)再登。張一山還可以,日本就沒有一個(gè)三十五以下演技在線的,美國的...
    呶呶的簡書閱讀 279評論 0 0