1. 程式人生 > >程序管理筆記三、完全公平排程演算法CFS

程序管理筆記三、完全公平排程演算法CFS

程序管理筆記三、CFS排程演算法

引言:CFS是英文Completely Fair Scheduler的縮寫,即完全公平排程器,負責程序排程。在Linux Kernel 2.6.23之後採用,它負責將CPU資源,分配給正在執行的程序,目標在於最大化程式互動效能,最小化整體CPU的運用。使用紅黑樹來實現,演算法效率為O(log(n))。

一、CFS排程演算法原理

  排程演算法最核心的兩點即為排程哪個程序執行、被排程程序執行的時間多久。前者稱為排程策略,後者為執行時間

1.1、排程策略

  cfs定義一種新的模型,它給cfs_rq(cfs的run queue)中的每一個程序安排一個虛擬時鐘,vruntime。如果一個程序得以執行,隨著時間的增長(即一個個tick的到來),其vruntime將不斷增大。沒有得到執行的程序vruntime不變。
  排程器總是選擇vruntime值最低的程序執行

。這就是所謂的“完全公平”。對於不同程序,優先順序高的程序vruntime增長慢,以至於它能得到更多的執行時間。

1)、公平的體現:機會平等,時間差異
  公平體現在vruntime (virtual runtime, 虛擬執行時間)上面,它記錄著程序已經執行的時間,其大小與程序的權重、執行時間存在一個定量計算關係。

vruntime = 實際執行時間 * 1024 / 程序權重

  實際上1024等於nice為0的程序的權重,程式碼中是NICE_0_LOAD,也就是說,所有程序都以nice值為0的權重1024作為基準,計算自己的vruntime增加速度。結合分配給程序實際執行的時間,可得如下換算關係:

分配給程序的時間 = 排程週期 * 程序權重 / 全部程序權重之和
vruntime = 實際執行時間 * 1024 / 程序權重
vruntime = (排程週期 * 程序權重 / 全部程序權重之和) * 1024 / 程序權重
vruntime = (排程週期 / 全部程序權重之和) * 1024

  可以看到程序在一個排程週期內的vruntime值大小與程序權重無關,所有程序的vruntime值在一個週期內增長是一致的。vruntime值較小的程序,說明它以前佔用cpu的時間較短,受到了不公平對待,因此選擇作為下一次執行的程序。
  這樣既能公平選擇程序,又能保證高優先順序程序獲得較多執行時間,就是cfs的主要思想了。其可以簡單概括為:機會平等、時間差異

1.2、執行時間

  cfs採用當前系統中全部可排程程序優先順序的比重確定每一個程序執行的時間片,即:

分配給程序的時間 = 排程週期 * 程序權重 / 全部程序之和。

  假如有三個可排程程序A、B、C,它們的優先順序分別為5,10,15,排程週期為60ms, 則它們的時間片分別為:60ms * 5 / 30 = 10ms、60ms * 10 / 30 = 20ms、60ms * 15 / 30 = 30ms

二、CFS排程演算法核心實現

2.1、骨架—紅黑樹

  cfs排程演算法使用紅黑樹來實現,其詳細內容可以參考維基百科紅黑樹的介紹。這裡簡單講一下cfs的結構。第一個是排程實體sched_entity,它代表一個排程單位,在組排程關閉的時候可以把他等同為程序。每一個task_struct中都有一個sched_entity,程序的vruntime和權重都儲存在這個結構中。
  sched_entity通過紅黑樹組織在一起,所有的sched_entity以vruntime為key(實際上是以vruntime-min_vruntime為key,是為了防止溢位)插入到紅黑樹中,同時快取樹的最左側節點,也就是vruntime最小的節點,這樣可以迅速選中vruntime最小的程序。

僅處於就緒態的程序在這棵樹上,睡眠程序和正在執行的程序都不在樹上。

這裡寫圖片描述
圖1、cfs演算法的骨架-紅黑樹資料結構

2.2、nice值與權重的關係

  每一個程序都有一個nice值,代表其靜態優先順序。可以參考 Linux nice及renice命令使用。nice值和程序的權重的關係儲存在陣列prio_to_weight中,如下所示:

/*prio_to_weight陣列反應的是nice值與權重的對應關係*/
static const int prio_to_weight[40] = {
     /* -20 */     88761,     71755,     56483,     46273,     36291,
     /* -15 */     29154,     23254,     18705,     14949,     11916,
     /* -10 */      9548,      7620,      6100,      4904,      3906,
     /*  -5 */      3121,      2501,      1991,      1586,      1277,
     /*   0 */      1024,       820,       655,       526,       423,
     /*   5 */       335,       272,       215,       172,       137,
     /*  10 */       110,        87,        70,        56,        45,
     /*  15 */        36,        29,        23,        18,        15,
     };

  可以看到,nice值越小,程序的權重越大。CFS排程器的一個排程週期是固定的,由sysctl_sched_latency變數儲存。

2.3、兩個重要的結構體

1)、完全公平佇列cfs_rq:描述執行在一個cpu上的處於TASK_RUNNING狀態的普通程序的各種執行資訊:

struct cfs_rq {
    struct load_weight load;  //執行佇列總的程序權重
    unsigned int nr_running, h_nr_running; //程序的個數

    u64 exec_clock;  //執行的時鐘
    u64 min_vruntime; //該cpu執行佇列的vruntime推進值, 一般是紅黑樹中最小的vruntime值

    struct rb_root tasks_timeline; //紅黑樹的根結點
    struct rb_node *rb_leftmost;  //指向vruntime值最小的結點
    //當前執行程序, 下一個將要排程的程序, 馬上要搶佔的程序, 
    struct sched_entity *curr, *next, *last, *skip;

    struct rq *rq; //系統中有普通程序的執行佇列, 實時程序的執行佇列, 這些佇列都包含在rq執行佇列中  
    ...
    };

2)、排程實體sched_entity:記錄一個程序的執行狀態資訊

struct sched_entity {
    struct load_weight  load; //程序的權重
    struct rb_node      run_node; //執行佇列中的紅黑樹結點
    struct list_head    group_node; //與組排程有關
    unsigned int        on_rq; //程序現在是否處於TASK_RUNNING狀態

    u64         exec_start; //一個排程tick的開始時間
    u64         sum_exec_runtime; //程序從出生開始, 已經執行的實際時間
    u64         vruntime; //虛擬執行時間
    u64         prev_sum_exec_runtime; //本次排程之前, 程序已經執行的實際時間
    struct sched_entity *parent; //組排程中的父程序
    struct cfs_rq       *cfs_rq; //程序此時在哪個執行佇列中
};

2.4、幾個與cfs有關的過程:

1)、建立新程序:需要設定新程序的vruntime值及將新程序加入紅黑樹中,並判斷是否需要搶佔當前程序。
2)、程序喚醒:需要調整睡眠程序的vruntime值, 並且將睡眠程序加入紅黑樹中. 並判斷是否需要搶佔當前程序
3)、程序排程:需要把當前程序加入紅黑樹中, 還要從紅黑樹中挑選出下一個要執行的程序.
4)、時鐘週期中斷:在時鐘中斷周期函式中, 需要更新當前執行程序的vruntime值, 並判斷是否需要搶佔當前程序
這裡詳細的程式碼實現,可以參考:Linux的CFS(完全公平排程)演算法,程式碼解釋非常詳實。

三、CFS排程演算法相關的有趣問題

3.1、新程序的vruntime的初始值是不是0?

  假如新程序的vruntime初值為0的話,比老程序的值小很多,那麼它在相當長的時間內都會保持搶佔CPU的優勢,老程序就要餓死了,這顯然是不公平的。所以CFS是這樣做的:每個CPU的執行佇列cfs_rq都維護一個 min_vruntime 欄位,記錄該執行佇列中所有程序的vruntime最小值,新程序的初始vruntime值就以它所在執行佇列的min_vruntime為基礎來設定,與老程序保持在合理的差距範圍內。

3.2、休眠程序的vruntime的值一直保持不變嗎?

  如果休眠程序的 vruntime 保持不變,而其他執行程序的 vruntime 一直在推進,那麼等到休眠程序終於喚醒的時候,它的vruntime比別人小很多,會使它獲得長時間搶佔CPU的優勢,其他程序就要餓死了。這顯然是另一種形式的不公平。CFS是這樣做的:在休眠程序被喚醒時重新設定vruntime值,以min_vruntime值為基礎,給予一定的補償,但不能補償太多。

