|
说明
|
task group
|
进程组,为了支持CGroup控制cpu,引入了组调度的概念,task group即包含所有要控制cpu的task集合以及配置信息。
|
group task
|
本文的专有名词,是指一个进程组下的task,这些task受一个CGroup控制
|
cfs_bandwidth
|
task_group的重要成员,包含了所要控制cpu的period,quota,定时器等信息
|
throttle
|
当group se在一个设定的时间周期内,消耗完了指定的cpu配额,则将其从cpu运行队列中移出,并不再调度。
注意:处于throttled状态的task仍是Ready状态的,只是不在rq上。
|
unthrottle
|
将throttle状态的group se,重新加入到cpu运行队列中调度。
|
struct cfs_rq { struct rb_root tasks_timeline; // 以vruntime为key,se为value的红黑树根节点,schedule时,cfs调度算法每次从这里挑选vruntime最小的se投入运行 struct rb_node* rb_leftmost; // 最左的叶子节点,即vruntime最小的se,直接取这个节点以加快速度 sched_entity* curr; // cfs_rq中当前正在运行的se struct rq* rq; /* cpu runqueue to which this cfs_rq is attached */ struct task_group* tg; /* group that "owns" this runqueue */ int throttled; // 表示该cfs_rq所属的group se是否被throttled s64 runtime_remaining; // cfs_rq从全局时间池申请的时间片剩余时间,当剩余时间小于等于0的时候,就需要重新申请时间片 }; struct sched_entity { unsigned int on_rq; // se是否在rq上,不在的话即使task是Ready状态也不会投入运行的 u64 vruntime; // cpu运行时长,cfs调度算法总是选择该值最小的se投入运行 /* rq on which this entity is (to be) queued: */ struct cfs_rq* cfs_rq; // se所在的cfs_rq,如果是普通task se,等于rq的cfs_rq,如果是group中的task,则等于group的cfs_rq /* rq "owned" by this entity/group: */ struct cfs_rq* my_q; // my_q == NULL表示是一个普通task se,否则表示是一个group se,my_q指向group的cfs_rq }; struct task { struct sched_entity se; }; struct rq { struct cfs_rq cfs; // 所有要调度的se都挂在cfs rq中 struct task_struct* curr; // 当前cpu上运行的task };
本文中的sched_entity定义比《极简cfs公平调度算法》中的要复杂些,各种cfs_rq容易搞混,这里讲一下cfs公平调度挑选group task调度流程(只用到了my_q这个cfs_rq),以梳理清楚其关系
task_struct *pick_next_task_fair(struct rq *rq) { struct cfs_rq *cfs_rq = &rq->cfs; // 开始的cfs_rq为rq的cfs do { se = pick_next_entity(cfs_rq); // 《极简cfs公平调度算法》中讲过这个函数,其就是取cfs_rq->rb_leftmost,即最小vruntime的se cfs_rq = group_cfs_rq(se); // 取se.my_q,如果是普通的task se,cfs_rq = NULL,这里就会退出循环,如果是group se,cfs_rq = group_se.my_q,然后在group se的cfs_rq中继续寻找vruntime最小的se } while (cfs_rq); return task_of(se); } cfs_rq *group_cfs_rq(struct sched_entity *grp) { return grp->my_q; }
struct cfs_bandwidth { ktime_t period; // cpu.cfs_period_us的值 u64 quota; // cpu.cfs_quota_us的值 u64 runtime; // 当前周期内剩余的quota时间 int timer_active; // period_timer是否激活 struct hrtimer period_timer; // 定时分配cpu quota的定时器,定时器触发时会更新runtime }; struct task_group { struct sched_entity** se; /* schedulable entities of this group on each cpu */ struct cfs_rq** cfs_rq; /* runqueue "owned" by this group on each cpu */ struct cfs_bandwidth cfs_bandwidth; // 管理记录CGroup控制cpu的信息 };
1> task_group.se是一个数组,每个cpu都有一个其对应的group se
void update_curr(struct cfs_rq* cfs_rq) { struct sched_entity* curr = cfs_rq->curr; curr->vruntime += delta_exec; // 增加se的运行时间 account_cfs_rq_runtime(cfs_rq, delta_exec); }
void account_cfs_rq_runtime(struct cfs_rq* cfs_rq, u64 delta_exec) { cfs_rq->runtime_remaining -= delta_exec; if (cfs_rq->runtime_remaining > 0) return; // 如果runtime_remaining不够了,则要向task group分配cpu quota,分配失败则设置task的thread flag为TIF_NEED_RESCHED,表示需要重新调度 if (!assign_cfs_rq_runtime(cfs_rq) && likely(cfs_rq->curr)) resched_curr(cfs_rq->rq); }
/* returns 0 on failure to allocate runtime */ int assign_cfs_rq_runtime(struct cfs_rq* cfs_rq) { struct cfs_bandwidth* cfs_b = cfs_rq->tg->cfs_bandwidth;; // 如果有限制cpu,则减去最小分配时间,如果cfs_b->runtime为0,那就没有时间可分配了,本函数就会返回0,表示分配失败 amount = min(cfs_b->runtime, min_amount); cfs_b->runtime -= amount; cfs_rq->runtime_remaining += amount; return cfs_rq->runtime_remaining > 0; }
void schedule() { prev = rq->curr; put_prev_task_fair(rq, prev); // 选择下一个task并切换运行 next = pick_next_task(rq); context_switch(rq, prev, next); }
void put_prev_task_fair(struct rq* rq, struct task_struct* prev) { struct sched_entity* se = &prev->se; put_prev_entity(se->cfs_rq, se); } void put_prev_entity(struct cfs_rq* cfs_rq, struct sched_entity* prev) { check_cfs_rq_runtime(cfs_rq); }
void check_cfs_rq_runtime(struct cfs_rq* cfs_rq) { if (cfs_rq->runtime_remaining > 0) return; throttle_cfs_rq(cfs_rq); }
void throttle_cfs_rq(struct cfs_rq* cfs_rq) { struct sched_entity* se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))]; // 取对应cpu rq上的group se dequeue_entity(se->cfs_rq, se, DEQUEUE_SLEEP); //从cpu rq中删除group se cfs_rq->throttled = 1; // 标记group cfs_rq被throttled }
viod init_cfs_bandwidth(struct cfs_bandwidth* cfs_b) { hrtimer_init(&cfs_b->period_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); cfs_b->period_timer.function = sched_cfs_period_timer; }
enum hrtimer_restart sched_cfs_period_timer(struct hrtimer* timer) { idle = do_sched_cfs_period_timer(cfs_b, overrun); return idle ? HRTIMER_NORESTART : HRTIMER_RESTART; }
int do_sched_cfs_period_timer(struct cfs_bandwidth* cfs_b, int overrun) { __refill_cfs_bandwidth_runtime(cfs_b); distribute_cfs_runtime(cfs_b, runtime, runtime_expires); }
void __refill_cfs_bandwidth_runtime(struct cfs_bandwidth* cfs_b) { cfs_b->runtime = cfs_b->quota; }
文章来源:https://www.toymoban.com/news/detail-413870.html
u64 distribute_cfs_runtime(struct cfs_bandwidth* cfs_b, u64 remaining, u64 expires) { struct cfs_rq* cfs_rq; list_for_each_entry_rcu(cfs_rq, &cfs_b->throttled_cfs_rq, throttled_list) { unthrottle_cfs_rq(cfs_rq); } } void unthrottle_cfs_rq(struct cfs_rq* cfs_rq) { se = cfs_rq->tg->se[cpu_of(rq_of(cfs_rq))]; enqueue_entity(cfs_rq, se, ENQUEUE_WAKEUP); // 将se加回rq.cfs_rq的红黑树上 }
文章来源地址https://www.toymoban.com/news/detail-413870.html
到了这里,关于极简组调度-CGroup如何限制cpu的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!