国产av日韩一区二区三区精品,成人性爱视频在线观看,国产,欧美,日韩,一区,www.成色av久久成人,2222eeee成人天堂

Linux 中 CPU 利用率是如何算出來的?

WBOY
發(fā)布: 2024-02-15 11:15:11
轉(zhuǎn)載
925人瀏覽過

在線上服務(wù)器觀察線上服務(wù)運行狀態(tài)的時候,絕大多數(shù)人都是喜歡先用 top 命令看看當(dāng)前系統(tǒng)的整體 cpu 利用率。例如,隨手拿來的一臺機(jī)器,top 命令顯示的利用率信息如下:

Linux 中 CPU 利用率是如何算出來的?

這個輸出結(jié)果說簡單也簡單,說復(fù)雜也不是那么容易就能全部搞明白的。例如:

問題 1:top 輸出的利用率信息是如何計算出來的,它精確嗎?
問題 2:ni 這一列是 nice,它輸出的是 cpu 在處理啥時的開銷?
問題 3:wa 代表的是 io wait,那么這段時間中 cpu 到底是忙碌還是空閑?

今天我們對 cpu 利用率統(tǒng)計進(jìn)行深入的學(xué)習(xí)。通過今天的學(xué)習(xí),你不但能了解 cpu 利用率統(tǒng)計實現(xiàn)細(xì)節(jié),還能對 nice、io wait 等指標(biāo)有更深入的理解。

今天我們先從自己的思考開始!

一、先思考一下

拋開 Linux 的實現(xiàn)先不談,如果有如下需求,有一個四核服務(wù)器,上面跑了四個進(jìn)程。

Linux 中 CPU 利用率是如何算出來的?

讓你來設(shè)計計算整個系統(tǒng) cpu 利用率的這個需求,支持像 top 命令這樣的輸出,滿足以下要求:

  • cpu 使用率要盡可能地準(zhǔn)確;
  • 要盡可能地體現(xiàn)秒級瞬時 cpu 狀態(tài)。

可以先停下來思考幾分鐘。

Linux 中 CPU 利用率是如何算出來的?

好,思考結(jié)束。經(jīng)過思考你會發(fā)現(xiàn),這個看起來很簡單的需求,實際還是有點小復(fù)雜的。

其中一個思路是把所有進(jìn)程的執(zhí)行時間都加起來,然后再除以系統(tǒng)執(zhí)行總時間*4。

Linux 中 CPU 利用率是如何算出來的?

這個思路是沒問題的,用這種方法統(tǒng)計很長一段時間內(nèi)的 cpu 利用率是可以的,統(tǒng)計也足夠的準(zhǔn)確。

但只要用過 top 你就知道 top 輸出的 cpu 利用率并不是長時間不變的,而是默認(rèn) 3 秒為單位會動態(tài)更新一下(這個時間間隔可以使用 -d 設(shè)置)。我們的這個方案體現(xiàn)總利用率可以,體現(xiàn)這種瞬時的狀態(tài)就難辦了。你可能會想到那我也 3 秒算一次不就行了?但這個 3 秒的時間從哪個點開始呢。粒度很不好控制。

上一個思路問題核心就是如何解決瞬時問題。提到瞬時狀態(tài),你可能就又來思路了。那我就用瞬時采樣去看,看看當(dāng)前有幾個核在忙。四個核中如果有兩個核在忙,那利用率就是 50%。

這個思路思考的方向也是正確的,但是問題有兩個:

  • 你算出的數(shù)字都是 25% 的整數(shù)倍;
  • 這個瞬時值會導(dǎo)致 cpu 使用率顯示的劇烈震蕩。

比如下圖:

Linux 中 CPU 利用率是如何算出來的?

在 t1 的瞬時狀態(tài)看來,系統(tǒng)的 cpu 利用率毫無疑問就是 100%,但在 t2 時間看來,使用率又變成 0% 了。思路方向是對的,但顯然這種粗暴的計算無法像 top 命令一樣優(yōu)雅地工作。

我們再改進(jìn)一下它,把上面兩個思路結(jié)合起來,可能就能解決我們的問題了。在采樣上,我們把周期定得細(xì)一些,但在計算上我們把周期定得粗一些。

我們引入采用周期的概念,定時比如每 1 毫秒采樣一次。如果采樣的瞬時,cpu 在運行,就將這 1 ms 記錄為使用。這時會得出一個瞬時的 cpu 使用率,把它都存起來。

Linux 中 CPU 利用率是如何算出來的?

