歡迎光臨
每天分享高質量文章

CFS頻寬控制學習(1)-入門

背景

從這篇文章

https://mp.weixin.qq.com/s/1U1-qd-rlc1xHHsvd5l8Kg中可以瞭解到Kubernetes的資源模型透過Cgroups的相關子系統實現,其中對於CPU來說,它的Limits設定依賴於cfs.cpu_quota_us和cfs.cpu_period_us兩個引數,而說起兩個引數就不得不提起CFS頻寬控制了。

CFS頻寬控制
0
1
什麼是CFS頻寬控制
在瞭解CFS頻寬控制之前,首先要知道的是CPU是一種可被管理的資源,也就說它是可以被精細化分配的資源。CFS頻寬控制在很多場景裡都很有用,一方面在它出現之前,經常會發生某個任務使用過量CPU導致其它任務出現不可接受的延遲或者抖動的現象。另一方面比如在一個多租戶的場景裡,系統(雲平臺)要按照使用者對各種資源的使用量進行收費,這時就需要對CPU的上限進行控制,以免使用者超額使用。

 

為了實現這個CPU頻寬控制的標的,Paul Turner和Bharata Rao先後提出了對CFS頻寬控制的核心補丁,在CFS bandwidth control中對他們的工作有相關的描述。按照CFS Bandwidth Control的描述,CFS頻寬控制是用來限制一個任務組能夠消耗的CPU的上限,具體來說就是透過設定period和quota,指定在period時間裡該任務組最多能夠消耗quota的CPU時間。

 

0
2
技術原理
在談到具體實現之前,還需要明確幾個任務排程相關的概念。從2.6內核以後,為了緩解多使用者下行程排程不公平的情況(舉個例子:A使用者有個實時行程,B使用者有個非互動行程,因為A的優先順序比較高,那麼A佔用了大量的CPU時間,那麼對B就不夠公平)被排程的物件就從單一行程變成了任務組,這樣一來可以每個使用者對應一個任務組,從而分配得到一定的CPU時間,在這個組裡不同的任務再去獲取相應的CPU時間。這樣的任務組叫做task_group,

定義在kernel/sched/sched.h:363中:

