CFS排程器的思想的新理解

2021-08-25 01:25:04 字數 3973 閱讀 2422

本文通過詳細分析老的排程器來說明cfs的優勢。總是新理解新理解的,我怎麼這麼沒完沒了啊,其實每隔一段時間我都會在工作之餘再讀一讀linux核心源**的關鍵部分,每次讀都有新的理解,然後就第一時間將心得記錄下來,今天又讀了cfs排程器,越來越發現其美妙了。這次配合了sched-nice-design.txt文件閱讀,很受啟發,萬惡的排程程式終止了,新的時代開始了,o(1)排程器和cfs的作者都是ingo molnar,他真的是乙個有破有立的傢伙,太猛了!

o(1)排程器是很不錯的,因為它的效率,正如它的名稱一樣,可是我們考慮的周全一點,作業系統對任務的排程不僅僅是用最快的速度選擇最值得執行的程序,而也要兼顧最不值得執行的程序,所謂「效率優先,兼顧公平」。o(1)對於優先順序排程的本意是很有效的,因為它可以在最快的時間內選擇出優先順序最高的程序,可是低優先順序的程序可能因此飢餓而得不到執行的機會正如那篇sched-nice-design文件中講的那樣,o(1)排程器以及以前的更土的排程器的根本麻煩在於時間片的計算,因為那些排程器沒有抽象到真正的的層次,更多的是利用底層的硬體中斷來進行的,這樣的話,根本無法用統一的方式來計算時間片,必須受累於底層的硬體,也就是說沒有乙個萬能的時間片計算公式對每一台機器每乙個系統都適用,比如乙個系統的hz定義為1000,另乙個是100,那麼這兩個系統的排程行為就是不一樣的;第二,老的時間片分時排程中的時間片更多的是一種絕對的參考值,一會會說出原因,現在要說的就是時間片計算公式計算出來的時間片和程序的優先順序不是乙個和諧的關係,比如線性關係,比如在nice值為+19到+15這之間,nice值每減少乙個點其時間片平均增加delta1,而nice在0到4,時間片平均增加delta2,而這兩者是不同的,這是不應該的,因為比如我想讓時間片大大增加些,我必須知道我當前的nice值。

以上說的是總體上的,下面我來說說我的理解,到o(1)排程器加上更土的排程器,linux的排程器的時間片的計算大致分了三個階段,更土的排程器裡面也即是o(n),計算出來的時間片幾乎分辨不出誰的優先順序更高,優先順序更高的程序的時間片也不會更具有優勢,因為那時的排程器想把時間片限制在50毫秒的左右不超出多少的範圍內,計算規則很簡單:

#elif hz

#define tick_scale(x) ((x)

#define nice_to_ticks(nice) (tick_scale(20-(nice))+1)

這個簡單的公式很線性,很和諧,但是計算出來的時間片幾乎差不離,高優先順序的程序沒有什麼優勢,後來發展到了o(1)排程器以後,當然進行了改進:

#define base_timeslice(p) (min_timeslice + /

((max_timeslice - min_timeslice) * /

(max_prio-1 - (p)->static_prio) / (max_user_prio-1)))

static inline unsigned int task_timeslice(task_t *p)

return base_timeslice(p);

這個公式將最高優先順序的時間片延長了,而把最低優先順序的時間片縮短了,不管怎樣增加了優先順序時間片的動態範圍,這個在效果上的表現就是,優先順序高的程序表現出來的時間片更加長了,為了將優先順序最高的程序的時間片和優先順序最低的程序的時間片有效分離,增加時間片基數是一種方式,比如,最低優先順序的時間片是10,然後每增加1個優先順序則增加時間片n倍,取n為2,那麼從低到高的時間片依次為10,20,40...,或者按照等差數列增加,比如差為10,那麼就是10,20,30,40...但是會遇到乙個問題,nice值或者優先順序值的增加會和時間片的增加同步嗎?如果我們強調同步,那麼最高優先順序的程序的時間片就會過於長,要麼就是基數過小,這樣的話就會引起頻繁排程而重新整理cache。後來在2.6.9核心往後的實現裡,又有了新的策略:

static unsigned int task_timeslice(task_t *p)