在統(tǒng)計 3 秒內(nèi)的 cpu 使用率的時候,比如上圖中的 t1 和 t2 這段時間范圍。那就把這段時間內(nèi)的所有瞬時值全加一下,取個平均值。這樣就能解決上面的問題了,統(tǒng)計相對準(zhǔn)確,避免了瞬時值劇烈震蕩且粒度過粗(只能以 25% 為單位變化)的問題了。

可能有同學(xué)會問了,假如 cpu 在兩次采樣中間發(fā)生變化了呢,如下圖這種情況。

Linux 中 CPU 利用率是如何算出來的?

在當(dāng)前采樣點到來的時候,進(jìn)程 A 其實剛執(zhí)行完,有一點點時間既沒被上一個采樣點統(tǒng)計到,本次也統(tǒng)計不到。對于進(jìn)程 B,其實只開始了一小段時間,把 1 ms 全記上似乎有點多記了。

確實會存在這個問題,但因為我們的采樣是 1 ms 一次,而我們實際查看使用的時候最少也是秒級別地用,會包括有成千上萬個采樣點的信息,所以這種誤差并不會影響我們對全局的把握。

事實上,Linux 也就是這樣來統(tǒng)計系統(tǒng) cpu 利用率的。雖然可能會有誤差,但作為一項統(tǒng)計數(shù)據(jù)使用已經(jīng)是足夠了的。在實現(xiàn)上,Linux 是將所有的瞬時值都累加到某一個數(shù)據(jù)上的,而不是真的存了很多份的瞬時數(shù)據(jù)。

接下來就讓我們進(jìn)入 Linux 來查看它對系統(tǒng) cpu 利用率統(tǒng)計的具體實現(xiàn)。

二、top 命令使用數(shù)據(jù)在哪兒

上一節(jié)我們說的 Linux 在實現(xiàn)上是將瞬時值都累加到某一個數(shù)據(jù)上的,這個值是內(nèi)核通過 /proc/stat 偽文件來對用戶態(tài)暴露。Linux 在計算系統(tǒng) cpu 利用率的時候用的就是它。

整體上看,top 命令工作的內(nèi)部細(xì)節(jié)如下圖所示。

Linux 中 CPU 利用率是如何算出來的?

top 命令訪問 /proc/stat 獲取各項 cpu 利用率使用值;

  • 內(nèi)核調(diào)用 stat_open 函數(shù)來處理對 /proc/stat 的訪問;

  • 內(nèi)核訪問的數(shù)據(jù)來源于 kernel_cpustat 數(shù)組,并匯總;

  • 打印輸出給用戶態(tài)。

接下來我們把每一步都展開來詳細(xì)看看。

通過使用 strace 跟蹤 top 命令的各種系統(tǒng)調(diào)用,可以看到它對該文件的調(diào)用。

#?strace?top
...
openat(AT_FDCWD,?"/proc/stat",?O_RDONLY)?=?4
openat(AT_FDCWD,?"/proc/2351514/stat",?O_RDONLY)?=?8
openat(AT_FDCWD,?"/proc/2393539/stat",?O_RDONLY)?=?8
...
登錄后復(fù)制

除了 /proc/stat 外,還有各個進(jìn)程細(xì)分的 /proc/{pid}/stat,是用來計算各個進(jìn)程的 cpu 利用率時使用的。

內(nèi)核為各個偽文件都定義了處理函數(shù),/proc/stat 文件的處理方法是 proc_stat_operations。

//file:fs/proc/stat.c
static?int?__init?proc_stat_init(void)
{
?proc_create("stat",?0,?NULL,?&proc_stat_operations);
?return?0;
}

static?const?struct?file_operations?proc_stat_operations?=?{
?.open??=?stat_open,
?...
};
登錄后復(fù)制

proc_stat_operations 中包含了該文件對應(yīng)的操作方法。當(dāng)打開 /proc/stat 文件的時候,stat_open 就會被調(diào)用到。stat_open 依次調(diào)用 single_open_size,show_stat 來輸出數(shù)據(jù)內(nèi)容。我們來看看它的代碼:

//file:fs/proc/stat.c
static?int?show_stat(struct?seq_file?*p,?void?*v)
{
?u64?user,?nice,?system,?idle,?iowait,?irq,?softirq,?steal;

?for_each_possible_cpu(i)?{
??struct?kernel_cpustat?*kcs?=?&kcpustat_cpu(i);

??user?+=?kcs->cpustat[CPUTIME_USER];
??nice?+=?kcs->cpustat[CPUTIME_NICE];
??system?+=?kcs->cpustat[CPUTIME_SYSTEM];
??idle?+=?get_idle_time(kcs,?i);
??iowait?+=?get_iowait_time(kcs,?i);
??irq?+=?kcs->cpustat[CPUTIME_IRQ];
??softirq?+=?kcs->cpustat[CPUTIME_SOFTIRQ];
??...
?}

?//轉(zhuǎn)換成節(jié)拍數(shù)并打印出來
?seq_put_decimal_ull(p,?"cpu??",?nsec_to_clock_t(user));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(nice));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(system));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(idle));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(iowait));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(irq));
?seq_put_decimal_ull(p,?"?",?nsec_to_clock_t(softirq));
?...
}
登錄后復(fù)制

在上面的代碼中,for_each_possible_cpu 是在遍歷存儲著 cpu 使用率數(shù)據(jù)的 kcpustat_cpu 變量。該變量是一個 percpu 變量,它為每一個邏輯核都準(zhǔn)備了一個數(shù)組元素。里面存儲著當(dāng)前核所對應(yīng)各種事件,包括 user、nice、system、idel、iowait、irq、softirq 等。

在這個循環(huán)中,將每一個核的每種使用率都加起來。最后通過 seq_put_decimal_ull 將這些數(shù)據(jù)輸出出來。

Linux 中 CPU 利用率是如何算出來的?

注意,在內(nèi)核中實際每個時間記錄的是納秒數(shù),但是在輸出的時候統(tǒng)一都轉(zhuǎn)化成了節(jié)拍單位。至于節(jié)拍單位多長,下一節(jié)我們介紹??傊?/proc/stat 的輸出是從 kernel_cpustat 這個 percpu 變量中讀取出來的。

我們接著再看看這個變量中的數(shù)據(jù)是何時加進(jìn)來的。

三、統(tǒng)計數(shù)據(jù)怎么來的

前面我們提到內(nèi)核是以采樣的方式來統(tǒng)計 cpu 使用率的。這個采樣周期依賴的是 Linux 時間子系統(tǒng)中的定時器。

Linux 內(nèi)核每隔固定周期會發(fā)出 timer interrupt (IRQ 0),這有點像樂譜中的節(jié)拍的概念。每隔一段時間,就打出一個拍子,Linux 就響應(yīng)之并處理一些事情。

Linux 中 CPU 利用率是如何算出來的?

一個節(jié)拍的長度是多長時間,是通過 CONFIG_HZ 來定義的。它定義的方式是每一秒有幾次 timer interrupts。不同的系統(tǒng)中這個節(jié)拍的大小可能不同,通常在 1 ms 到 10 ms 之間??梢栽谧约旱?Linux config 文件中找到它的配置。

#?grep?^CONFIG_HZ?/boot/config-5.4.56.bsk.10-amd64
CONFIG_HZ=1000
登錄后復(fù)制

從上述結(jié)果中可以看出,我的機(jī)器每秒要打出 1000 次節(jié)拍。也就是每 1 ms 一次。

每次當(dāng)時間中斷到來的時候,都會調(diào)用 update_process_times 來更新系統(tǒng)時間。更新后的時間都存儲在我們前面提到的 percpu 變量 kcpustat_cpu 中。

Linux 中 CPU 利用率是如何算出來的?

我們來詳細(xì)看下匯總過程 update_process_times 的源碼,它位于 kernel/time/timer.c 文件中。

//file:kernel/time/timer.c
void?update_process_times(int?user_tick)
{
?struct?task_struct?*p?=?current;

?//進(jìn)行時間累積處理
?account_process_tick(p,?user_tick);
?...
}
登錄后復(fù)制

這個函數(shù)的參數(shù) user_tick 指的是采樣的瞬間是處于內(nèi)核態(tài)還是用戶態(tài)。接下來調(diào)用 account_process_tick。

//file:kernel/sched/cputime.c
void?account_process_tick(struct?task_struct?*p,?int?user_tick)
{
?cputime?=?TICK_NSEC;
?...

?if?(user_tick)
??//3.1?統(tǒng)計用戶態(tài)時間
??account_user_time(p,?cputime);
?else?if?((p?!=?rq->idle)?||?(irq_count()?!=?HARDIRQ_OFFSET))
??//3.2?統(tǒng)計內(nèi)核態(tài)時間
??account_system_time(p,?HARDIRQ_OFFSET,?cputime);
?else
??//3.3?統(tǒng)計空閑時間
??account_idle_time(cputime);
}
登錄后復(fù)制

