linux内核分析之调度算法(一)

linux调度算法在2.6.32中采用调度类实现模块式的调度方式。这样,能够很好的加入新的调度算法。

linux调度器是以模块方式提供的,这样做的目的是允许不同类型的进程可以有针对性地选择调度算法。这种模块化结构被称为调度器类,他允许多种不同哦可动态添加的调度算法并存,调度属于自己范畴的进程。每个调度器都有一个优先级,调度代码会按照优先级遍历调度类,拥有一个可执行进程的最高优先级的调度器类胜出,去选择下面要执行的那个程序。

linux上主要有两大类调度算法,CFS(完全公平调度算法)和实时调度算法。宏SCHED_NOMAL主要用于CFS调度,而SCHED_FIFO和SCHED_RR主要用于实时调度。如下面的宏定义:

/*
 * Scheduling policies
 */
 /*支援Real-Time Task的排程,包括有SCHED_FIFO與SCHED_RR. 
 */
 
 /*(也稱為SCHED_OTHER): 主要用以排程
 一般目的的Task.*/
#define SCHED_NORMAL		0
#define SCHED_FIFO		1
/*task預設的 Time Slice長度為100 msecs*/
#define SCHED_RR		2
/*主要用以讓Task可以延長執行的時間
(Time Slice),減少被中斷發生Task Context-Switch
的次數.藉此可以提高 Cache的利用率 
(每次Context-Switch都會導致Cache-Flush). 比
較適合用在固定週期執行的Batch Jobs任
務主機上,而不適合用在需要使用者互
動的產品 (會由於Task切換的延遲,而
感覺到系統效能不佳或是反應太慢).*/
#define SCHED_BATCH		3
/* SCHED_ISO: reserved but not implemented yet */
/*為系統中的Idle Task排程.*/
#define SCHED_IDLE		5

linux调度算法实现的高层数据结构主要有运行实体、调度类、运行队列,下面我们主要看看这几个数据结构的字段和意义。

运行实体,rq结构体每个cpu有一个,主要存储一些基本的用于调度的信息,包括实时调度的和CFS调度的

 /*每个处理器都会配置一个rq*/
struct rq {
	/* runqueue lock: */
	spinlock_t lock;

	/*
	 * nr_running and cpu_load should be in the same cacheline because
	 * remote CPUs use both these fields when doing load calculation.
	 */
	 /*用以记录目前处理器rq中执行task的数量*/
	unsigned long nr_running;
	#define CPU_LOAD_IDX_MAX 5
	/*用以表示处理器的负载,在每个处理器的rq中
	都会有对应到该处理器的cpu_load参数配置,在每次
	处理器触发scheduler tick时,都会呼叫函数
	update_cpu_load_active,进行cpu_load的更新。在系统初始化的时候
	会呼叫函数sched_init把rq的cpu_load array初始化为0.
	了解他的更新方式最好的方式是通过函数update_cpu_load,公式如下澹?
	cpu_load[0]会直接等待rq中load.weight的值。
	cpu_load[1]=(cpu_load[1]*(2-1)+cpu_load[0])/2
	cpu_load[2]=(cpu_load[2]*(4-1)+cpu_load[0])/4
	cpu_load[3]=(cpu_load[3]*(8-1)+cpu_load[0])/8
	cpu_load[4]=(cpu_load[4]*(16-1)+cpu_load[0]/16
	呼叫函数this_cpu_load时,所返回的cpu load值是cpu_load[0]
	而在进行cpu blance或migration时,就会呼叫函数
	source_load target_load取得对该处理器cpu_load index值,
	来进行计算*/
	unsigned long cpu_load[CPU_LOAD_IDX_MAX];
#ifdef CONFIG_NO_HZ
	unsigned long last_tick_seen;
	unsigned char in_nohz_recently;
#endif
	/* capture load from *all* tasks on this cpu: */
	/*load->weight值,会是目前所执行的schedule entity的
	load->weight的总和,也就是说rq的load->weight越高,
	也表示所负责的排程单元load->weight总和越高
	表示处理器所负荷的执行单元也越重*/
	struct load_weight load;
	/*在每次scheduler tick中呼叫update_cpu_load时,
	这个值就增加一,可以用来反馈目前cpu
	load更新的次数*/
	unsigned long nr_load_updates;
	/*用来累加处理器进行context switch的次数,会在
	函数schedule呼叫时进行累加,并可以通过函数
	nr_context_switches统计目前所有处理器总共的context switch
	次数,或是可以透过查看档案/proc/stat中的ctxt位得知目前
	整个系统触发context switch的次数*/
	u64 nr_switches;
	
