搜尋
首頁系統教程LinuxLinux 中 CPU 使用率是如何算出來的?
Linux 中 CPU 使用率是如何算出來的?Feb 15, 2024 am 11:15 AM
linuxlinux教程linux系統linux指令shell腳本overflow嵌入式linuxlinux入門linux學習

在線上伺服器觀察線上服務運作狀態的時候,絕大多數人都是喜歡先用 top 指令看看目前系統的整體 cpu 使用率。例如,隨手拿來的一台機器,top 指令顯示的使用率資訊如下:

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

這個輸出結果說簡單也簡單,說複雜也不是那麼容易就能全部搞懂的。例如:

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

今天我們對 cpu 使用率統計進行深入的學習。透過現今的學習,你不僅能了解 cpu 使用率統計實現細節,還能對 nice、io wait 等指標有更深入的理解。

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

一、先思考一下

#拋開 Linux 的實作先不談,如果有以下需求,有一個四核心伺服器,上面跑了四個進程。

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

讓你來設計一個計算整個系統 cpu 使用率的這個需求,支援像 top 指令這樣的輸出,滿足以下要求:

  • cpu 使用率要盡可能準確;
  • # 要盡可能體現秒級瞬時 cpu 狀態。

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

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

好,思考結束。經過思考你會發現,這個看起來很簡單的需求,實際上還是有點小複雜的。

其中一個想法是把所有行程的執行時間加起來,然後再除以系統執行總時間*4。

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

這個想法是沒問題的,用這種方法統計很長一段時間內的 cpu 使用率是可以的,統計也足夠的準確。

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

上一個思路問題核心就是如何解決瞬時問題。提到瞬時狀態,你可能就又來思路了。那我就用瞬時採樣去看,看看目前有幾個核在忙。四個核如果有兩個核在忙,那麼利用率就是 50%。

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

  • 你算出的數字都是 25% 的整數倍;
  • # 這個瞬時值會導致 cpu 使用率顯示的劇烈震盪。

例如下圖:

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

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

我們再改進一下它,把上面兩個想法結合起來,可能就能解決我們的問題了。在取樣上,我們把週期定得細一些,但在計算上我們把週期定得粗一些。

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

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

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

可能有同學會問了,假如 cpu 在兩次取樣中間發生變化了呢,如下圖這種情況。

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

在目前取樣點到來的時候,流程 A 其實剛執行完,有一點點時間既沒被上一個取樣點統計到,本次也統計不到。對於進程 B,其實只開始了一小段時間,把 1 ms 全記上似乎有點多記了。

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

事實上,Linux 也就是這樣來統計系統 cpu 使用率的。雖然可能會有誤差,但作為一項統計數據使用已經是足夠了的。在實作上,Linux 是將所有的瞬時值都累積到某一個資料上的,而不是真的存了很多份的瞬時資料。

接下來就讓我們進入 Linux 來查看它對系統 cpu 使用率統計的具體實作。

二、top 指令使用資料在哪裡

#上一節我們說的 Linux 在實作上是將瞬時值都累積到某一個資料上的,這個值是核心透過 /proc/stat 偽檔案來對使用者態暴露。 Linux 在計算系統 cpu 使用率的時候用的就是它。

整體來看,top 指令工作的內部細節如下圖所示。

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

#top 指令存取 /proc/stat 取得各項 cpu 使用率使用值;

  • 核心呼叫 stat_open 函數來處理對 /proc/stat 的存取;

  • 核心存取的資料來自 kernel_cpustat 數組,並彙總;

  • 列印輸出給使用者狀態。

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

透過使用 strace 追蹤 top 指令的各種系統調用,可以看到它對該檔案的調用。

# 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
...

#除了 /proc/stat 外,還有各個行程細分的 /proc/{pid}/stat,是用來計算各個行程的 cpu 使用率時所使用的。

#核心為各個偽檔案都定義了處理函數,/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,
 ...
};

proc_stat_operations 中包含了該檔案對應的操作方法。當開啟 /proc/stat 檔案的時候,stat_open 就會被呼叫到。 stat_open 依序呼叫 single_open_size,show_stat 來輸出資料內容。讓我們來看看它的程式碼:

//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];
  ...
 }

 //转换成节拍数并打印出来
 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));
 ...
}

在上面的程式碼中,for_each_possible_cpu 是在遍歷儲存 cpu 使用率資料的 kcpustat_cpu 變數。這個變數是一個 percpu 變量,它為每個邏輯核都準備了一個數組元素。裡面存放著目前核所對應各種事件,包括 user、nice、system、idel、iowait、irq、softirq 等。

在這個循環中,將每一個核的每種使用率加起來。最後透過 seq_put_decimal_ull 將這些資料輸出出來。

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