在這個函數(shù)中,首先設(shè)置 cputime = TICK_NSEC, 一個 TICK_NSEC 的定義是一個節(jié)拍所占的納秒數(shù)。接下來根據(jù)判斷結(jié)果分別執(zhí)行 account_user_time、account_system_time 和 account_idle_time 來統(tǒng)計用戶態(tài)、內(nèi)核態(tài)和空閑時間。

3.1 用戶態(tài)時間統(tǒng)計

//file:kernel/sched/cputime.c
void?account_user_time(struct?task_struct?*p,?u64?cputime)
{
?//分兩種種情況統(tǒng)計用戶態(tài)?CPU?的使用情況
?int?index;
?index?=?(task_nice(p)?>?0)???CPUTIME_NICE?:?CPUTIME_USER;

?//將時間累積到?/proc/stat?中
?task_group_account_field(p,?index,?cputime);
?......
}
登錄后復(fù)制

account_user_time 函數(shù)主要分兩種情況統(tǒng)計:

  • 如果進(jìn)程的 nice 值大于 0,那么將會增加到 CPU 統(tǒng)計結(jié)構(gòu)的 nice 字段中。
  • 如果進(jìn)程的 nice 值小于等于 0,那么增加到 CPU 統(tǒng)計結(jié)構(gòu)的 user 字段中。

看到這里,開篇的問題 2 就有答案了,其實用戶態(tài)的時間不只是 user 字段,nice 也是。之所以要把 nice 分出來,是為了讓 Linux 用戶更一目了然地看到調(diào)過 nice 的進(jìn)程所占的 cpu 周期有多少。

我們平時如果想要觀察系統(tǒng)的用戶態(tài)消耗的時間的話,應(yīng)該是將 top 中輸出的 user 和 nice 加起來一并考慮,而不是只看 user!

接著調(diào)用 task_group_account_field 來把時間加到前面我們用到的 kernel_cpustat 內(nèi)核變量中。

//file:kernel/sched/cputime.c
static?inline?void?task_group_account_field(struct?task_struct?*p,?int?index,
??????u64?tmp)
{
?__this_cpu_add(kernel_cpustat.cpustat[index],?tmp);
?...
}
登錄后復(fù)制

3.2 內(nèi)核態(tài)時間統(tǒng)計

我們再來看內(nèi)核態(tài)時間是如何統(tǒng)計的,找到 account_system_time 的代碼。

//file:kernel/sched/cputime.c
void?account_system_time(struct?task_struct?*p,?int?hardirq_offset,?u64?cputime)
{
?if?(hardirq_count()?-?hardirq_offset)
??index?=?CPUTIME_IRQ;
?else?if?(in_serving_softirq())
??index?=?CPUTIME_SOFTIRQ;
?else
??index?=?CPUTIME_SYSTEM;

?account_system_index_time(p,?cputime,?index);
}
登錄后復(fù)制

內(nèi)核態(tài)的時間主要分 3 種情況進(jìn)行統(tǒng)計。

  • 如果當(dāng)前處于硬中斷執(zhí)行上下文, 那么統(tǒng)計到 irq 字段中;
  • 如果當(dāng)前處于軟中斷執(zhí)行上下文, 那么統(tǒng)計到 softirq 字段中;
  • 否則統(tǒng)計到 system 字段中。

判斷好要加到哪個統(tǒng)計項中后,依次調(diào)用 account_system_index_time、task_group_account_field 來將這段時間加到內(nèi)核變量 kernel_cpustat 中。

//file:kernel/sched/cputime.c
static?inline?void?task_group_account_field(struct?task_struct?*p,?int?index,
??????u64?tmp)
{?
?__this_cpu_add(kernel_cpustat.cpustat[index],?tmp);
}
登錄后復(fù)制

3.3 空閑時間的累積

沒錯,在內(nèi)核變量 kernel_cpustat 中不僅僅是統(tǒng)計了各種用戶態(tài)、內(nèi)核態(tài)的使用時間,空閑也一并統(tǒng)計起來了。

如果在采樣的瞬間,cpu 既不在內(nèi)核態(tài)也不在用戶態(tài)的話,就將當(dāng)前節(jié)拍的時間都累加到 idle 中。

//file:kernel/sched/cputime.c
void?account_idle_time(u64?cputime)
{
?u64?*cpustat?=?kcpustat_this_cpu->cpustat;
?struct?rq?*rq?=?this_rq();

?if?(atomic_read(&rq->nr_iowait)?>?0)
??cpustat[CPUTIME_IOWAIT]?+=?cputime;
?else
??cpustat[CPUTIME_IDLE]?+=?cputime;
}
登錄后復(fù)制

