linux排程器原始碼研究
-
/* 一個排程實體(紅黑樹的一個結點),其包含一組或一個指定的程序,包含一個自己的執行佇列,一個父親指標,一個指向需要排程的執行佇列指標 */
-
struct sched_entity {
-
/* 權重,在陣列prio_to_weight[]包含優先順序轉權重的數值 */
-
struct load_weight load; /* for load-balancing */
-
/* 實體在紅黑樹對應的結點資訊 */
-
struct rb_node run_node;
-
/* 實體所在的程序組 */
-
struct list_head group_node;
-
/* 實體是否處於紅黑樹執行佇列中 */
-
unsigned int on_rq;
-
/* 開始執行時間 */
-
u64 exec_start;
-
/* 總執行時間 */
-
u64 sum_exec_runtime;
-
/* 虛擬執行時間,在時間中斷或者任務狀態發生改變時會更新
-
* 其會不停增長,增長速度與load權重成反比,load越高,增長速度越慢,就越可能處於紅黑樹最左邊被排程
-
* 每次時鐘中斷都會修改其值
-
* 具體見calc_delta_fair()函式
-
*/
-
u64 vruntime;
-
/* 程序在切換進CPU時的sum_exec_runtime值 */
-
u64 prev_sum_exec_runtime;
-
/* 此排程實體中程序移到其他CPU組的數量 */
-
u64 nr_migrations;
-
#ifdef CONFIG_SCHEDSTATS
-
/* 用於統計一些資料 */
-
struct sched_statistics statistics;
-
#endif
-
#ifdef CONFIG_FAIR_GROUP_SCHED
-
/* 代表此程序組的深度,每個程序組都比其parent排程組深度大1 */
-
int depth;
-
/* 父親排程實體指標,如果是程序則指向其執行佇列的排程實體,如果是程序組則指向其上一個程序組的排程實體
-
* 在 set_task_rq 函式中設定
-
*/
-
struct sched_entity *parent;
-
/* 實體所處紅黑樹執行佇列 */
-
struct cfs_rq *cfs_rq;
-
/* 實體的紅黑樹執行佇列,如果為NULL表明其是一個程序,若非NULL表明其是排程組 */
-
struct cfs_rq *my_q;
-
#endif
-
#ifdef CONFIG_SMP
-
/* Per-entity load-tracking */
-
struct sched_avg avg;
-
#endif
- };
-
struct task_struct {
-
........
-
/* 表示是否在執行佇列 */
-
int on_rq;
-
/* 程序優先順序
-
* prio: 動態優先順序,範圍為100~139,與靜態優先順序和補償(bonus)有關
-
* static_prio: 靜態優先順序,static_prio = 100 + nice + 20 (nice值為-20~19,所以static_prio值為100~139)
-
* normal_prio: 沒有受優先順序繼承影響的常規優先順序,具體見normal_prio函式,跟屬於什麼型別的程序有關
-
*/
-
int prio, static_prio, normal_prio;
-
/* 實時程序優先順序 */
-
unsigned int rt_priority;
-
/* 排程類,排程處理函式類 */
-
const struct sched_class *sched_class;
-
/* 排程實體(紅黑樹的一個結點) */
-
struct sched_entity se;
-
/* 排程實體(實時排程使用) */
-
struct sched_rt_entity rt;
-
#ifdef CONFIG_CGROUP_SCHED
-
/* 指向其所在程序組 */
-
struct task_group *sched_task_group;
-
#endif
-
........
- }
- load:權重,通過優先順序轉換而成,是vruntime計算的關鍵。
- on_rq:表明是否處於CFS紅黑樹執行佇列中,需要明確一個觀點就是,CFS執行佇列裡面包含有一個紅黑樹,但這個紅黑樹並不是CFS執行佇列的全部,因為紅黑樹僅僅是用於選擇出下一個排程程式的演算法。很簡單的一個例子,普通程式執行時,其並不在紅黑樹中,但是還是處於CFS執行佇列中,其on_rq為真。只有準備退出、即將睡眠等待和轉為實時程序的程序其CFS執行佇列的on_rq為假。
- vruntime:虛擬執行時間,排程的關鍵,其計算公式:一次排程間隔的虛擬執行時間 = 實際執行時間 * (NICE_0_LOAD / 權重)。可以看出跟實際執行時間和權重有關,紅黑樹就是以此作為排序的標準,優先順序越高的程序在執行時其vruntime增長的越慢,其可執行時間相對就長,而且也越有可能處於紅黑樹的最左結點,排程器每次都選擇最左邊的結點為下一個排程程序。注意其值為單調遞增,在每個排程器的時鐘中斷時當前程序的虛擬執行時間都會累加。單純的說就是程序們都在比誰的vruntime最小,最小的將被排程。
- cfs_rq:此排程實體所處於的CFS執行佇列。
- my_q:如果此排程實體代表的是一個程序組,那麼此排程實體就包含有一個自己的CFS執行佇列,其CFS執行佇列中存放的是此程序組中的程序,這些程序就不會在其他CFS執行佇列的紅黑樹中被包含(包括頂層紅黑樹也不會包含他們,他們只屬於這個程序組的紅黑樹)。
而在 struct task_struct 結構中,我們注意到有個排程類,裡面包含的是排程處理函式,它具體如下
-
struct sched_class {
-
/* 下一優先順序的排程類
-
* 排程類優先順序順序: stop_sched_class -> dl_sched_class -> rt_sched_class -> fair_sched_class -> idle_sched_class
-
*/
-
const struct sched_class *next;
-
/* 將程序加入到執行佇列中,即將排程實體(程序)放入紅黑樹中,並對 nr_running 變數加1 */
-
void (*enqueue_task) (struct
rq *rq, struct task_struct *p, int flags);
-
/* 從執行佇列中刪除程序,並對 nr_running 變數中減1 */
-
void (*dequeue_task) (struct
rq *rq, struct task_struct *p, int flags);
-
/* 放棄CPU,在 compat_yield sysctl 關閉的情況下,該函式實際上執行先出隊後入隊;在這種情況下,它將排程實體放在紅黑樹的最右端 */
-
void (*yield_task) (struct
rq *rq);
-
bool (*yield_to_task) (struct
rq *rq, struct task_struct *p, bool
preempt);
-
/* 檢查當前程序是否可被新程序搶佔 */
-
void (*check_preempt_curr) (struct
rq *rq, struct task_struct *p, int flags);
-
/*
-
* It is the responsibility of the pick_next_task() method
that will
-
* return the next task to call put_prev_task() on the
@prev task or
-
* something equivalent.
-
*
-
* May return RETRY_TASK when it finds a higher prio class has runnable
-
* tasks.
-
*/
-
/* 選擇下一個應該要執行的程序執行 */
-
struct task_struct * (*pick_next_task) (struct
rq *rq,
-
struct task_struct *prev);
-
/* 將程序放回執行佇列 */
-
void (*put_prev_task) (struct
rq *rq, struct task_struct *p);
-
#ifdef CONFIG_SMP
-
/* 為程序選擇一個合適的CPU */
-
int (*select_task_rq)(struct
task_struct *p, int task_cpu, int sd_flag, int flags);
-
/* 遷移任務到另一個CPU */
-
void (*migrate_task_rq)(struct
task_struct *p, int next_cpu);
-
/* 用於上下文切換後 */
-
void (*post_schedule) (struct
rq *this_rq);
-
/* 用於程序喚醒 */
-
void (*task_waking) (struct
task_struct *task);
-
void (*task_woken) (struct
rq *this_rq, struct task_struct *task);
-
/* 修改程序的CPU親和力(affinity) */
-
void (*set_cpus_allowed)(struct
task_struct *p,
-
const struct cpumask *newmask);
-
/* 啟動執行佇列 */
-
void (*rq_online)(struct
rq *rq);
-
/* 禁止執行佇列 */
-
void (*rq_offline)(struct
rq *rq);
-
#endif
-
/* 當程序改變它的排程類或程序組時被呼叫 */
-
void (*set_curr_task) (struct
rq *rq);
-
/* 該函式通常呼叫自 time tick 函式;它可能引起程序切換。這將驅動執行時(running)搶佔 */
-
void (*task_tick) (struct
rq *rq, struct task_struct *p, int queued);
-
/* 在程序建立時呼叫,不同調度策略的程序初始化不一樣 */
-
void (*task_fork) (struct
task_struct *p);
-
/* 在程序退出時會使用 */
-
void (*task_dead) (struct
task_struct *p);
-
/* 用於程序切換 */
-
void (*switched_from) (struct
rq *this_rq, struct task_struct *task);
-
void (*switched_to) (struct
rq *this_rq, struct task_struct *task);
-
/* 改變優先順序 */
-
void (*prio_changed) (struct
rq *this_rq, struct task_struct *task,
-
int oldprio);
-
unsigned int (*get_rr_interval) (struct
rq *rq,
-
struct task_struct *task);
-
void (*update_curr) (struct
rq *rq);
-
#ifdef CONFIG_FAIR_GROUP_SCHED
-
void (*task_move_group) (struct
task_struct *p, int on_rq);
-
#endif
- };
CFS執行佇列(struct cfs_rq)
我們現在知道,在系統中至少有一個CFS執行佇列,其就是根CFS執行佇列,而其他的程序組和程序都包含在此執行佇列中,不同的是程序組又有它自己的CFS執行佇列,其執行佇列中包含的是此程序組中的所有程序。當排程器從根CFS執行佇列中選擇了一個程序組進行排程時,程序組會從自己的CFS執行佇列中選擇一個排程實體進行排程(這個排程實體可能為程序,也可能又是一個子程序組),就這樣一直深入,直到最後選出一個程序進行執行為止。對於 struct cfs_rq 結構沒有什麼好說明的,只要確定其代表著一個CFS執行佇列,並且包含有一個紅黑樹進行選擇排程程序即可。
-
/* CFS排程的執行佇列,每個CPU的rq會包含一個cfs_rq,而每個組排程的sched_entity也會有自己的一個cfs_rq佇列 */
-
struct cfs_rq {
-
/* CFS執行佇列中所有程序的總負載 */
-
struct load_weight load;
-
/*
-
* nr_running: cfs_rq中排程實體數量
-
* h_nr_running: 只對程序組有效,其下所有程序組中cfs_rq的nr_running之和
-
*/
-
unsigned int nr_running, h_nr_running;
-
u64 exec_clock;
-
/* 當前CFS佇列上最小執行時間,單調遞增
-
* 兩種情況下更新該值:
-
* 1、更新當前執行任務的累計執行時間時
-
* 2、當任務從佇列刪除去,如任務睡眠或退出,這時候會檢視剩下的任務的vruntime是否大於min_vruntime,如果是則更新該值。
-
*/
-
u64 min_vruntime;
-
#ifndef CONFIG_64BIT
-
u64 min_vruntime_copy;
-
#endif
-
/* 該紅黑樹的root */
-
struct rb_root tasks_timeline;
-
/* 下一個排程結點(紅黑樹最左邊結點,最左邊結點就是下個排程實體) */
-
struct rb_node *rb_leftmost;
-
/*
-
* 'curr' points to currently
running entity on this cfs_rq.
-
* It is set to NULL otherwise (i.e
when none are currently running).
-
*/
-
/*
-
* curr: 當前正在執行的sched_entity(對於組雖然它不會在cpu上執行,但是當它的下層有一個task在cpu上執行,那麼它所在的cfs_rq就把它當做是該cfs_rq上當前正在執行的sched_entity)
-
* next: 表示有些程序急需執行,即使不遵從CFS排程也必須執行它,排程時會檢查是否next需要排程,有就排程next
-
*
-
* skip: 略過程序(不會選擇skip指定的程序排程)
-
*/
-
struct sched_entity *curr, *next, *last, *skip;
-
#ifdef CONFIG_SCHED_DEBUG
-
unsigned int nr_spread_over;
-
#endif
-
#ifdef CONFIG_SMP
-
/*
-
* CFS Load tracking
-
* Under CFS, load is tracked on a
per-entity basis and aggregated up.
-
* This allows for the description of both thread and group usage (in
-
* the FAIR_GROUP_SCHED case).
-
*/
-
unsigned long runnable_load_avg, blocked_load_avg;
-
atomic64_t decay_counter;
-
u64 last_decay;
-
atomic_long_t removed_load;
-
#ifdef CONFIG_FAIR_GROUP_SCHED
-
/* Required to track per-cpu
representation of a task_group */
-
u32 tg_runnable_contrib;
-
unsigned long tg_load_contrib;
-
/*
-
* h_load = weight * f(tg)
-
*
-
* Where f(tg) is the
recursive weight fraction assigned to
-
* this group.
-
*/
-
unsigned long h_load;
-
u64 last_h_load_update;
-
struct sched_entity *h_load_next;
-
#endif /* CONFIG_FAIR_GROUP_SCHED */
-
#endif /* CONFIG_SMP */
-
#ifdef CONFIG_FAIR_GROUP_SCHED
-
/* 所屬於的CPU rq */
-
struct rq *rq; /* cpu
runqueue to which this cfs_rq is attached */
-
/*
-
* leaf cfs_rqs are those that hold tasks (lowest schedulable entity in
-
* a hierarchy). Non-leaf
lrqs hold other higher schedulable entities
-
* (like users, containers etc.)
-
*
-
* leaf_cfs_rq_list ties together list of leaf cfs_rq's in a cpu. This
-
* list is used during load balance.
-
*/
-
int on_list;
-
struct list_head leaf_cfs_rq_list;
-
/* 擁有該CFS執行佇列的程序組 */
-
struct task_group *tg; /* group
that "owns" this runqueue */
-
#ifdef CONFIG_CFS_BANDWIDTH
-
int runtime_enabled;
-
u64 runtime_expires;
-
s64 runtime_remaining;
-
u64 throttled_clock, throttled_clock_task;
-
u64 throttled_clock_task_time;
-
int throttled, throttle_count;
-
struct list_head throttled_list;
-
#endif /* CONFIG_CFS_BANDWIDTH */
-
#endif /* CONFIG_FAIR_GROUP_SCHED */
- };
- load:其儲存的是程序組中所有程序的權值總和,需要注意子程序計算vruntime時需要用到程序組的load。
CPU執行佇列(struct rq)
每個CPU都有自己的 struct rq 結構,其用於描述在此CPU上所執行的所有程序,其包括一個實時程序佇列和一個根CFS執行佇列,在排程時,排程器首先會先去實時程序佇列找是否有實時程序需要執行,如果沒有才會去CFS執行佇列找是否有進行需要執行,這就是為什麼常說的實時程序優先順序比普通程序高,不僅僅體現在prio優先順序上,還體現在排程器的設計上,至於dl執行佇列,我暫時還不知道有什麼用處,其優先順序比實時程序還高,但是建立程序時如果建立的是dl程序建立會錯誤(具體見sys_fork)。相關推薦
linux排程器原始碼研究
在組排程中,也涉及到排程實體這個概念,它的結構為struct sched_entity(簡稱se),就是圖1 紅黑樹中的se。其實際上就代表了一個排程物件,可以為一個程序,也可以為一個程序組。對於根的紅黑樹而言,一個程序組就相當於一個排程實體,一個程序也相當於一個排程實體。我們可以先看看其結構,如下
linux排程器原始碼分析
引言 排程器作為作業系統的核心部件,具有非常重要的意義,其隨著linux核心的更新也不斷進行著更新。本系列文章通過linux-3.18.3原始碼進行排程器的學習和分析,一步一步將linux現有的排程器原原本本的展現出來。此篇文章作為開篇,主要介紹排程器的原理及重要資料結構。 排程器介紹
linux排程器/proc資訊解讀
轉載自 https://blog.csdn.net/wudongxu/article/details/8574755 注下面的時間或時刻都是從rq->clock中獲得的,而這個值是由update_rq_clock底層cpu來更新的。並且很多資訊是需要核心配置CO
linux排程器_第三代cfs(3)_分解程式碼vruntime的詳解
前面講了排隊的關鍵值,是靠這兩個引數確定的se->vruntime - cfs_rq->min_vruntime,後一個引數是為了建立一個標準,像一本線二本線那樣的概念,所以今天我們來重點討論第一個引數的情況。 vruntime的詳解: 看到虛擬執行時間,肯定會
linux排程器(六)——應用層理解CFS及組排程
上面我們介紹了CFS及組排程相關的主要內容,但可能很多人還跟我一樣有點雲裡霧裡的,下面我們直接從應用層面上也檢視CFS及組排程的效果。首先對於非組排程,決定它們執行時間的唯一因素就是weight,也就是我們知道的nice,我們可以通過renice來重新調整
MapReduce多使用者任務排程器——容量排程器(Capacity Scheduler)原理和原始碼研究
前言:為了研究需要,將Capacity Scheduler和Fair Scheduler的原理和程式碼進行學習,用兩篇文章作為記錄。如有理解錯誤之處,歡迎批評指正。 容量排程器(Capacity Scheduler)是Yahoo公司開發的多使用者排程器。多使用者排程器的使用
Linux高效能網路:協程系列08-協程實現之排程器
目錄 Linux高效能網路:協程系列01-前言 Linux高效能網路:協程系列02-協程的起源 Linux高效能網路:協程系列03-協程的案例 Linux高效能網路:協程系列04-協程實現之工作原理 Linux高效能網路:協程系列05-協程實現之原語操作 Linux高效能網路:協程
Linux CFS排程器之負荷權重load_weight--Linux程序的管理與排程(二十五)
1. 負荷權重 1.1 負荷權重結構struct load_weight 負荷權重用struct load_weight資料結構來表示, 儲存著程序權重值weight。其定義在/include/linux/sched.h, v=4.6, L1195, 如下所示 struct load_weight {
Java定時任務Timer排程器【一】 原始碼分析(圖文詳解版)
就以鬧鐘的例子開頭吧(後續小節皆以鬧鐘為例,所有原始碼只列關鍵部分)。 public class ScheduleDemo { public static void main(String[] args) throws InterruptedException {
Java定時任務Timer排程器【二】 多執行緒原始碼分析(圖文版)
上一節通過一個小例子分析了Timer執行過程,牽涉的執行執行緒雖然只有兩個,但實際場景會比上面複雜一些。 首先通過一張簡單類圖(只列出簡單的依賴關係)看一下Timer暴露的介面。 為了演示Timer所暴露的介面,下面舉一個極端的例子(每一個介面方法面
排程器簡介,以及Linux的排程策略
程序是作業系統虛擬出來的概念,用來組織計算機中的任務。但隨著程序被賦予越來越多的任務,程序好像有了真實的生命,它從誕生就隨著CPU時間執行,直到最終消失。不過,程序的生命都得到了作業系統核心的關照。就好像疲於照顧幾個孩子的母親核心必須做出決定,如何在程序間分配有限的計算資源,最終讓使用者獲得最佳的使用體驗。核
jQuery原始碼研究:選擇器模組所用方法(1)
Sizzle模組第二篇。 順序看下去,看模組所用的方法。 2、createCache()方法,建立一個受限的鍵值對型別快取。返回的是一個函式, function createCache(){ var keys = []; function cache( key
jQuery原始碼研究:選擇器
jQuery的css選擇器,是一大亮點,其實現原始碼也可單獨拎出來作為模組使用。 先看個整體,在jQuery原始碼中在行229-2752區域。 var Sizzle = (function(window){ // 具體實現暫略... })(window) css選擇
系統技術非業餘研究 » Erlang R14B 新增加引數 +swt 控制排程器的喚醒閥值
來自github上的otp原始碼提交訊息: Erlang R14B 新增加引數 +swt very_low|low|medium|high|very_high Set scheduler wakeup threshold. Default is medium The threshold dete
Linux程序排程器的設計--Linux程序的管理與排程(十七)
1 前景回顧 1.1 程序排程 記憶體中儲存了對每個程序的唯一描述, 並通過若干結構與其他程序連線起來. 排程器面對的情形就是這樣, 其任務是在程式之間共享CPU時間, 創造並行執行的錯覺, 該任務分為兩個不同的部分, 其中一個
小米開原始檔管理器MiCodeFileExplorer-原始碼研究(9)-入口分析
AndroidManifest.xml是Android應用程式最重要的配置檔案。入口檔案和intent-filter<application android:icon="@drawable/icon" android:label="@st
分散式任務排程平臺XXL-JOB--原始碼解析六:executor執行器原始碼解析之啟動jetty服務接受請求並執行排程
啟動jetty服務並執行排程 1.1 啟動一個jetty server服務 首先載入ServerConnector, 載入HandlerCollection並設定handlers, 其中connector負責接受客戶端http請求, 而handlers負責處理客戶
Dubbo原始碼解析(一)請求排程器 Dispatcher
排程器 Dispatcher 排程策略 all 所有訊息都派發到執行緒池,包括請求,響應,連線事件,斷開事件,心跳等。 direct 所有訊息都不派發到執行緒池,全部在 IO 執行緒上直接執行。 message 只有請求響應訊息派發到執行緒池,其它連線斷開
linux rt排程器
RT(RealTime scheduler)實時排程器,對應使用者設定的排程策略為 SCHED_FIFO/SCHED_RR。 SCHED_FIFO 先進先出佇列排程,高優先順序任務一直執行,直到任務阻塞,或者主動退出,或者被更高優先順序 任務搶佔。 SCHED_RR 時間片
Linux CFS排程器之虛擬時鐘vruntime與排程延遲--Linux程序的管理與排程(二十六)
CFS負責處理普通非實時程序, 這類程序是我們linux中最普遍的程序, 今天我們把注意力轉向CFS的虛擬時鐘 1 前景回顧 1.1 CFS排程器類 Linux核心使用CFS是來排程我們最常見的普通程序, 其所屬排程器類為fai