前面對linux排程演算法的框架進行了介紹,在這裡對cfs(完全公平排程)演算法進行分析。
cfs允許每個程序執行一段時間、迴圈輪轉、選擇執行最少的程序作為下乙個執行程序,而不再採用分配給每個程序時間片的做法了,cfs在所有可執行程序總數基礎上計算出乙個程序應該執行多久,而不是依靠nice值來計算時間片。nice值在cfs中被作為程序獲得的處理器執行比的權重:越高的nice值(越低的優先順序)程序獲得更低的處理器使用權重,這是相對預設nice值程序的程序而言的;相反,更低的nice值(越高的優先順序)的程序獲得更高的處理器使用權重。
資料結構
執行佇列
struct cfs_rq ;
執行實體結構為sched_entity,所有的排程器都必須對程序執行時間做記賬。cfs不再有時間片的概念,但是他也必須維護每個程序執行的時間記賬,因為他需要確保每個程序只在公平分配給他的處理器時間內執行。cfs使用排程器實體結構來最終執行記賬
/* * cfs stats for a schedulable entity (task, task-group etc) * * current field usage histogram: * * 4 se->block_start * 4 se->run_node * 4 se->sleep_start * 6 se->load.weight */ struct sched_entity ;
排程器的實體作為乙個名為se的成員變數,潛入在程序描述符struct task_struct內。
具體的排程類
/* * all the scheduling class methods: */ static const struct sched_class fair_sched_class = ;
對於從執行佇列中刪除函式dequeue_task_fair
/* * the dequeue_task method is called before nr_running is * decreased. we remove the task from the rbtree and * update the fair scheduling stats: */ static void dequeue_task_fair(struct rq *rq, struct task_struct *p, int sleep) /*更新hrtick*/ hrtick_update(rq); }/*刪除動作發生在程序阻塞(變為不可執行狀態) 或者終止時(結束執行)*/ static void dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se, int sleep) #endif } clear_buddies(cfs_rq, se); if (se != cfs_rq->curr) __dequeue_entity(cfs_rq, se); account_entity_dequeue(cfs_rq, se); update_min_vruntime(cfs_rq); }/*實現記賬功能,由系統定時器週期呼叫*/ static void update_curr(struct cfs_rq *cfs_rq) } static inline void __update_curr(struct cfs_rq *cfs_rq, struct sched_entity *curr, unsigned long delta_exec) static inline unsigned long calc_delta_fair(unsigned long delta, struct sched_entity *se) /*在這裡不打算詳細分析calc_delta_mine (delta_exec,weight,lw),它的執行過程約為delta *= weight / lw. 從這個函式中可以看到,如果程序的優先順序為0,那麼就是返回delta. 如果不為0,就會呼叫calc_delta_mine()對delta值進行修正.對上面對calc_delta_mine()的說明來看 ,有如下關係: delta = delta* nice_0_load/ se->load se->load值是怎麼來的呢? 可以跟蹤sys_nice(),就可以發現se->load 其它就是表示nice對應的load值,nice越低,值越大. 據此,就可以得到乙個結論.在執行相同時間的條件下(delta相同), 高優先的程序計算出來的delta值會比低優先順序的程序計算出來 的低.應此,高優先的程序就會位於rb_tree的左邊,在下次排程的 時候就會優先排程. */ static unsigned long calc_delta_mine(unsigned long delta_exec, unsigned long weight, struct load_weight *lw) tmp = (u64)delta_exec * weight; /* * check whether we'd overflow the 64-bit multiplication: */ if (unlikely(tmp > wmult_const)) tmp = srr(srr(tmp, wmult_shift/2) * lw->inv_weight, wmult_shift/2); else tmp = srr(tmp * lw->inv_weight, wmult_shift); return (unsigned long)min(tmp, (u64)(unsigned long)long_max); } static void account_entity_dequeue(struct cfs_rq *cfs_rq, struct sched_entity *se) /*執行個數減一*/ cfs_rq->nr_running--; se->on_rq = 0;/*表示不再執行佇列中*/ }
刪除函式最終將由下面函式從紅黑樹裡面刪除
static void __dequeue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) rb_erase(&se->run_node, &cfs_rq->tasks_timeline); }
向執行佇列中新增項的函式為enqueue_task_fair完成
static void enqueue_task_fair(struct rq *rq, struct task_struct *p, int wakeup) hrtick_update(rq); }
該函式更新相關排程資訊後最終會呼叫下面函式插入執行程序的紅黑樹
static void __enqueue_entity(struct cfs_rq *cfs_rq, struct sched_entity *se) else } /* * maintain a cache of leftmost tree entries (it is frequently * used): */ if (leftmost) cfs_rq->rb_leftmost = &se->run_node; rb_link_node(&se->run_node, parent, link); rb_insert_color(&se->run_node, &cfs_rq->tasks_timeline); }
可見cfs的執行佇列布局是放在紅黑樹裡面的,而這顆紅黑樹的排序方式是按照執行實體的vruntime來的。vruntime的計算方式在上面已經做了分析。
程序選擇
cfs排程演算法的核心是選擇具有最小vruntine的任務。執行佇列採用紅黑樹方式存放,其中節點的鍵值便是可執行程序的虛擬執行時間。cfs排程器選取待執行的下乙個程序,是所有程序中vruntime最小的那個,他對應的便是在樹中最左側的葉子節點。實現選擇的函式為pick_next_task_fair
static struct task_struct *pick_next_task_fair(struct rq *rq) while (cfs_rq); p = task_of(se); hrtick_start_fair(rq, p); return p; }
該函式最終呼叫__pick_next_entity完成實質工作
/*函式本身並不會遍歷數找到最左葉子節點(是 所有程序中vruntime最小的那個),因為該值已經快取 在rb_leftmost欄位中*/ static struct sched_entity *__pick_next_entity(struct cfs_rq *cfs_rq)
總結:cfs排程執行佇列採用紅黑樹方式組織,紅黑樹種的key值以vruntime排序。每次選擇下乙個程序執行時即是選擇最左邊的乙個程序執行。而對於入隊和處隊都會更新排程佇列、排程實體的相關資訊。
linux核心程序排程系列之排程概述
多工作業系統分為非搶占式多工和搶占式多工。linux採用的是搶占式多工的模式,這就意味著程序對cpu的占用時間是由作業系統決定的,跟具體的說,由作業系統的程序排程程式所決定的,這個章節就介紹關於程序的排程策略。一 排程策略 1 其實程序的排程策略和程序的型別有關 第一種分配方法 cpu消耗型和i o...
Linux核心排程
linux核心的三種排程策略 1,sched other 分時排程策略,2,sched fifo實時排程策略,先到先服務。一旦占用cpu則一直執行。一直執行直到有更高優先順序任務到達或自己放棄 3,sched rr實時排程策略,時間片輪轉。當程序的時間片用完,系統將重新分配時間片,並置於就緒佇列尾。...
linux核心之程序排程(一)
等待佇列 sleep相關函式將程序的狀態設定為非執行態,在下一次排程來時,將在schedule函式中將本程序從執行佇列中移除。sleep函式將程序加入等待佇列,然後呼叫schedule函式選擇並重新開始另乙個程式的執行。當呼叫wake up類函式將程序喚醒時,wake up類函式將程序加入執行佇列中...