在 cpu 空閑的情況下,進(jìn)一步判斷當(dāng)前是不是在等待 IO(例如磁盤 IO),如果是的話這段空閑時間會加到 iowait 中,否則就加到 idle 中。從這里,我們可以看到 iowait 其實是 cpu 的空閑時間,只不過是在等待 IO 完成而已。

看到這里,開篇問題 3 也有非常明確的答案了,io wait 其實是 cpu 在空閑狀態(tài)的一項統(tǒng)計,只不過這種狀態(tài)和 idle 的區(qū)別是 cpu 是因為等待 io 而空閑。

四、總結(jié)

本文深入分析了 Linux 統(tǒng)計系統(tǒng) CPU 利用率的內(nèi)部原理。全文的內(nèi)容可以用如下一張圖來匯總:

Linux 中 CPU 利用率是如何算出來的?

Linux 中的定時器會以某個固定節(jié)拍,比如 1 ms 一次采樣各個 cpu 核的使用情況,然后將當(dāng)前節(jié)拍的所有時間都累加到 user/nice/system/irq/softirq/io_wait/idle 中的某一項上。

top 命令是讀取的 /proc/stat 中輸出的 cpu 各項利用率數(shù)據(jù),而這個數(shù)據(jù)在內(nèi)核中是根據(jù) kernel_cpustat 來匯總并輸出的。

回到開篇問題 1,top 輸出的利用率信息是如何計算出來的,它精確嗎?

/proc/stat 文件輸出的是某個時間點的各個指標(biāo)所占用的節(jié)拍數(shù)。如果想像 top 那樣輸出一個百分比,計算過程是分兩個時間點 t1, t2 分別獲取一下 stat 文件中的相關(guān)輸出,然后經(jīng)過個簡單的算術(shù)運算便可以算出當(dāng)前的 cpu 利用率。

再說是否精確。這個統(tǒng)計方法是采樣的,只要是采樣,肯定就不是百分之百精確。但由于我們查看 cpu 使用率的時候往往都是計算 1 秒甚至更長一段時間的使用情況,這其中會包含很多采樣點,所以查看整體情況是問題不大的。

另外從本文,我們也學(xué)到了 top 中輸出的 cpu 時間項目其實大致可以分為三類:

第****一類:用戶態(tài)消耗時間,包括 user 和 nice。如果想看用戶態(tài)的消耗,要將 user 和 nice 加起來看才對。

第二類:內(nèi)核態(tài)消耗時間,包括 irq、softirq 和 system。

第三類:空閑時間,包括 io_wait 和 idle。其中 io_wait 也是 cpu 的空閑狀態(tài),只不過是在等 io 完成而已。如果只是想看 cpu 到底有多閑,應(yīng)該把 io_wait 和 idle 加起來才對。


以上就是Linux 中 CPU 利用率是如何算出來的?的詳細(xì)內(nèi)容,更多請關(guān)注php中文網(wǎng)其它相關(guān)文章!

最佳 Windows 性能的頂級免費優(yōu)化軟件
最佳 Windows 性能的頂級免費優(yōu)化軟件

每個人都需要一臺速度更快、更穩(wěn)定的 PC。隨著時間的推移,垃圾文件、舊注冊表數(shù)據(jù)和不必要的后臺進(jìn)程會占用資源并降低性能。幸運的是,許多工具可以讓 Windows 保持平穩(wěn)運行。

下載
相關(guān)標(biāo)簽:
來源:良許Linux教程網(wǎng)網(wǎng)
本文內(nèi)容由網(wǎng)友自發(fā)貢獻(xiàn),版權(quán)歸原作者所有,本站不承擔(dān)相應(yīng)法律責(zé)任。如您發(fā)現(xiàn)有涉嫌抄襲侵權(quán)的內(nèi)容,請聯(lián)系admin@php.cn
最新問題
開源免費商場系統(tǒng)廣告
最新下載
更多>
網(wǎng)站特效
網(wǎng)站源碼
網(wǎng)站素材
前端模板
關(guān)于我們 免責(zé)申明 意見反饋 講師合作 廣告合作 最新更新
php中文網(wǎng):公益在線php培訓(xùn),幫助PHP學(xué)習(xí)者快速成長!
關(guān)注服務(wù)號 技術(shù)交流群
PHP中文網(wǎng)訂閱號
每天精選資源文章推送
PHP中文網(wǎng)APP
隨時隨地碎片化學(xué)習(xí)
PHP中文網(wǎng)抖音號
發(fā)現(xiàn)有趣的

Copyright 2014-2025 http://www.miracleart.cn/ All Rights Reserved | php.cn | 湘ICP備2023035733號