	u64 nr_migrations_in;
	/*为cfs fair scheduling class 的rq*/
	struct cfs_rq cfs;
	/*为real-time scheduling class 的rq*/
	struct rt_rq rt;
	
/*用以支援可以group cfs tasks的机制*/
#ifdef CONFIG_FAIR_GROUP_SCHED
	/* list of leaf cfs_rq on this cpu: */
	/*在有设置fair group scheduling 的环境下,
	会基于原本cfs rq中包含有若干task的group
	所成的排程集合,也就是说当有一个group a
	就会有自己的cfs rq用来排程自己所属的tasks,
	而属于这group a的tasks所使用到的处理器时间
	就会以这group a总共所分的的时间为上限。
	基于cgroup的fair group scheduling 架构,可以创造出
	有阶层性的task组织,根据不同task的功能群组化
	在配置给该群主对应的处理器资源,让属于
	该群主下的task可以透过rq机制排程。使用属于
	该群主下的资源。
	这个变数主要是管理CFS RQ list,操作上可以透过函数
	list_add_leaf_cfs_rq把一个group cfs rq加入到list中,或透过
	函数list_del_leaf_cfs_rq把一个group cfs rq移除,并可以
	透过for_each_leaf_cfs_rq把一个rq上得所有leaf cfs_rq走一遍
	*/
	struct list_head leaf_cfs_rq_list;
#endif
/*用以支援可以group real-time tasks的机制*/
#ifdef CONFIG_RT_GROUP_SCHED
	/*类似leaf_cfs_rq_list所扮演的角色,只是这里
	是针对属于real-time的task,在实际操作上可以
	透过函数list_add_leaf_rt_rq,list_del_leaf_rt_rq或
	巨集for_each_leaf_rt_rq*/
	struct list_head leaf_rt_rq_list;
#endif

