写在前面
schduler_tick函数可谓是调度中的关键函数之一,其后缀的tick表明其是一个周期性的函数,调用频率其实是由CONFIG_HZ宏控制,ARM64平台通常为250HZ,即4ms执行一次。
scheduler_tick函数原型
/*
* This function gets called by the timer code, with HZ frequency.
* We call it with interrupts disabled.
*/
void scheduler_tick(void)
{
int cpu = smp_processor_id(); // 获取当前CPU。
struct rq *rq = cpu_rq(cpu); // 获取当前CPU上的runqueue
struct task_struct *curr = rq->curr; // 获取当前runqueue上正在运行的任务。
struct rq_flags rf;
unsigned long thermal_pressure;
arch_scale_freq_tick();
sched_clock_tick();
rq_lock(rq, &rf);
update_rq_clock(rq); // 更新当前runqueue中的clock时间
thermal_pressure = arch_scale_thermal_pressure(cpu_of(rq));
update_thermal_load_avg(rq_clock_thermal(rq), rq, thermal_pressure);
curr->sched_class->task_tick(rq, curr, 0); // 执行不同调度类中的task_tick回调函数
calc_global_load_tick(rq); // 更新该runqueue相关的CPU load负载,会根据cfs_rq中计算的runnable_load_avg来更新CPU负载
psi_task_tick(rq);
rq_unlock(rq, &rf);
perf_event_task_tick();
#ifdef CONFIG_SMP
rq->idle_balance = idle_cpu(cpu);
trigger_load_balance(rq);
#endif
}
使用qemu模拟ARM64平台。可以看到函数调用栈。
task_tick_fair函数
/*
* scheduler tick hitting a task of our scheduling class.
*
* NOTE: This function can be called remotely by the tick offload that
* goes along full dynticks. Therefore no local assumption can be made
* and everything must be accessed through the @rq and @curr passed in
* parameters.
*/
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{
struct cfs_rq *cfs_rq;
struct sched_entity *se = &curr->se; // 获取当前task中包含的调度实体se。
/*
在不支持组调度的条件下只循环一次,在组调度的条件下调度实体存在层次关系,更新子调度实体时必须更新父调度实体
*/
for_each_sched_entity(se) {
/* 获取当前运行进程所在的cfs就绪队列 */
cfs_rq = cfs_rq_of(se);
/* 完成周期性调度 */
entity_tick(cfs_rq, se, queued);
}
if (static_branch_unlikely(&sched_numa_balancing))
task_tick_numa(rq, curr);
update_misfit_status(curr, rq);
update_overutilized_status(task_rq(curr));
}
entity_tick函数
static void
entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
{
/*
* Update run-time statistics of the 'current'.
*/
/* 更新vruntime */
update_curr(cfs_rq);
/*
* Ensure that runnable average is periodically updated.
*/
/* 更新load */
update_load_avg(cfs_rq, curr, UPDATE_TG);
update_cfs_group(curr);
#ifdef CONFIG_SCHED_HRTICK
/*
* queued ticks are scheduled to match the slice, so don't bother
* validating it and just reschedule.
*/
if (queued) {
resched_curr(rq_of(cfs_rq));
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
/*
如果进程的数目不少于两个,则由check_preempt_tick作出决策。
*/
if (cfs_rq->nr_running > 1)
check_preempt_tick(cfs_rq, curr);
}