CFS排程器中子程序被喚醒後的細節 實驗和理論

2021-05-21 19:32:53 字數 4275 閱讀 9561

在我提交並自己打上child-runs-first補丁之前,我做了乙個實驗,目的是驗證一下我先前理論分析的結果,我覺得子程序無論如何搶占父程序的機率都會比不搶占要大些,當然前提是要有的,具體見下面的試驗。空有理論是沒有用的,理論上cfs排程器執行嚴格的歸一化,然而實際上卻不是那樣的,實踐結果永遠比理論更加現實,因為不執行歸一化絲毫影響不了cfs排程器選擇最小vruntime的程序,這很簡單,每乙個程序按照自己的權值和當前的系統排程週期執行動態時間片的時間,同時按照不同的速率推進自己的虛擬時間,排程器只要能保證各個程序在執行按照其權值分給它們的不同時間片之後可以向前推進相同的虛擬時間就可以了,管他什麼歸一化呢?正是這樣,cfs排程器執行的很好。

環境:單cpu,hz=250,linux-2.6.28原始核心(沒有打我的child_runs_first補丁)

核心配置與實驗前提:sysctl_sched_child_runs_first=0,sysctl_sched_features=(僅開啟akeup_preempt),sysctl_sched_wakeup_granularity=0,sysctl_sched_latency_ns=20000000.

目的:在沒有sysctl_sched_child_runs_first策略的情況下測試cfs排程器在子程序被喚醒時的行為

測試程式:

-------stub--------

/*模擬cpu程序,將cfs_rq的nr_running提高到一定數量*/

int main(int argc,char*argv )

nice(atoi(argv[1]));

int a = 1,b=0;

while(a++||1)

b+=a;

-------child_run_delay--------

/*父程序延遲一會再fork,避免託shell在fork父程序時給了父程序min_vruntime的福*/

#include

#include

#include

int main(int argc,char *argv)

int v = atoi(argv[1]);

nice(v);

unsigned long i = 1000000;

while(i-->0)

v++;

if(fork() == 0)

printf("sub/n");

exit(0);

printf("main,%d/n",v);

-------child_run_nodelay--------

/*馬上fork子程序,託父程序vruntime很小的福*/

#include

#include

#include

int main(int argc,char *argv)

int v = atoi(argv[1]);

nice(v);

if(fork() == 0)

printf("sub/n");

exit(0);

printf("main/n");

測試過程:連續建立9個stub程序,nice值分散開來,然後以不同的nice值執行child_run_delay和child_run_nodelay。

結果:測試**中i=1000時的測試結果

child_run_delay

child_run_nodelay

nice 15

96%preempt

95%preempt

nice 10

96%preempt

94%preempt

nice -10

70%preempt

12%preempt

nice -17

64%preempt

10%preempt

nice -20

11%preempt

4%preempt

測試**中i=10000000時的測試結果

child_run_delay

child_run_nodelay

nice 15

36%preempt

96%preempt

nice 10

33%preempt

96%preempt

nice -10

24%preempt

10%preempt

nice -17

15%preempt

9%preempt

nice -20

9%preempt

6%preempt

結果分析:

child_run_delay程序和child_run_nodelay程序的建立也是shell執行fork而來的,那時這些程序將被賦予cfs_rq的min_vruntime,不論如何,這個vruntime是所有執行程序的最小的vruntime,包括正在執行的curr(因為min_vruntime在update_curr中被實時更新),cfs_rq的min_vruntime是單調遞增的,只有在當前程序是最後乙個被落下的程序時,cfs_rq的min_vruntime才會和curr的vruntime相等,也就是說cfs_rq的min_vruntime永遠是最小的vruntime,這個比2.6.28以前的有所改進,不會拉大程序間vruntime的距離從而造成對vruntime值大的程序的不公平。既然child_run_delay程序和child_run_nodelay程序的vruntime是最小的,那麼如果它們的nice值小的話,其虛擬時鐘推進慢,到了fork的時候其vruntime可能還是最小的,於是根據**和核心配置,搶占的機率就會變小。什麼叫變小呢?誰和誰比呢?其實就是main剛執行的時候和fork的時候比較,另外還有乙個比較,就是delay程式和nodelay程式的比較,對於nodely,搶占的機率更小,因為父程序趁著自己的vruntime最小的時候,幾乎是馬上就fork了子程序,此時子程序被賦予最小的min_vruntime,而此時此vruntime很大很大機率就是父程序的還沒有推進幾步vruntime,如果父程序的nice值很小,推進的更慢,不搶占的機率更大,但是如果父程序的nice值很大的話,虛擬時鐘即vruntime推進的很快,稍微一執行就不是最小的了,那麼到了fork的時搶占就是很大機率的事情了。