注意,在核心中實際每個時間記錄的是納秒數,但是在輸出的時候統一都轉換成了節拍單位。至於節拍單位多長,下一節我們介紹。總之, /proc/stat 的輸出是從 kernel_cpustat 這個 percpu 變數讀取出來的。

我們接著再看看這個變數中的資料是何時加進來的。

三、统计数据怎么来的

前面我们提到内核是以采样的方式来统计 cpu 使用率的。这个采样周期依赖的是 Linux 时间子系统中的定时器。

Linux 内核每隔固定周期会发出 timer interrupt (IRQ 0),这有点像乐谱中的节拍的概念。每隔一段时间,就打出一个拍子,Linux 就响应之并处理一些事情。

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

一个节拍的长度是多长时间,是通过 CONFIG_HZ 来定义的。它定义的方式是每一秒有几次 timer interrupts。不同的系统中这个节拍的大小可能不同,通常在 1 ms 到 10 ms 之间。可以在自己的 Linux config 文件中找到它的配置。

# grep ^CONFIG_HZ /boot/config-5.4.56.bsk.10-amd64
CONFIG_HZ=1000

从上述结果中可以看出,我的机器每秒要打出 1000 次节拍。也就是每 1 ms 一次。

每次当时间中断到来的时候,都会调用 update_process_times 来更新系统时间。更新后的时间都存储在我们前面提到的 percpu 变量 kcpustat_cpu 中。

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

我们来详细看下汇总过程 update_process_times 的源码,它位于 kernel/time/timer.c 文件中。

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

 //进行时间累积处理
 account_process_tick(p, user_tick);
 ...
}

这个函数的参数 user_tick 指的是采样的瞬间是处于内核态还是用户态。接下来调用 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 统计用户态时间
  account_user_time(p, cputime);
 else if ((p != rq->idle) || (irq_count() != HARDIRQ_OFFSET))
  //3.2 统计内核态时间
  account_system_time(p, HARDIRQ_OFFSET, cputime);
 else
  //3.3 统计空闲时间
  account_idle_time(cputime);
}

在这个函数中,首先设置 cputime = TICK_NSEC, 一个 TICK_NSEC 的定义是一个节拍所占的纳秒数。接下来根据判断结果分别执行 account_user_time、account_system_time 和 account_idle_time 来统计用户态、内核态和空闲时间。

3.1 用户态时间统计

//file:kernel/sched/cputime.c
void account_user_time(struct task_struct *p, u64 cputime)
{
 //分两种种情况统计用户态 CPU 的使用情况
 int index;
 index = (task_nice(p) > 0) ? CPUTIME_NICE : CPUTIME_USER;

 //将时间累积到 /proc/stat 中
 task_group_account_field(p, index, cputime);
 ......
}

account_user_time 函数主要分两种情况统计:

  • 如果进程的 nice 值大于 0,那么将会增加到 CPU 统计结构的 nice 字段中。
  • 如果进程的 nice 值小于等于 0,那么增加到 CPU 统计结构的 user 字段中。

看到这里,开篇的问题 2 就有答案了,其实用户态的时间不只是 user 字段,nice 也是。之所以要把 nice 分出来,是为了让 Linux 用户更一目了然地看到调过 nice 的进程所占的 cpu 周期有多少。

我们平时如果想要观察系统的用户态消耗的时间的话,应该是将 top 中输出的 user 和 nice 加起来一并考虑,而不是只看 user!

接着调用 task_group_account_field 来把时间加到前面我们用到的 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);
 ...
}

3.2 内核态时间统计

我们再来看内核态时间是如何统计的,找到 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);
}

内核态的时间主要分 3 种情况进行统计。

  • 如果当前处于硬中断执行上下文, 那么统计到 irq 字段中;
  • 如果当前处于软中断执行上下文, 那么统计到 softirq 字段中;
  • 否则统计到 system 字段中。

判断好要加到哪个统计项中后,依次调用 account_system_index_time、task_group_account_field 来将这段时间加到内核变量 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);
}

3.3 空闲时间的累积

没错,在内核变量 kernel_cpustat 中不仅仅是统计了各种用户态、内核态的使用时间,空闲也一并统计起来了。

如果在采样的瞬间,cpu 既不在内核态也不在用户态的话,就将当前节拍的时间都累加到 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;
}

在 cpu 空闲的情况下,进一步判断当前是不是在等待 IO(例如磁盘 IO),如果是的话这段空闲时间会加到 iowait 中,否则就加到 idle 中。从这里,我们可以看到 iowait 其实是 cpu 的空闲时间,只不过是在等待 IO 完成而已。

看到这里,开篇问题 3 也有非常明确的答案了,io wait 其实是 cpu 在空闲状态的一项统计,只不过这种状态和 idle 的区别是 cpu 是因为等待 io 而空闲。

四、总结

本文深入分析了 Linux 统计系统 CPU 利用率的内部原理。全文的内容可以用如下一张图来汇总:

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