	/*
	 * This is part of a global counter where only the total sum
	 * over all CPUs matters. A task can increase this counter on
	 * one CPU and if it got migrated afterwards it may decrease
	 * it on another CPU. Always updated under the runqueue lock:
	 */
	 /*一般来说,linux kernel 的task状态可以为TASK_RUNNING
	 TASK_INTERRUPTIBLE(sleep),
	 TASK_UNINTERRUPTIBLE(Deactivate Task,此时Task会从rq中
	 移除)或TASK_STOPPED.
	 透过这个变数会统计目前rq中有多少task属于
	 TASK_UNINTERRUPTIBLE的状态。当呼叫函数
	 active_task时,会把nr_uninterruptible值减一,并透过 该函数
	enqueue_task把对应的task依据所在的scheduling class
	放在 对应的rq中,并把目前rq中nr_running值加一*/
	unsigned long nr_uninterruptible;
	/*curr:指向目前处理器正在执行的task;
	idle:指向属于idle-task scheduling class 的idle task;
	stop:指向目前最高等级属于stop-task scheduling class
	的task;*/
	struct task_struct *curr, *idle;
	/*基于处理器的jiffies值,用以记录下次进行处理器
	balancing 的时间点*/
	unsigned long next_balance;
	/*用以存储context-switch发生时,前一个task的memory management
	结构并可用在函数finish_task_switch中,透过函数mmdrop释放前一个
	task的记忆体资源*/	
	struct mm_struct *prev_mm;
	/*用以记录目前rq的clock值,基本上该值会等于透过sched_clock_cpu
	(cpu_of(rq))的回传值,并会在每次呼叫scheduler_tick时透过
	函数update_rq_clock更新目前rq clock值。
	在实作部分,函数sched_clock_cpu会透过sched_clock_local或
	ched_clock_remote取得对应的sched_clock_data,而处理的sched_clock_data
	值,会透过函数sched_clock_tick在每次呼叫scheduler_tick时进行更新;
	*/
	u64 clock;
	/*用以记录目前rq中有多少task处于等待i/o的sleep状态
	在实际的使用上,例如当driver接受来自task的调用,但处于等待i/o
	回复的阶段时,为了充分利用处理器的执行资源,这时
	就可以在driver中呼叫函数io_schedule,此时
	就会把目前rq中的nr_iowait加一,并设定目前task的io_wait为1
	然后触发scheduling 让其他task有机会可以得到处理器执行时间*/
	atomic_t nr_iowait;

#ifdef CONFIG_SMP
	/*root domain是基于多核心架构下的机制,
	会由rq结构记住目前采用的root domain,其中包括了
	目前的cpu mask(包括span,online rt overload), reference count 跟cpupri
	当root domain有被rq参考到时,refcount 就加一,反之就减一。而cpu
	mask span表示rq可挂上的cpu mask,noline为rq目前已经排程的
	cpu mask cpu上执行real-time task.可以参考函数pull_rt_task,当一个rq中属于
	real-time的task已经执行完毕,就会透过函数pull_rt_task从该
	rq中属于rto_mask cpu mask 可以执行的处理器上,找出是否有一个处理器
	有大于一个以上的real-time task,若有就会转到目前这个执行完成
	real-time task 的处理器上
	而cpupri不同于Task本身有区分140個(0-139)
	Task Priority (0-99為RT Priority 而 100-139為Nice值 -20-19). 
	CPU Priority本身有102個Priority (包括,-1 為Invalid,
	0為Idle,1為Normal,2-101對應到Real-Time Priority 0-99).
	參考函式convert_prio, Task Priority如果是 140就會對應到
	CPU Idle,如果是大於等於100就會對應到CPU Normal,
	若是Task Priority介於0-99之間,就會對應到CPU Real-Time Priority 101-2之間.) 
	在實際的操作上,例如可以透過函式cpupri_find
	帶入一個要插入的Real-Time Task,此時就會依據cpupri中
	pri_to_cpu選擇一個目前執行Real-Time Task且該Task
	的優先級比目前要插入的Task更低的處理器,
	並透過CPU Mask(lowest_mask)返回目前可以選擇的處理器Mask.
	實作的部份可以參考檔案kernel/sched_cpupri.c.
	在初始化的過程中,會透過函式sched_init呼叫函式init_defrootdomain,
	對Root Domain與 CPU Priority機制進行初始化.
	*/
	struct root_domain *rd;
	/*Schedule Domain是基於多核心架構下的機制.
	每個處理器都會有一個基礎的Scheduling Domain,
	Scheduling Domain可以有階層性的架構,透過parent
	可以找到上一層的Domain,或是透過child找到
	下一層的 Domain (NULL表示結尾.).並可透過span
	栏位,表示這個Domain所能涵蓋的處理器範圍.
	通常Base Domain會涵蓋系統中所有處理器的個數,
	而Child Domain所能涵蓋的處理器個數不超過它的
	Parent Domain. 而當在進行Scheduling Domain 中的Task Balance
	時,就會以該Domain所能涵蓋的處理器為最大範圍.
	同時,每個Schedule Domain都會包括一個或一個以上的
	CPU Groups (結構為struct sched_group),並透過next變數把
	CPU Groups串連在一起(成為一個單向的Circular linked list),
	每個CPU Group都會有變數cpumask來定义這個CPU Group
	所涵蓋的處理器範圍.並且CPU Group所包括的處理器
	範圍,必需涵蓋在所屬的Schedule Domain處理器範圍中.
	當進行Scheduling Domain的Balancing時,會以其下的CPU Groups
	為單位,根據cpu_power (會是該Group所涵蓋的處理器
	Tasks Loading的總和)來比較不同的CPU Groups的負荷,
	以進行Tasks的移動,達到Balancing的目的.
	在有支援SMP的架構下,會在函式sched_init中,呼叫open_softirq,
	註冊 SCHED_SOFTIRQ Software IRQ与其对应的 Callback函式 
	run_rebalance_domains. 並會在每次呼叫函式scheduler_tick時,
	透過函式trigger_load_balance确认是否目前的jiffies值已經
	大於RunQueue下一次要觸發Load Balance的next_balance時間值,
	並透過函式raise_softirq觸發SCHED_SOFTIRQ Software IRQ. 
	在Software IRQ觸發後,就會呼叫函式run_rebalance_domains,
	並在函式rebalance_domains中,進行后续處理器上的
	Scheduling Domain Load Balance動作.
	有關Scheduling Domain進一步的內容,也可以參考
	Linux Kernel文件 Documentation/scheduler/sched-domains.txt.
	*/
	struct sched_domain *sd;
	/*這值會等於函式idle_cpu的返回值,如果為1表示
	目前CPU RunQueue中執行的為Idle Task. 反之為0,
	則表示處理器執行的不是Idle Task (也就是說
	處理器正在忙碌中.).*/
	unsigned char idle_at_tick;
	/* For active balancing */
	/*若這值不為0,表示會有在Schedule排程動作
	結束前,要呼叫的收尾函式. (实作為inline函式
	post_schedule in kernel/sched.c),目前只有Real-Time Scheduling 
	Class有支援這個機制(會呼叫函式has_pushable_tasks 
	in kernel/sched_rt.c).*/
	int post_schedule;
	/*當RunQueue中此值為1,表示這個RunQueue正在進行
	Fair Scheduling的Load Balance,此時會呼叫stop_one_cpu_nowait
	暫停該RunQueue所屬處理器的排程,並透過函式
	active_load_balance_cpu_stop,把Tasks從最忙碌的處理器,
	移到Idle的處理器上執行.*/
	int active_balance;
	/*用以儲存目前進入Idle且負責進行 Load Balance
	流程的處理器ID. 呼叫的流程為,在呼叫函式schedule時,
	若該處理器RunQueue的nr_running為0 (也就是目前沒有
	正在執行的Task),就會呼叫idle_balance,並觸發後續Load 
	Balance流程.*/
	int push_cpu;
	/* cpu of this runqueue: */
	/*用以儲存目前运作這個RunQueue的處理器ID*/
	int cpu;
	/*為1表示目前此RunQueue有在對應的處理器掛上
	並執行.*/
	int online;
	/*如果RunQueue中目前有Task正在執行,這個值會
	等於目前該RunQueue的Load Weight除以目前RunQueue
	中Task數目的均值. 
	(rq->avg_load_per_task = rq->load.weight / nr_running;).*/
	unsigned long avg_load_per_task;

