Linux进程管理之scheduler_tick函数

392 阅读2分钟

写在前面

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平台。可以看到函数调用栈。

image.png

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);
}