最近发现了github上有个作者仓库中竟然有多个调度器,分了下源代码。
作者很牛逼。
其他调度器
Baby scheduler
作者称,baby scheduler是一个很基本且轻量级但有高性能的CPU调度器,可以用于学习Linux内核调度器;也正是因为其是basic的,所以很多features是没有的。
主要分三种:
Deadline Scheduling (dl) - main
Virtual Run Time Scheduling (vrt)
Round Robin Scheduling (rr)
baby调度器的throughput较高,因为其负载均衡只在一个CPU(CPU0)上进行扫描其他CPUs,然后每次迁移至迁移一个task(每个tick周期)。三种调度器的主要区别就是pick next的选择算法不同(排列顺序本身就不同)。
该调度器中CFS的痕迹很多,基本上就是沿用CFS。主要优化点在于负载均衡。
CacULE scheduler
该调度器也是一组CFS补丁,主要专注于interactivity score机制,受ULE调度器(freebsd)启发得来。
主要特征:
- 每个CPU都有自己的runqueue(这个CFS中也是)
- NORMAL runqueue是link-list链表形式的(不是CFS中的红黑树)
- RT和其他runqueue和Linux中已有的类似
- wake up之后preempt抢占的依据是其interactive score得分更高就可以抢占
cacule中的ineractive score就是源自ULE中,这个我分析过。
可以参考ULE源代码。
CacULE调度器并不是为了替换CFS,主要就是改变了pick next的算法,根据interactive score来选择下一个任务。
TT scheduler
task type调度器,也是基于CFS打补丁,不过其将task分类,分了5大类,且这种分类是根据task的行为来实时判断:
#define TT_REALTIME 0
#define TT_INTERACTIVE 1
#define TT_NO_TYPE 2
#define TT_CPU_BOUND 3
#define TT_BATCH 4
-
那么它是如何判断task type的:
-
判断实时进程:burst before sleep is <= 4ms
#define INTERACTIVE_HRRN 2U #define RT_WAIT_DELTA 800000U //8ms #define RT_BURST_DELTA 2000000U //2ms #define RT_BURST_MAX 4000000U //4ms if (LEQ(ttn->burst, RT_BURST_MAX) && LEQ(ttn->curr_burst, RT_BURST_MAX)) return true;
-
判断cpu_bound类型:
根据其运行时间占总时间(wait time + vruntime)的百分比,超过80%则是cpu_bound类型
_hrrn_percent = ttn->vruntime * 100ULL; _hrrn_percent /= ttn->wait_time + ttn->vruntime; return (GEQ(_hrrn_percent, 80ULL));
-
判断batch类型:
根据vruntime占总时间百分比,占比50%~80%之间是batch类型
_hrrn = (ttn->wait_time + ttn->vruntime) / ttn->vruntime; static inline bool is_batch(struct tt_node *ttn, u64 _hrrn) { // HRRN > 50% return (LES(_hrrn, 2ULL)); }
-
判断interactive类型:
根据vruntime占总时间百分比,小于50%
_hrrn = (ttn->wait_time + ttn->vruntime) / ttn->vruntime; static inline bool is_interactive(struct tt_node *ttn, u64 now, u64 _hrrn) { u64 wait; if (LES(_hrrn, (u64) INTERACTIVE_HRRN)) return false; wait = now - se_of(ttn)->exec_start; if (wait && EQ_D(wait, ttn->prev_wait_time, RT_WAIT_DELTA)) return false; return true; }
-
-
分出了task type后又是如何干扰调度器的:
tt scheduler中维护了以个candidate候选队列:但只有realtime和interactive两种类型的任务才能进入候选队列。
struct global_candidate { struct rq *rq; struct tt_node *candidate; u64 hrrn; // for update raw_spinlock_t lock; };
然后再newidle_balance或者nohz_idle_balance的时候,idle空闲cpu就能够从candidate候选队列中拉取任务来执行
int idle_pull_global_candidate(struct rq *dist_rq);
此外:tt scheduler在负载均衡是选择cpu时也是根据rq的nr_running来判断load的,并没有CFS的load_avg那么复杂。