sched_latency_ns比較小,vruntime推進很活躍的情況下,即使執行-20的nice值的child_run_delay測試程序也可能發生搶占並且機率很大,這好像和上面一段的分析結果相反,為什麼,因為在執行了很大一會後並且執行的都是cpu指令,沒有睡眠之類的,該程序的vruntime即使權值再高它也不一定是在乙個系統排程週期(sysctl_sched_latency_ns)最後乙個執行的程序,更何況由於程序的不確定的睡眠喚醒新建退出,嚴格的虛擬時鐘歸一化已經被打亂,只要不是最後乙個執行的程序,也就是說只要說這個程序不是被落下的那乙個,那麼該程序的vruntime就一定大於cfs_rq的min_vruntime,於是該程序在fork子程序的時候,由於cfs_rq的min_vruntime給了新程序,那麼新程序的vruntime會小於current(即高權值的父程序)的vruntime,如果搶占粒度很小的話,那麼子程序就會搶占父程序,而我們為了使事情簡單化將搶占粒度設定為0,於是搶占發生。可是如果高權值的父程序如果是最後乙個執行的,那麼由於它的虛擬時鐘推進很慢,因此很大的機率下其vruntime將一直是cfs_rq的min_vruntime,如果是,那麼搶占不發生,可是即使該種情況下該程序的vruntime和cfs_rq的vruntime相等的機率再大,該程序被落下為最後乙個的機率本身就很小,畢竟每乙個程序被落下為最後乙個都有可能,因此不論如何,發生搶占的機率還是要比不發生搶占的機率大,符合測試,如果將活動程序的數量減小,那麼不發生搶占的機率會變大,但是統計意義上還是高不過發生搶占的機率,fork前執行cpu指令的時間越長,效果越明顯。

被隱蔽的真相:如果將child_run_delay中的i值減小,結果就是越低nice值的程序越不容易發生搶占,高nice值的幾乎都會搶占。這其實是shell在執行我們的測試程式時引入的,原因見上述分析,i的值足夠小,i--的迴圈時間沒有到達該父程序的動態時間片,因此就會出現容易誤導使用者的結果。

事情的真相:不要以為權值大的程序就會被最後執行,不要以為權值大的程序的vruntime就一定是cfs_rq的min_vruntime,不是這樣的,以理想情況分析,乙個排程週期內誰最後執行是不一定的,要看它們入隊的順序以及入隊時紅黑樹的既有情況。在不借助父程序被shell建立時被賦予很小的vruntime的優勢的情況下,也就是fork之前不睡眠不阻塞執行足夠長的時間的情況下,fork時的子程序搶占父程序的機率更大些,和父程序的優先順序沒有必然關係,只能說相同條件下低權值的程序搶占絕對機率更大些但是永遠不會超過不搶占發生的機率,因為那麼多程序只要父程序不是最後乙個執行的那麼就不會搶占,而每個程序最後乙個執行的機會近乎均等。

inux核心 CFS程序排程器

現代作業系統設計的目的在於管理底層硬體資源並使整個計算機系統執行效能達到最優。我們可以發現實現這一目的的方法可以歸結為兩點 cpu虛擬化和記憶體虛擬化。現代作業系統一般都能提供多工執行環境,每個程序都可以擁有自己的虛擬cpu,程序在執行過程中感覺自己是獨佔cpu。類似地,記憶體虛擬化是通過讓每個程序...

CFS排程器的思想的新理解

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

CFS排程器的思想的新理解

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