Linux 中的定时器会以某个固定节拍,比如 1 ms 一次采样各个 cpu 核的使用情况,然后将当前节拍的所有时间都累加到 user/nice/system/irq/softirq/io_wait/idle 中的某一项上。

top 命令是读取的 /proc/stat 中输出的 cpu 各项利用率数据,而这个数据在内核中是根据 kernel_cpustat 来汇总并输出的。

回到开篇问题 1,top 输出的利用率信息是如何计算出来的,它精确吗?

/proc/stat 文件输出的是某个时间点的各个指标所占用的节拍数。如果想像 top 那样输出一个百分比,计算过程是分两个时间点 t1, t2 分别获取一下 stat 文件中的相关输出,然后经过个简单的算术运算便可以算出当前的 cpu 利用率。

再说是否精确。这个统计方法是采样的,只要是采样,肯定就不是百分之百精确。但由于我们查看 cpu 使用率的时候往往都是计算 1 秒甚至更长一段时间的使用情况,这其中会包含很多采样点,所以查看整体情况是问题不大的。

另外从本文,我们也学到了 top 中输出的 cpu 时间项目其实大致可以分为三类:

第****一类:用户态消耗时间,包括 user 和 nice。如果想看用户态的消耗,要将 user 和 nice 加起来看才对。

第二类:内核态消耗时间,包括 irq、softirq 和 system。

第三类:空闲时间,包括 io_wait 和 idle。其中 io_wait 也是 cpu 的空闲状态,只不过是在等 io 完成而已。如果只是想看 cpu 到底有多闲,应该把 io_wait 和 idle 加起来才对。


以上是Linux 中 CPU 使用率是如何算出來的?的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:良许Linux教程网。如有侵權,請聯絡admin@php.cn刪除
什么是linux设备节点什么是linux设备节点Apr 18, 2022 pm 08:10 PM

linux设备节点是应用程序和设备驱动程序沟通的一个桥梁;设备节点被创建在“/dev”,是连接内核与用户层的枢纽,相当于硬盘的inode一样的东西,记录了硬件设备的位置和信息。设备节点使用户可以与内核进行硬件的沟通,读写设备以及其他的操作。

Linux中open和fopen的区别有哪些Linux中open和fopen的区别有哪些Apr 29, 2022 pm 06:57 PM

区别:1、open是UNIX系统调用函数,而fopen是ANSIC标准中的C语言库函数;2、open的移植性没fopen好;3、fopen只能操纵普通正规文件,而open可以操作普通文件、网络套接字等;4、open无缓冲,fopen有缓冲。

linux怎么判断pcre是否安装linux怎么判断pcre是否安装May 09, 2022 pm 04:14 PM

在linux中,可以利用“rpm -qa pcre”命令判断pcre是否安装;rpm命令专门用于管理各项套件,使用该命令后,若结果中出现pcre的版本信息,则表示pcre已经安装,若没有出现版本信息,则表示没有安装pcre。

linux中eof是什么linux中eof是什么May 07, 2022 pm 04:26 PM

在linux中,eof是自定义终止符,是“END Of File”的缩写;因为是自定义的终止符,所以eof就不是固定的,可以随意的设置别名,linux中按“ctrl+d”就代表eof,eof一般会配合cat命令用于多行文本输出,指文件末尾。

linux中什么叫端口映射linux中什么叫端口映射May 09, 2022 pm 01:49 PM

端口映射又称端口转发,是指将外部主机的IP地址的端口映射到Intranet中的一台计算机,当用户访问外网IP的这个端口时,服务器自动将请求映射到对应局域网内部的机器上;可以通过使用动态或固定的公共网络IP路由ADSL宽带路由器来实现。

linux怎么查询mac地址linux怎么查询mac地址Apr 24, 2022 pm 08:01 PM

linux查询mac地址的方法:1、打开系统,在桌面中点击鼠标右键,选择“打开终端”;2、在终端中,执行“ifconfig”命令,查看输出结果,在输出信息第四行中紧跟“ether”单词后的字符串就是mac地址。

手机远程linux工具有哪些手机远程linux工具有哪些Apr 29, 2022 pm 05:30 PM

手机远程linux工具有:1、JuiceSSH,是一款功能强大的安卓SSH客户端应用,可直接对linux服务进行管理;2、Termius,可以利用手机来连接Linux服务器;3、Termux,一个强大的远程终端工具;4、向日葵远程控制等等。

linux中lsb是什么意思linux中lsb是什么意思May 07, 2022 pm 05:08 PM

linux中,lsb是linux标准基础的意思,是“Linux Standards Base”的缩写,是linux标准化领域中的标准;lsb制定了应用程序与运行环境之间的二进制接口,保证了linux发行版与linux应用程序之间的良好结合。

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
3 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

WebStorm Mac版

WebStorm Mac版

好用的JavaScript開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版