	struct task_struct *migration_thread;
	struct list_head migration_queue;
	/*這個值會由Real-Time Scheduling Class呼叫函式
	update_curr_rt,用以統計目前Real-Time Task執行時間的
	均值,在這函式中會以目前RunQueue的clock_task
	減去目前Task執行的起始時間,取得執行時間的
	Delta值. (delta_exec = rq->clock_task – curr->se.exec_start; ).
	在透過函式sched_rt_avg_update把這Delta值跟原本
	RunQueue中的rt_avg值取平均值. 以運作的週期來看,
	這個值可反應目前系統中Real-Time Task平均被
	分配到的執行時間值.*/
	u64 rt_avg;
	/*這個值主要在函式sched_avg_update更新,以笔者手中
	的Linux Kernel 2.6.38.6的實作來說,當RunQueue Clock
	減去age_stamp大於 0.5秒 (=sched_avg_period),就會把這值
	累加0.5秒 (單位都是nanoseconds). 從函式scale_rt_power
	的實作來說,age_stamp值離RunQueue Clock越遠,表示total
	值越大,available值也越大,而函式scale_rt_power返回的
	div_u64計算結果也越大,最終 RunQueue的cpu_power
	與Scheduling Domain中的Scheduling Group的cpu_power
	值也就越大. (可參考函式update_cpu_power的實作).*/
	u64 age_stamp;
	/*這值會在觸發Scheduling時,若判斷目前處理器
	RunQueue沒有正在運作的Task,就會透過函式
	idle_balance更新這值為為目前RunQueue的clock值.
	可用以表示這個處理器是何時進入到Idle的
	狀態*/
	u64 idle_stamp;
	/*會在有Task運作且idle_stamp不為0 (表示前一個
	狀態是在Idle)時以目前RunQueue的clock減去
	idle_stmp所計算出的Delta值為依據,更新這個值
	. 可反應目前處理器進入Idle狀態的時間長短*/
	u64 avg_idle;
#endif