struct task_group {#ifdef CONFIG_FAIR_GROUP_SCHED // 由該引數控制該特性
/* schedulable entities of this group on each CPU */struct sched_entity **se; // 執行物體/* runqueue "owned" by this group on each CPU */struct cfs_rq       **cfs_rq;#endifstruct cfs_bandwidth    cfs_bandwidth; // CPU頻寬}

其中包含了一個型別為sched_entity的成員。sched_entity也即排程物體,它可以是行程,也可以是行程組,甚至使用者。這些排程物體按照vruntime時間序,以紅黑樹的形式組織,並由cfs_rq管理,同樣是task_group的欄位,它的定義在:kernel/sched/sched.h:488:

struct cfs_rq {#ifdef CONFIG_FAIR_GROUP_SCHEDstruct rq       *rq;    /* CPU runqueue to which this cfs_rq is attached */};

它包含了一個rq結構的成員,這是該cfs_rq附著到每個CPU run queue的指標。

 

在回到task_group的定義裡,還可以看到cfs_bandwidth的結構體成員,它的定義在:`kernel/sched/sched.h:337`:

struct cfs_bandwidth {#ifdef CONFIG_CFS_BANDWIDTHktime_t     period;   // periodu64     quota;        // quota#endif};

可以看到period和quota正是cfs_bandwidth的成員。那麼綜上所述,CPU頻寬控制的物件是task_group,配置好的period和quota都是作用在task_group上,然後再分配給task_group下的cfs_rq。可以以下圖作為示例:

 

進一步說,可以認為給task_group分配的quota是一個global quota pool,然後假定每個cfs_rq又擁有一個local quota pool,那麼cfs_rq要從global quota pool中申請一定數量的CPU時間,這個時間叫做slice,可以透過/proc/sys/kernel/sched_cfs_bandwidth_slice_us,目前預設值為5ms。見下圖:

 

當某個cfs_rq申請到slice以後就會開始執行,當前slice消耗完後就會繼續申請。當在一個period的時間global quota pool已經分配乾凈,此時該cfs_rq就會進入一個叫做throttled的狀態,在這個狀態中任務因為無法獲取CPU時間所以無法被執行。這個throttle的統計狀態可以透過cpu.stat cgroup進行檢視。當該period結束後,global quota pool會被掃清並可以分配slice,如此進入下一個週期。

 

那麼回到前面所說的cfs_bandwidth定義裡:

struct cfs_bandwidth {#ifdef CONFIG_CFS_BANDWIDTHktime_t     period;   // periodu64     quota;        // quotau64     runtime;      // 剩餘時間,隨任務執行減少
struct hrtimer    period_timer; // 高精度週期定時器,與period時間相等struct hrtimer    slack_timer;  // 實現延遲歸還struct list_head  throttled_cfs_rq;/* Statistics: */                // cpu.stat的傳回值int     nr_periods;          // 經歷的時段數,單位為次數int     nr_throttled;        // 發生throttle的次數u64     throttled_time;      // 被throttle的時間總和#endif};

可以瞭解到period的週期性由一個高精度定時器period_timer實現,被throttle的cfs_rq會掛到throttle_cfs_rq連結串列中。runtime的值最初跟quota一樣,但隨著任務執行會逐步減少。cpu.stats的值來自廈門的nr_periods、nr_throttled和throttled_time。

0
3
實驗測試
在Google Cloud上申請一臺單核的伺服器進行測試,程式碼採用

[cfs.go(https://gist.github.com/bobrik/2030ff040fad360327a5fab7a09c4ff1#file-cfs-go)。

這段程式碼的主要功能是執行一個可以造成CPU密集的sha512加密演演算法,並執行5ms,然後進入sleep狀態,待sleep結束以後繼續進行sha512加密,如此迴圈。透過引數可以配置sleep的時間和迴圈次數iteration。

 

為了方便測試,採用go 1.12.5的Docker映象進行測試,首先看一下不開啟頻寬控制的時候,執行下麵的命令:

`docker run –rm -it -v $(pwd):$(pwd) -w $(pwd) golang:1.12.5 go run cfs.go -iterations 20 -sleep 10ms`

得到結果:

 

可以從”burn took”看出來執行加密只需要5ms的時間,跟程式碼裡寫的是一樣的。

 

然後設定quota為25ms,period為100ms,程式slice是系統預設的5ms。比如執行下麵的命令:

docker run –rm -it –cpu-quota 25000 –cpu-period 100000 -v $(pwd):$(pwd) -w $(pwd) golang:1.12.5 go run cfs.go -iterations 20 -sleep 10ms

得到結果:

 

這個時候就會看到”burn took”有的時候會花費25ms,這個時間是怎麼來的?當任務開始執行的時候,先執行5ms,然後睡眠10ms,然後又執行5ms                                                                                   

 

 

也就是說當25ms的quota耗盡的時候,總共經歷了75ms(忽略第一個period)。此時由於quota已經耗盡,所以無法執行。然後從這一刻起直到period要掃清的第200ms,該cfs_rq就處於throttle的狀態。這部分throttle的時間剛好就是25ms。

 

註1:相關程式碼均參考核心5.1版本原始碼

註2:因學習需要略過部分不相關的程式碼

0
4參考資料
 

[CFS Bandwidth Control]

(https://www.kernel.org/doc/Documentation/scheduler/sched-bwc.txt)

 

[CPU bandwidth control for CFS]

(https://landley.net/kdocs/ols/2010/ols2010-pages-245-254.pdf)

 

[CFS bandwidth control]

(https://lwn.net/Articles/428230/)


    已同步到看一看
    贊(0)

    分享創造快樂