3.3、程序佔用的時間片可以無窮小嗎?

  假設有兩個程序,它們的vruntime初值都是一樣的,第一個程序只要一執行,它的vruntime馬上就比第二個程序更大了,那麼它的CPU會立即被第二個程序搶佔嗎?答案是這樣的:為了避免過於短暫的程序切換造成太大的消耗,CFS設定了程序佔用CPU的最小時間值, sched_min_granularity_ns ,正在CPU上執行的程序如果不足這個時間是不可以被調離CPU的。

3.4、程序從一個CPU遷移至另外一個CPU的時候vruntime會變化嗎?

  當程序從一個CPU的執行佇列中出來 (dequeue_entity) 的時候,它的vruntime要減去佇列的min_vruntime值; 而當程序加入另一個CPU的執行佇列 ( enqueue_entiry) 時,它的vruntime要加上該佇列的min_vruntime值。 這樣,程序從一個CPU遷移到另一個CPU之後,vruntime保持相對公平。

小結:這裡參考了很多人的部落格,大家從不同角度描述了他們理解的CFS演算法,可以概括為演算法原理、演算法實現。我是一個有強迫症的人,前一篇寫了程序排程演算法,第三篇一定要寫CFS,這樣才覺得學習是有連貫性的,知識也是成體系有脈絡的。後面有精力,在大的框架搭建完成以後,會仔細學習紅黑樹,推敲cfs核心結構體的組織關係。

糾錯及建議:[email protected]

相關推薦

程序管理筆記完全公平排程演算法CFS

程序管理筆記三、CFS排程演算法 引言:CFS是英文Completely Fair Scheduler的縮寫,即完全公平排程器,負責程序排程。在Linux Kernel 2.6.23之後採用,它負責將CPU資源,分配給正在執行的程序,目標在於最大化程式互動

程序管理筆記程序排程概念及基本策略

程序排程概念及基本策略 引言:多程序併發是邏輯併發,在單個CPU上,實際上任意時刻只能有一個程序處於執行狀態,而其它程序處於非執行狀態。那麼程序是如何排程的呢?本篇筆記將依次介紹程序排程概念、排程目標、排程演算法、演算法評估。下一篇筆記將重點介紹CFS排程演算

linux核心分析——CFS完全公平排程演算法

1.1 CFS原理     cfs定義了一種新的模型,它給cfs_rq(cfs的run queue)中的每一個程序安排一個虛擬時鐘,vruntime。如果一個程序得以執行,隨著時間的增長(也就是一個個tick的到來),其vruntime將不斷增大。沒有得到執行的程序vruntime不變。    而排程器總是選

微信小程序學習筆記(持續更新)---小程序組件通信

操作 nts bin json view data 組件 學習 所有 參照這裏 這裏將重要的點貼一下: 一、項目目錄結構 在項目同級目錄新建components文件夾,新建component會生成wxml,wxss,js,json文件。將所有的公共組件都寫在此文件夾下。 二

資料科學和人工智慧技術筆記 資料預處理

三、資料預處理 作者:Chris Albon 譯者:飛龍 協議:CC BY-NC-SA 4.0 為 Scikit-Learn 轉換 Pandas 類別資料 # 匯入所需的庫 from sklearn import preprocessing import

RxJava2複習筆記操作符Zip

zip專用於合併事件,該合併不是連線(連線操作符後面會說),而是兩兩配對。它按照嚴格的順序應用這個函式。因此它只發射與發射資料項最少的那個Observable物件一樣多的資料。 這裡用簡書大神Season_zlc的一張圖片來形象的解釋這個合併和傳送過程。

Mongodb學習筆記使用asp.net在Mongodb中儲存和讀取圖片檔案

今天練習瞭如何使用c# driver儲存和讀取圖片。 廢話不多說,直接上程式碼。 一、儲存圖片(檔案應該也一樣): private void SaveImgBJSON(string id, byte[] byteImg) {

Linux 程序管理 筆記

https://www.ibm.com/developerworks/cn/linux/l-linux-process-management/index.htmlLinux 程序管理剖析 程序可以是短期的(從命令列執行的一個命令),也可以是長期的(一種網路服務) 在使用者空間,程序是由程序識別符號(PID

深入理解Java虛擬機器學習筆記——虛擬機器類載入機制