if (p->static_prio

return scale_prio(def_timeslice*4, p->static_prio);

else

return scale_prio(def_timeslice, p->static_prio);

不僅如此,就是因為老的排程器(呵呵,現在o(1)都成了老的排程器了)只選擇最高優先順序的程序而不管低優先順序的程序,因此它是「少了一環」的排程,不能稱為是完全的排程,正是它的不完全,才引入了很多複雜的外圍演算法,比如基於平均睡眠時間的互動檢測演算法,比如優先順序的靜態和動態分類演算法,前面的一篇文章說過,o(1)排程器的延時減少了,減少的是尋找最高優先順序程序的時間,別的時間並沒有有效減少也不可能減少,因為乙個程序需要占用確定的時間片,那麼所有的程序完全執行完也將是確定的時間片,而系統的程序數量將使整個排程週期延長,這可能造成更大面積的飢餓現象存在,為了避免這種現象,不管是linux還是windows,都有很多動態演算法,比如飢餓檢測以及優先順序臨時提公升等等,這些演算法本身就造成了不多但是有的延時。新的cfs改變了這一切。

cfs很有創意,它囊括了關於排程的一切,包括優先順序,包括尋找最值得執行的程序等等一切,在cfs中,以前o(1)的創意如今簡單的成了一棵紅黑樹,紅黑樹本身就有優先順序的性質,因為它是一棵排序樹,而且插入和刪除的效率很高,由於從執行佇列刪除程序很多都是發生了該程序執行時期,出隊執行更是增加了效率,到此為此o(1)的所有工作,一棵紅黑樹就全部搞定了,就是簡單的找出最高「優先順序」的程序,在cfs中,沒有了時間片,優先順序對應成了weight,叫做權值,系統的執行時間完全在所有程序之間按照權值公平分配,公平性體現在每個程序都有乙個虛擬時鐘,各個虛擬時鐘相互追趕,排程器總是選擇虛擬時鐘最慢的程序執行,前面文章說過,權值小的程序虛擬時鐘走的快,比如它在1個tick就走了1個字,而權值大的程序虛擬時鐘走的慢,比如10個tick走了乙個字,cfs的創舉是,它可保證乙個虛擬時鐘的速度比是1:10的程序對,那麼它的權值比是10:1,怎麼保證呢?在linux的cfs排程器中,有乙個靜態陣列,叫做prio_to_weight,它有40個元素,其中對應0到39這40個優先順序,每增加乙個優先順序值,那麼它的權值減少十分之一,注意,如果開始乙個程序的權值是10,優先順序是10,那麼它的優先順序變成9以後其權值會是9嗎?不!因為整個系統的執行時間不變,你減少了1/10的時間,那麼其它的所有程序將會增加這些時間,因為乙個程序減少1/10,別的程序增加1/10,那麼這個程序將減少25%的時間,這就是這個陣列的妙處,每乙個元素都是後乙個元素乘以1.25%。這樣的話,整個系統的權值將是乙個按比例縮減的,相應的,其虛擬時鐘的速度將按照這個比例增加,虛擬時鐘向前推進你完全可以理解成以前的時間片,只不過這個時間片不再是固定的了,而是動態的,比如乙個程序執行了10個tick其虛擬時鐘走了乙個字,這時系統中又來了乙個程序,那麼這個程序就不再執行10個tick了,可能會變成9個,如果這個系統就剩下它乙個程序了,那麼它將一直執行,不再進行無用的--p->task_timeslice的操作。記住,一切都是按照比例進行的,這個比例就是那個陣列的縮減比例。

我們看看cfs怎麼解決之前排程器遇到的那些問題,第一,不再需要雙斜率的線段,因為可以保證高權值的程序的「時間片」就是低權值的n倍,這是這個陣列決定的,排程器會計算這個程序的權值占用系統當前所有程序的權值和的幾分之幾,然後就分配整個排程週期幾分之幾的「時間片」,這個機制之所以可以如此完美又有效的進行就是因為每個程序虛擬時鐘的向前推進方式--互相追趕,排程器選擇最慢的執行(推進的速度不同)。cfs中的所謂動態執行時間可以保證cpu永遠不會浪費去做沒有意義的事情,如果系統只剩下乙個程序,那麼排程器會把整個cpu全部給了這個程序,本質上,正如作者的話:"cfs百分之八十的工作可以用一句話概括:cfs在真實的硬體上模擬了完全理想的多工處理器"。在「完全理想的多工處理器「下,每個程序都能同時獲得cpu的執行時間。當系統中有兩個程序時,cpu的計算時間被分成兩份,每個程序獲得50%。然而在實際的硬體上,當乙個程序占用cpu時,其它程序就必須等待。這就產生了不公平。cfs的排程器只知道把cpu分給當前的程序們,這種分配是按照程序的權值平均分配的,虛擬時鐘只是實現這種公平的手段而已。cfs中的nice呼叫可以「放心」的進行,因為nice影響的程序執行的「時間片」只和相對值有關和絕對值無關。cfs中將優先順序巧妙的轉換成了權值這個概念,而權值又是那麼巧妙的組織,好,非常好!

cfs如此統一的解決了那麼多問題,統一就是美,簡單就是美!cfs是乙個完全的排程方案,而不僅僅是挑選第乙個值得執行的程序的方案,這個意義上o(1)僅僅是乙個挑選第乙個程序的方案罷了。

CFS排程器的思想的新理解

本文通過詳細分析老的排程器來說明cfs的優勢。總是新理解新理解的,我怎麼這麼沒完沒了啊,其實每隔一段時間我都會在工作之餘再讀一讀linux核心源 的關鍵部分,每次讀都有新的理解,然後就第一時間將心得記錄下來,今天又讀了cfs排程器,越來越發現其美妙了。這次配合了sched nice design.t...

CFS排程演算法的思想和細節

今天在郵件列表裡面有位朋友問了乙個問題,問題表述如下 在喚醒程序的時候,發現在check preempt wakeup 中.會將 cfs rq next設定為喚醒的程序,cfs rq last設定為當前的執行程序.然後將要喚醒的程序重新入列,即 enqueue task 在pick next tas...

CFS排程演算法的思想和細節

今天在郵件列表裡面有位朋友問了乙個問題,問題表述如下 在喚醒程序的時候,發現在check preempt wakeup 中.會將 cfs rq next設定為喚醒的程序,cfs rq last設定為當前的執行程序.然後將要喚醒的程序重新入列,即 enqueue task 在pick next tas...