作者:新浪微博(@NP等不等于P) 計算機學習微信公眾號(jsj_xx) 對于樸素的CFS調(diào)度而言,其控制粒度是一個進程,這樣的粒度在很多場景是不合適的。比如,希望達到用戶粒度,也就是希望每個用戶占有相同的cpu時間。但是,在各個用戶擁有的進程數(shù)量不同的情況下,顯然樸素的CFS(請參考我們之前的《理解進程CFS調(diào)度》)會將cpu更多地分配給進程數(shù)量多的用戶。組調(diào)度的引入,正是解決此問題的。 參考linux kernel souce code 4.0,本文分享我們(計算機學習微信公眾號:jsj_xx)對組調(diào)度的理解(如有錯誤,還望指正,謝謝)。 組調(diào)度其實屬于cgroup框架的cpu子系統(tǒng),故需要開啟cgroup:CONFIG_CGROUP_SCHED。同時,開啟CFS組調(diào)度:CONFIG_FAIR_GROUP_SCHED。注意,本文僅討論普通進程,不涉及實時進程。 1 組調(diào)度原理 我們先看task_group(本文中簡稱為tg)的shares的含義。 tg的shares值就是該tg(se)的權(quán)重。想想CFS中的進程,其權(quán)重需要從優(yōu)先級轉(zhuǎn)化而來,而tg就簡單了:直接指定即可。另外,tg和task是兩種完全不同的個體,由此引入se來抽象兩者:tg和task。 以下是我對組調(diào)度(tg)的理解要點:
組調(diào)度的原理,就是每個tg都擁有自己的cfs_rq以及融入上級的se,從而搭建出一顆se/cfs_rq樹,通過tg的shares或者task的權(quán)重將所有節(jié)點(tg或task)管理起來。關(guān)鍵是,引入了平行于task的調(diào)度實體:tg。 2 tg數(shù)據(jù)結(jié)構(gòu) 我們看下tg的數(shù)據(jù)結(jié)構(gòu):
簡單地說,就是搭建了兩顆樹:tg/cgroup固定樹和se/cfs_rq靈活樹。 3 與cgroup的關(guān)系 一個tg,通過內(nèi)嵌一個css(cgroup_subsys_state)的方式融入到cgroup框架里(cgroup框架,我們以后會有專題去談)。涉及到cgroup中的cpu子系統(tǒng),相關(guān)的注冊函數(shù)如下: 我們主要看看cpu_cgroup_css_alloc(),創(chuàng)建css(也就是tg,因為css是內(nèi)嵌于tg里的)的處理。 對于頂級tg:root_task_group,其css是預(yù)留的,無需分配。對于普通tg,需要分配tg(和內(nèi)嵌的css)。這樣,cgroup/tg的樹結(jié)構(gòu)層次,也就在這個處理中搭建出來了。 【參考cpu_cgroup_css_alloc()->sched_create_group()->alloc_fair_sched_group()】 另外,還可以看到,task加入tg的動作是支持批處理的。 【參考cpu_cgroup_attach()】 我們再看看設(shè)置shares時的處理,這是借助cgroup文件系統(tǒng)里的shares文件來實現(xiàn)的,其處理函數(shù): 在sched_group_set_shares()里設(shè)置指定tg的shares值: 此處是組調(diào)度的核心處理,設(shè)置tg的shares。我們仔細分析此函數(shù):
理解了這些,我們重新審視此tg的shares:
回到sched_group_set_shares(),繼續(xù)分析。此處理中,最關(guān)鍵的是兩個要點:
那么,修改tg的shares時,如果不擴散呢? 我的理解是:tg的shares的修改,如果不做上述這種擴散處理,甚至不去利用各個tg在各個cpu上的load比重,這樣,tg的shares在各個cpu上都是一樣的(都等于tg的shares值),也是可以保證組調(diào)度的運行。但是,這種實現(xiàn)僅能保證了tg的粒度,tg內(nèi)的task粒度卻失衡了。。。 最后,要補充一下上述討論的前提:各個cpu是負載均衡的。拋開均衡(SMP負載均衡涉及調(diào)度域,我們以后再談)談組調(diào)度是沒有意義的,我覺得。畢竟tg的shares是一個值,而調(diào)度時所有cpu都是圍繞該值運轉(zhuǎn),如果cpu負載不均,勢必令調(diào)度失去公平。 4 與CFS的關(guān)系 CFS的紅黑樹存在于每一個tg里,也就是說每個tg的cfs_rq都在運作一個CFS調(diào)度(參考我們之前的《理解進程CFS調(diào)度》)。這樣,在將一個task加入到某個tg時,需要自底(表示葉子的task)向上層次地做入隊列處理: 自然,需要保證從該task到根節(jié)點是完整的路徑,所以這個上溯動作直到碰到某層的tg(se)早已經(jīng)存在于隊列里才停止。 出隊列也是如此。 task出隊列,則沿著task所在的tg上溯,將沿途的只有一個成員的tg也做出隊列操作??梢姡纤莩鲫犃胁僮鲿掷m(xù)到碰到一個tg,該tg至少包含一個跟此task無關(guān)(此處的“無關(guān)”是指此成員不在此上溯路徑里)的成員。 注意,上述兩個操作都是在se/cfs_rq樹中。 至于各個(層)的tg或task,它們CFS調(diào)度涉及的vruntime,這些都是獨立的。也就是說,層次之間的CFS運轉(zhuǎn)是獨立的,它們各自的周期時間計算只與自己的cfs_rq有關(guān),與組調(diào)度(其它的cfs_rq)是毫無關(guān)系的。這樣,可以想象,底層tg可能會由于上級tg的slice用完而reschedule,但其實自己的slice還有剩余,這是一種正常現(xiàn)象。因為底層的調(diào)度實體,周期以及各個slice的計算是獨立與其它層的,但是調(diào)度控制還是受制于其它的(這里的“其它”是指自底向上,直至根節(jié)點的整個路徑層次)。 5 與負載均衡的關(guān)系 在遷移進程時,也會針對遷移進程做上述擴散處理,相當于進程在tg內(nèi)的遷移而已。只是,進程的選擇需要大作文章,涉及調(diào)度域,這是另一個層次結(jié)構(gòu),與組調(diào)度的層次結(jié)構(gòu)都無關(guān)。 6 總結(jié) 組調(diào)度將調(diào)度的粒度靈活化,并行保證了tg和task的粒度,這是由tg shares在cpu間劃分貫穿樹層次結(jié)構(gòu)來實現(xiàn)的,同時伴以SMP負載均衡的輔佐。 新浪微博(@NP等不等于P) 計算機學習微信公眾號(jsj_xx) 原創(chuàng)技術(shù)文章,感悟計算機,透徹理解計算機! |
|