【原創】(二)Linux程序排程器-CPU負載
背景
Read the fucking source code!
--By 魯迅A picture is worth a thousand words.
--By 高爾基
說明:
- Kernel版本:4.14
- ARM64處理器,Contex-A53,雙核
- 使用工具:Source Insight 3.5, Visio
1. 概述
CPU負載(cpu load
)指的是某個時間點程序對系統產生的壓力。
來張圖來類比下(參考Understanding Linux CPU Load)
- CPU的執行能力,就如大橋的通行能力,分別有滿負荷,非滿負荷,超負荷等狀態,這幾種狀態對應不同的cpu load值;
- 單CPU滿負荷執行時cpu_load為1,當多個CPU或多核時,相當於大橋有多個車道,滿負荷執行時cpu_load值為CPU數或多核數;
- CPU負載的計算(以單CPU為例),假設一分鐘內執行10個任務代表滿負荷,當一分鐘給出30個任務時,CPU只能處理10個,剩餘20個不能處理,cpu_load=3;
在實際系統中檢視:
cat /proc/cpuinfo
:檢視CPU資訊;cat /proc/loadavg
:檢視cpu最近1/5/15分鐘的平均負載:
計算CPU負載,可以讓排程器更好的進行負載均衡處理,以便提高系統的執行效率。此外,核心中的其他子系統也可以參考這些CPU負載值來進行相應的調整,比如DVFS
目前核心中,有以下幾種方式來跟蹤CPU負載:
- 全域性CPU平均負載;
- 執行佇列CPU負載;
PELT(per entity load tracking)
;
這也是本文需要探討的內容,開始吧。
2. 全域性CPU平均負載
2.1 基礎概念
先來明確兩個與CPU負載計算相關的概念:
active task(活動任務)
:只有知道活動任務數量,才能計算CPU負載,而活動任務包括了TASK_RUNNING
和TASK_UNINTERRUPTIBLE
兩類任務。包含TASK_UNINTERRUPTIBLE
任務的原因是,這類任務經常是在等待I/O請求,將其包含在內也合理;NO_HZ
:我們都知道Linux核心每隔固定時間發出timer interrupt
HZ
是用來定義1秒中的timer interrupts
次數,HZ
的倒數是tick
,是系統的節拍器,每個tick
會處理包括排程器、時間管理、定時器等事務。週期性的時鐘中斷帶來的問題是,不管CPU空閒或繁忙都會觸發,會帶來額外的系統損耗,因此引入了NO_HZ
模式,可以在CPU空閒時將週期性時鐘關掉。在NO_HZ
期間,活動任務數量的改變也需要考慮,而它的計算不如週期性時鐘模式下直觀。
2.2 流程
Linux核心中定義了三個全域性變數值avenrun[3]
,用於存放最近1/5/15分鐘的平均CPU負載。
看一下計算流程:
- 計算活動任務數,這個包括兩部分:1)週期性排程中新增加的活動任務;2)在
NO_HZ
期間增加的活動任務數; - 根據活動任務數值,再結合全域性變數值
avenrun[]
中的old value
,來計算新的CPU負載值,並最終替換掉avenrun[]
中的值; - 系統預設每隔5秒鐘會計算一次負載,如果由於
NO_HZ
空閒而錯過了下一個CPU負載的計算週期,則需要再次進行更新。比如NO_HZ
空閒20秒而無法更新CPU負載,前5秒負載已經更新,需要計算剩餘的3個計算週期的負載來繼續更新;
2.3 計算方法
Linux核心中,採用11位精度的定點化計算,CPU負載1.0由整數2048表示,巨集定義如下:
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ+1) /* 5 sec intervals */
#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5 2014 /* 1/exp(5sec/5min) */
#define EXP_15 2037 /* 1/exp(5sec/15min) */
計算公式如下:
load
值為舊的CPU負載值avenrun[]
,整個計算完成後得到新的負載值,再更新avenrun[]
;EXP_1/EXP_5/EXP_15
,分別代表最近1/5/15分鐘的定點化值的指數因子;active
值,根據讀取calc_load_tasks
的值來判斷,大於0則乘以FIXED_1(2048)
傳入;- 根據
active
和load
值的大小關係來決定是否需要加1,類似於四捨五入的機制;
關鍵程式碼如下:
active = atomic_long_read(&calc_load_tasks);
active = active > 0 ? active * FIXED_1 : 0;
avenrun[0] = calc_load(avenrun[0], EXP_1, active);
avenrun[1] = calc_load(avenrun[1], EXP_5, active);
avenrun[2] = calc_load(avenrun[2], EXP_15, active);
NO_HZ
模式下活動任務數量更改的計算
由於NO_HZ
空閒效應而更改的CPU活動任務數量,存放在全域性變數calc_load_nohz[2]
中,並且每5秒計算週期交替更換一次儲存位置(calc_load_read_idx/calc_load_write_idx
),其他程式可以去讀取最近5秒內的活動任務變化的增量值。
計算示例
假設在某個CPU上,開始計算時load=0.5
,根據calc_load_tasks
值獲取不同的active
,中間進入NO_HZ
模式空閒了20秒,整個計算的值如下圖:
3. 執行佇列CPU負載
- Linux系統會計算每個tick的平均CPU負載,並將其儲存在執行佇列中
rq->cpu_load[5]
,用於負載均衡;
下圖顯示了計算執行佇列的CPU負載的處理流程:
最終通過cpu_load_update
來計算,邏輯如下:
- 其中傳入的
this_load
值,為執行佇列現有的平均負載值。
上圖中的衰減因子,是在NO_HZ
模式下去進行計算的。在沒有使用tick時,從預先計算的表中計算負載值。Linux核心中定義了兩個全域性變數:
#define DEGRADE_SHIFT 7
static const u8 degrade_zero_ticks[CPU_LOAD_IDX_MAX] = {0, 8, 32, 64, 128};
static const u8 degrade_factor[CPU_LOAD_IDX_MAX][DEGRADE_SHIFT + 1] = {
{ 0, 0, 0, 0, 0, 0, 0, 0 },
{ 64, 32, 8, 0, 0, 0, 0, 0 },
{ 96, 72, 40, 12, 1, 0, 0, 0 },
{ 112, 98, 75, 43, 15, 1, 0, 0 },
{ 120, 112, 98, 76, 45, 16, 2, 0 }
};
衰減因子的計算主要是在delay_load_missed()
函式中完成,該函式會返回load * 衰減因子
的值,作為上圖中的old_load
。
計算方式如下:
4. PELT
PELT, Per-entity load tracking
。在Linux引入PELT
之前,CFS排程器
在計算CPU負載時,通過跟蹤每個執行佇列上的負載來計算;在引入PELT
之後,通過跟蹤每個排程實體的負載貢獻來計算。(其中,排程實體:指task或task_group
)
4.1 PELT計算方法
總體的計算思路:
將排程實體的可執行狀態時間(正在執行+等待CPU排程執行),按1024us
劃分成不同的週期,計算每個週期內該排程實體對系統負載的貢獻,最後完成累加。其中,每個計算週期,隨著時間的推移,需要乘以衰減因子y進行一次衰減操作。
先來看一下每個排程實體的負載貢獻計算公式:
- 當前時間點的負載貢獻 = 當前時間點負載 + 上個週期負載貢獻 * 衰減因子;
- 假設一個排程實體被排程執行,執行時間段可以分成三個段
d1/d2/d3
,這三個段是被1024us
的計算週期分割而成,period_contrib
是排程實體last_update_time
時在計算週期間的貢獻值,; - 總體的貢獻值,也是根據
d1/d2/d3
來分段計算,最終相加即可; y
為衰減因子,每隔1024us
就乘以y來衰減一次;
計算的呼叫流程如下圖:
- 函式主要是計算時間差,再分成d1/d2/d3來分段計算處理,最終更新相應的欄位;
decay_load
函式要計算val * y^n
,核心提供了一張表來避免浮點運算,值儲存在runnable_avg_yN_inv
陣列中;
static const u32 runnable_avg_yN_inv[] = {
0xffffffff, 0xfa83b2da, 0xf5257d14, 0xefe4b99a, 0xeac0c6e6, 0xe5b906e6,
0xe0ccdeeb, 0xdbfbb796, 0xd744fcc9, 0xd2a81d91, 0xce248c14, 0xc9b9bd85,
0xc5672a10, 0xc12c4cc9, 0xbd08a39e, 0xb8fbaf46, 0xb504f333, 0xb123f581,
0xad583ee9, 0xa9a15ab4, 0xa5fed6a9, 0xa2704302, 0x9ef5325f, 0x9b8d39b9,
0x9837f050, 0x94f4efa8, 0x91c3d373, 0x8ea4398a, 0x8b95c1e3, 0x88980e80,
0x85aac367, 0x82cd8698,
};
Linux中使用struct sched_avg
來記錄排程實體和CFS執行佇列的負載資訊,因此struct sched_entity
和struct cfs_rq
結構體中,都包含了struct sched_avg
,欄位介紹如下:
struct sched_avg {
u64 last_update_time; //上一次負載更新的時間,主要用於計算時間差;
u64 load_sum; //可執行時間帶來的負載貢獻總和,包括等待排程時間和正在執行時間;
u32 util_sum; //正在執行時間帶來的負載貢獻總和;
u32 period_contrib; //上一次負載更新時,對1024求餘的值;
unsigned long load_avg; //可執行時間的平均負載貢獻;
unsigned long util_avg; //正在執行時間的平均負載貢獻;
};
4.2 PELT計算呼叫
PELT
計算的發生時機如下圖所示:
- 排程實體的相關操作,包括入列出列操作,都會進行負載貢獻的計算;
PELT
的演算法還在持續的改進中,各個核心版本也存在差異,大體的思路已經在上文中介紹到了,細節就不再深入分析了。
相關推薦
【原創】(二)Linux程序排程器-CPU負載
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(一)Linux程序排程器-基礎
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux程序排程器-程序切換
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux程序排程-組排程及頻寬控制
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(五)Linux程序排程-CFS排程器
# 背景 - `Read the fucking source code!` --By 魯迅 - `A picture is worth a thousand words.` --By 高爾基 說明: 1. Kernel版本:4.14 2. ARM64處理器,Contex-A53,雙核 3. 使用工具:S
【原創】(六)Linux程序排程-實時排程器
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(三)Linux paging_init解析
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(四)Linux記憶體模型之Sparse Memory Model
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(六)Linux記憶體管理 - zoned page frame allocator - 1
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(七)Linux記憶體管理 - zoned page frame allocator - 2
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(八)Linux記憶體管理 - zoned page frame allocator - 3
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(九)Linux記憶體管理 - zoned page frame allocator - 4
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【原創】(十三)Linux記憶體管理之vma/malloc/mmap
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
【ElasticSearch】(二)目前我對ES的應用場景
【前提】 目前公司訂單資料庫雖然使用了索引、快取、讀寫分離等,由於業務本身複雜以及資料量的不斷上漲,導致查詢出現了瓶頸,海量資料查詢對於APP有時候甚至出現超時現象,不得不對這種情況做出處理。其中可行解決方案比如:分庫分表、使用Elastic
計算機網路學習【入門】——(二)物理層
第二章 物理層 一、物理層定義的標準 物理層解決如何在連線各種計算機的傳輸媒體上傳輸資料位元流,而不是指具體的傳輸媒體。 1.物理層的主要任務描述為:確定傳輸媒體的介面的一些特性,即: 機械特性:比如網線的介面形狀,大小,引線數目 電氣特性:例規定電壓
C入門【五】(二)
3.編寫程式碼模擬三次密碼輸入的場景 且不得超過三次 //需求如題所示模擬登陸時 上次失敗則自動“封號“ 題目難度不大,主要考慮迴圈條件何時退出 #define _CRT_SECURE_NO_WARNINGS 1 #include "stdio.h" #include "stdlib
【Spring】(二)使用Spring進行事務管理的幾種方式
Spring既是SSH中的一員,也是SSM中的一員。不管是在SSH,還是在SSM中,Spring都起到了十分重要的“管理”作用,不論是對action的管理,還是對Controller的管理
【YOLO】(二):Yolo_mark使用教程
一、獲取原始碼 首先,從github上拉取程式碼。 git clone [email protected]:AlexeyAB/Yolo_mark.git 二、編譯可執行檔案 然後用visual studio開啟yolo_mark.sln,配置正確的openc
【原創】(十二)Linux記憶體管理之vmap與vmalloc
背景 Read the fucking source code! --By 魯迅 A picture is worth a thousand words. --By 高爾基 說明: Kernel版本:4.14 ARM64處理器,Contex-A53,雙核 使用工具:Source Insight 3.5,
教老婆學Linux運維(二)Linux常用命令指南【上】
目錄 教老婆學Linux(二)Linux常用命令指南【上】 一、概述 二、常用命令 教老婆學Linux(二)Linux常用命令指南【上】 作者:姚毛毛的部落格 tips:文