	/* calc_load related fields */
	/*用以記錄下一次計算CPU Load的時間,初始值
	為目前的jiffies加上五秒與1次的Scheduling Tick的
	間隔 (=jiffies + LOAD_FREQ,且LOAD_FREQ=(5*HZ+1))*/
	unsigned long calc_load_update;
	/*會等於RunQueue中nr_running與nr_uninterruptible的
	總和.(可參考函式calc_load_fold_active).*/
	long calc_load_active;

#ifdef CONFIG_SCHED_HRTICK
#ifdef CONFIG_SMP
	/*在函式init_rq_hrtick初始化RunQueue High-Resolution 
	Tick時,此值預設為0.
	在函式hrtick_start中,會判斷目前觸發的RunQueue
	跟目前處理器所使用的RunQueue是否一致,
	若是,就直接呼叫函式hrtimer_restart,反之就會
	依據RunQueue中hrtick_csd_pending的值,如果
	hrtick_csd_pending為0,就會透過函式
	__smp_call_function_single讓RunQueue所在的另一個
	處理器執行rq->hrtick_csd.func 所指到的函式 
	__hrtick_start. 並等待該處理器執行完畢後,
	才重新把hrtick_csd_pending設定為1.
	也就是說, RunQueue的hrtick_csd_pending是用來作為
	SMP架構下,由處理器A觸發處理器B執行
	_hrtick_start函式的一個保護機制.而有關在
	SMP下如何由一個處理器觸發另一個處理器
	執行函式的機制,可以參考kernel/smp.c中
	相關smp_call_function_xxxxxxx的實作.s*/
	int hrtick_csd_pending;
	/*用以儲存hrtick機制中,要跨處理器執行的
	函式結構.*/
	struct call_single_data hrtick_csd;
#endif
	/*為High-Resolution Tick的结构,會透過函式
	hrtimer_init初始化.*/
	struct hrtimer hrtick_timer;
#endif

#ifdef CONFIG_SCHEDSTATS
	/* latency stats */
	/*為Scheduling Info.的統計結構,可以參考
	include/linux/sched.h中的宣告. 例如在每次觸發
	Schedule時,呼叫函式schedule_debug對上一個Task
	的lock_depth進行確認(Fork一個新的Process 時,
	會把此值預設為-1就是No-Lock,當呼叫
	Kernel Lock時, 就會把Current Task的lock_depth加一.),
	若lock_depth>=0,就會累加Scheduling Info.的bkl_count值,
	用以代表Task Blocking的次數.*/
	struct sched_info rq_sched_info;
	/*可用以表示RunQueue中的Task所得到CPU執行
	時間的累加值.
	在發生Task Switch時,會透過sched_info_switch呼叫
	sched_info_arrive並以目前RunQueue Clock值更新
	Task 的sched_info.last_arrival時間,而在Task所分配時間
	結束後,會在函式sched_info_depart中以現在的
	RunQueue Clock值減去Task的sched_info.last_arrival
	時間值,得到的 Delta作為變數rq_cpu_time的累
	加值.*/
	unsigned long long rq_cpu_time;
	/* could above be rq->cfs_rq.exec_clock + rq->rt_rq.rt_runtime ? */

	/* sys_sched_yield() stats */
	/*用以統計呼叫System Call sys_sched_yield的次數.*/
	unsigned int yld_count;

	/* schedule() stats */
	unsigned int sched_switch;
	/*可用以統計觸發Scheduling的次數. 在每次觸發
	Scheduling時,會透過函式schedule呼叫schedule_debug,
	呼叫schedstat_inc對這變數進行累加.*/
	unsigned int sched_count;
	/*可用以統計進入到Idle Task的次數. 會在函式
	pick_next_task_idle中,呼叫schedstat_inc對這變數進行
	累加.*/
	unsigned int sched_goidle;

	/* try_to_wake_up() stats */
	/*用以統計Wake Up Task的次數.*/
	unsigned int ttwu_count;
	/*用以統計Wake Up 同一個處理器Task的次數.*/
	unsigned int ttwu_local;

	/* BKL stats */
	unsigned int bkl_count;
#endif
};

调度类,sched_class为对模块编程的上层支持,对于每个linux新添加进来的调度算法都需要有自己的调度类实例。