1、概述 虛擬機器把描述類的資料從Class檔案載入到記憶體,並對資料進行校驗、轉換解析和初始化,最終形成能夠被虛擬機器直接使用的資料型別,這就是虛擬機器的類載入機制。在Java中,類的載入、校驗、解析和初始化都是在執行期間完成的。 2、類載入的時機 類從被載入都虛擬機器記

python學習筆記()字典

映射類型 strong 賦值 python學習 兩個 4.3 所有 tde 及其   字典是一種映射類型的數據類型。辣麽什麽是映射呢?如果看過《數據結構與算法》這一本書的小夥伴應該有印象(我也只是大學學習過,嘻嘻)。   映射:就是將兩個集合一 一對應起來,通過集合a的值

作業系統-實驗 模擬處理機HRRN排程演算法(Java實現)

                                          &

作業優先排程演算法 先來先服務短作業排程演算法(c語言描述)

/*先來先服務排程演算法*/ #include<stdio.h> #define N 10 int Arival[N]={0}; int Go[N]={0}; int Start[N]={0}; int End[N]={0}; int Timer[N]={0};

FIFOLRUOPT頁面排程演算法及例子

網上很多介紹3種頁面置換演算法的例子和過程是不正確的, 本文根據《作業系統概念》第七版對三種演算法做介紹,並給出正確的例子以驗證演算法。 一、FIFO先進先出頁面置換演算法,建立一個FIFO佇列來管

《程式設計之美》讀書筆記之[小飛電梯排程演算法]

   在高峰時間,實習生小飛常常會被電梯每層樓都停弄得很不耐煩,於是他想出了這樣一個辦法:由於樓層並不高,那麼在繁忙的時間,每次電梯從一層往上走時,我們只允許電梯停在其中的某一層。所有乘客都從一樓上電梯,到達某層樓後,

處理機管理(四)--windows / linux 的程序及其排程

windows的程序和執行緒 程序具有以下兩個基本特徵:(1)程序是系統進行資源分配的基本單位;(2)程序是系統進行處理機排程分派的單位。 Windows Server 2008把這兩個特徵分開處理:程序擁有“資源擁有者”的特徵;執行緒擁有“排程和執行”的特徵。

處理機管理(二)--程序排程管理

程序的排程與管理 程序控制塊佇列(PCB),作業系統採用連結串列的方法將這些程序的PCB連結起來生成佇列。 對於單CPU系統,生成的PCB佇列如下。 (1)執行佇列。任何時刻系統中最多隻有一個程序處於執行狀態。 (2)就緒佇列。就緒佇列中的PCB會根據某種

【讀書筆記】《Linux核心設計與實現》程序管理程序排程

大學跟老師做嵌入式專案,寫過I2C的裝置驅動,但對Linux核心的瞭解也僅限於此。Android系統許多導致root的漏洞都是核心中的,研究起來很有趣,但看相關的分析文章總感覺隔著一層窗戶紙,不能完全理會。所以打算系統的學習一下Linux核心。買了兩本書《Linux核心設計與實現(第3版)》和《深入理解Lin

Linux的程序管理檢視殺死程序任務管理系統資源監控(課堂學習筆記

  一、程序管理 1.檢視Linux啟動的第一個程序 2.檢視程序狀態 2.1觀察系統所有程式:ps aux 2.2檢視部分程序 2.3啟動httpd服務 2.4程序樹(可以檢視父程序與子程序) 二、檢視程序 1.Linux程序狀態 2.觀察程序

處理機管理)--作業排程

作業排程 作業:使用者要求計算機系統所做的一個計算問題或一次事務處理的完整過程。 作業步:任何一個作業都要經過若干加工步驟之後,才能得到結果。每一個加工步驟為一個作業步。 作業控制塊:在把一個作業提交給系統時,系統也要開闢一個作業控制塊(Job control block,

處理機管理(一)--程序的引入,程序

程序的引入 一個程式通常由若干個程式段組成,他們必須按照某種先後次序執行,前一個操作執行完後,才能執行後繼操作,這種計算過程即程式的順序執行過程。 順序執行的特性:順序性、封閉性、可再現性 這樣系統中一次只能執行一個獨立程式,導致計算機不同部件之間有忙有閒,不能夠充分發揮系