/* * The 'current' period is already promised to the current tasks, * however the extra weight of the new task will slow them down a * little, place the new task so that it fits in the slot that * stays open at the end. */ if (initial && sched_feat(START_DEBIT)) vruntime += sched_vslice(cfs_rq, se); 稍微加一些,呵呵
if (!initial) { 如果是睡了,唤醒的,应该有些补偿的 具体怎么补,多了怎么办,少了怎么办? /* sleeps upto a single latency don't count. */ if (sched_feat(NEW_FAIR_SLEEPERS)) { unsigned long thresh = sysctl_sched_latency;
/* * convert the sleeper threshold into virtual time */ if (sched_feat(NORMALIZED_SLEEPER)) thresh = calc_delta_fair(thresh, se);
vruntime -= thresh; }
/* ensure we never gain time by being placed backwards. */ vruntime = max_vruntime(se->vruntime, vruntime); }
3 关于tick中断 为了保证调度,在每个tick都会对进程时间信息等进行更新。首先找一个从hrtimer上层到进程调度之间的点,暂时往进程调度的方向说,后面谈到hrtimer时,再往hrtimer追根溯源吧。 这个点就是,update_process_times,它是在timer interrupt 中被调 的,它会调一个跟process直接相关的函数,scheduler_tick(). /* * This function gets called by the timer code, with HZ frequency. * We call it with interrupts disabled. * * It also gets called by the fork code, when changing the parent's * timeslices. */ void scheduler_tick(void) { int cpu = smp_processor_id(); ... update_cpu_load(rq); curr->sched_class->task_tick(rq, curr, 0); task_tick即公平调度类里的 spin_unlock(&rq->lock);
static void entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued) { /* * Update run-time statistics of the 'current'. */ update_curr(cfs_rq); 更新当前进程current的信息,这个昨天已经看过了
#ifdef CONFIG_SCHED_HRTICK /* * queued ticks are scheduled to match the slice, so don't bother * validating it and just reschedule. */ if (queued) { resched_task(rq_of(cfs_rq)->curr); return; } /* * don't let the period tick interfere with the hrtick preemption */ if (!sched_feat(DOUBLE_TICK) && hrtimer_active(&rq_of(cfs_rq)->hrtick_timer)) return; #endif
if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT)) check_preempt_tick(cfs_rq, curr); 这个要与check_preempt_curr对比着看,我当初就在这个地方晕了。。。注释是这样的:“ Preempt the current task with a newly woken task if needed:”
第二个函数折磨了我好久~ 既然是完全公平调度,每个进程的运行时间应该完全公平呀? 实际上ideal_runtime 是与进程的优先级有关系的。 首先看ideal_runtime是怎么计算出来的。 3) /* * We calculate the wall-time slice from the period by taking a part * proportional to the weight. * * s = p*P[w/rw]这是通过一个比例算出的 */ static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se) { unsigned long nr_running = cfs_rq->nr_running;运行的进程数目
4)看看cale_delata_weight第一个参数是怎么得到的。 /* * The idea is to set a period in which each task runs once. 设置一个period,让每个进程都跑一遍。 * * When there are too many tasks (sysctl_sched_nr_latency) we have to stretch * this period because otherwise the slices get too small. 当进程数目大于默认值(5)时,要拉伸一下这个period * * p = (nr <= nl) ? l : l*nr/nl */ static u64 __sched_period(unsigned long nr_running) { u64 period = sysctl_sched_latency; unsigned long nr_latency = sched_nr_latency;
if (unlikely(nr_running > nr_latency)) { period = sysctl_sched_min_granularity; 最小粒度 4ms period *= nr_running; 4ms * 进程数目 }
return period; }
这个比较好理解,小于5个进程时,这个period周期是20ms,大于5个running number 时,就让4*nr;后面就是线性的,
这是对一个即将被调度出去的进程的处理。 2) 它会掉pub_prev_entity 这个例程看得有点儿迷糊。 如果进程还在runqueue上,就把它重新放回红黑树,只是位置变了,并把当前的curr指针清空。 static void put_prev_entity(struct cfs_rq *cfs_rq, struct sched_entity *prev) { /* * If still on the runqueue then deactivate_task() * was not called and update_curr() has to be done: */ if (prev->on_rq) 这个条件还得研究研究,什么情况下用与不用 update_curr(cfs_rq);
check_spread(cfs_rq, prev); if (prev->on_rq) { update_stats_wait_start(cfs_rq, prev); /* Put 'current' back into the tree. */ __enqueue_entity(cfs_rq, prev); 怎么又放回去呢? } cfs_rq->curr = NULL; }
这儿有个小猜想,希望得到验证: 1) a task call schedule(),it's se->on_rq = 1,so it need denqueue .call deactivate_task()
2) a task picked by schedule() need to get the cpu to run,it's se->on_rq = 0,so it need enqueue. 3) a task's exec time is over,need change to rb-tree's right it's se->on_rq = 1,just change the tree