/*CFS排程機制在設計時,考慮到排程機制的
彈性,定義了Scheduler Class的機制,讓排程機制
可以根據設計的需求,延伸不同的排程模
組進來,每個新加入的排程機制都必須要
提供Scheduler Class的實作,結構為 struct sched_class*/
struct sched_class {
	/*會指向下一個Scheduling Class,以筆者所採用
	的Linux Kernel 2.6.38.6而言,Scheduling Class的順序為
	stop_sched_class->rt_sched_class->fair_sched_class->idle_sched_class*/
	const struct sched_class *next;
	/*當Task屬於Runnable狀態時,就會呼叫這個函式
	把Task配置到RunQueue RBTree中,進行排程動作,
	並呼叫inc_nr_running將RunQueue中nr_running的值
	加一.(nr_running用以代表目前RunQueue有多少
	Runnable Task進行排程)*/
	void (*enqueue_task) (struct rq *rq, struct task_struct *p, int wakeup);
	/*當Task不需要執行時,就會呼叫這個函式
	把Task從RunQueue RBTree中移除,並呼叫
	dec_nr_running將RunQueue中nr_running的值減一.*/
	void (*dequeue_task) (struct rq *rq, struct task_struct *p, int sleep);
	/*用以暫停目前正在執行中的Task,如果
	sysctl_sched_compat_yield有設定,就會找出目前
	RBTree中最右邊的Task(也就是vrruntime最多
	的Task),讓目前Task的vrruntime值等於最右邊
	Task值的vrruntime加一(可參考:
	se->vruntime = rightmost->vruntime + 1),如此在下次
	排程觸發時就會透過函式put_prev_task把目前
	的Task放到RBTree的最右邊,也就等同於暫停
	Task,讓該Task下次被執行到的機會最低.*/
	void (*yield_task) (struct rq *rq);
	/*用以決定一個Task是否可以中斷目前正在
	運作的Task,取得執行權.以CFS本身的實作來說
	(in sched_fair.c).如果想要取代目前Task的Task本身
	的Scheduling Policy為 Batch或是Idle時,會直接返回,
	不會用來取代目前正在執行中的Task.反之,
	如果目前正在執行中的Task的Scheduling Policy
	為Idle,就會直接由所傳入的Task取代目前正
	在執行的Task.*/
	void (*check_preempt_curr) (struct rq *rq, struct task_struct *p, int flags);
	/*用以在排程觸發時,從RunQueue RBTree中,
	取出符合目前Scheduling邏輯的下一個要
	被執行的Task.*/
	struct task_struct * (*pick_next_task) (struct rq *rq);
	/*用以在排程觸發時,把上一個執行完畢的
	Task放到目前RunQueue RBTree中對應的位置.*/
	void (*put_prev_task) (struct rq *rq, struct task_struct *p);

#ifdef CONFIG_SMP
	/*通常用在執行一個新的程序,或是WakeUp
	一個Task時,會根據目前SMP下每個處理器的
	負荷,決定Task是否要切換到另一個處理器
	的RunQueue去執行,執行時會返回最後目標
	處理器的值.*/
	int  (*select_task_rq)(struct task_struct *p, int sd_flag, int flags);

	unsigned long (*load_balance) (struct rq *this_rq, int this_cpu,
			struct rq *busiest, unsigned long max_load_move,
			struct sched_domain *sd, enum cpu_idle_type idle,
			int *all_pinned, int *this_best_prio);

	int (*move_one_task) (struct rq *this_rq, int this_cpu,
			      struct rq *busiest, struct sched_domain *sd,
			      enum cpu_idle_type idle);
	void (*pre_schedule) (struct rq *this_rq, struct task_struct *task);
	void (*post_schedule) (struct rq *this_rq);
	void (*task_wake_up) (struct rq *this_rq, struct task_struct *task);

	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
	/*這個函式用以改變Task目前所屬的Scheduling
	Class與改變Task Group.*/
	void (*set_curr_task) (struct rq *rq);
	/*這是Scheduler的 Timer Tick來源,系統中觸發的
	Scheduling Tick會呼叫這個函式 (看HZ設定多少,
	100就是每秒呼叫這函式100次,1000就是每秒
	呼叫這函式1000次),
	用以讓排程機制可以決定哪些Task應該要配
	執行與哪些Task應該要被移出RunQueue.*/
	void (*task_tick) (struct rq *rq, struct task_struct *p, int queued);
	void (*task_new) (struct rq *rq, struct task_struct *p);

	void (*switched_from) (struct rq *this_rq, struct task_struct *task,
			       int running);
	void (*switched_to) (struct rq *this_rq, struct task_struct *task,
			     int running);
	void (*prio_changed) (struct rq *this_rq, struct task_struct *task,
			     int oldprio, int running);

	unsigned int (*get_rr_interval) (struct task_struct *task);

#ifdef CONFIG_FAIR_GROUP_SCHED
	void (*moved_group) (struct task_struct *p);
#endif
};

调度实体,调度实体用于调度时间记账,linux中CFS和实时调度使用不同的调度实体。调度运行队列,对于不用的调度算法同样运用不用的运行队列,对于CFS调度,运用的是红黑树,而对于实时调度为组链表。在后面具体的调度算法介绍中我们会看到他们的运用。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值