Linux系统核心调度器——周期性调度器详解

日期 内核版本 架构 作者 内容
2019-5-13 Linux-2.6.32

X86

Bystander Linux进程调度

1 绪论

在《Linux系统进程调度——调度架构详细分析》一文已经讲解Linux内核中实现了两个调度器:主调度器周期调度器,两者合称为通用调度器核心调度器,也详细解释调度框架、调度类、调度实体、调度策略等内容。

在《Linux系统进程核心调度器——主调度器schedule函数详解》一文中说明由以下两种方式分别触发激活主调度器周期调度器:

  1. 进程直接放弃CPU
  2. 通过周期性机制, 以固定的频率检测是否有必要调度

2 周期调度器

周期性调度器在scheduler_tick()中实现。 如果系统正在活动中, 内核会按照频率HZ自动调用该函数。 如果没有进程在等待调度,在计算机电力供应不足的情况下, 内核将关闭该调度器以减少能耗。

2.1 如何调用scheduler_tick()

在单处理系统上的全局时钟 中断处理程序或者多处理器系统上的本地时钟中断处理程序将调用update_process_times()来更新内核统计计数。update_process_times()主要完成以下任务:

  1. 首先调用account_process_tick(),它根据当前进程运行了多久时间和当前进程类别,选择调用account_user_time()、account_system_time(),还是account_idle_time()。
  2. 调用run_local_timers(),从而间接调用raise_softirq(),用来激活本地CPU上的TIMER_SOFTIRQ任务队列。
  3. 调用scheduler_tick(),该函数使当前进程时间片计数器减1,并检查计数器是否已经减到0。
void update_process_times(int user_tick)
{
	struct task_struct *p = current;
	int cpu = smp_processor_id();

	/* Note: this timer irq context must be accounted for as well. */
	account_process_tick(p, user_tick);
	run_local_timers();
	rcu_check_callbacks(cpu, user_tick);
	printk_tick();
	scheduler_tick();
	run_posix_cpu_timers(p);
}

2.2 scheduler_tick()实现

在scheduler_tick()中主要实现以下5个主要工作:

  1. 调用sched_clock_tick()以纳秒为单位将当前时间放入sched_clock_data中;
  2. 调用update_rq_clock()就绪队列时钟的更新, 实际上更新struct rq当前实例的时钟时间戳;
  3. 调用update_cpu_load()更新CPU上负载,为下一步执行调度类挑选进程做准备;
  4. 调用curr->sched_class->task_tick()内核先找到了就绪队列上当前运行的进程curr, 然后调用curr所属调度类sched_class的周期性调度方法task_tick。在Linux-2.6.32中有task_tick_rt(), task_tick_fair(), task_tick_idle()。
  5. 如果系统支持SMP则调用trigger_load_balance()定期进行堵负载均衡。

scheduler_tick()源码如下:

void scheduler_tick(void)
{
    /*获取当前CPU ID*/
	int cpu = smp_processor_id();
    /*根据CPU ID 获取当前CPU运行队列rq*/
	struct rq *rq = cpu_rq(cpu);
    /*获取当前CPU上运行队列正在运行进程*/
	struct task_struct *curr = rq->curr;
    /*以纳秒为单位将当前时间放入sched_clock_data中*/
	sched_clock_tick();

	spin_lock(&rq->lock);
    /*更新rq的时间.即使rq->clock变为当前时间*/  
	update_rq_clock(rq);
     /*更新负载信息,也就是更新rq->cpu_load[]*/  
	update_cpu_load(rq);
/*执行当前运行进程curr的调度类的task_tick函数*/
	curr->sched_class->task_tick(rq, curr, 0);
	spin_unlock(&rq->lock);
     /* 与perf计数事件相关 */
	perf_event_task_tick(curr, cpu);

#ifdef CONFIG_SMP
    /*判断当前CPU是否空闲*/
	rq->idle_at_tick = idle_cpu(cpu);
/* 如果需要定期进行负载平衡,则触发sched_softirq */
	trigger_load_balance(rq, cpu);
#endif
}

 

你可能感兴趣的:(linux内核)