1. 调用时机
一般会在时钟中断时被调用,作用有:
- 剥夺时间片耗完的进程的CPU使用权,当然具体细节上没这么简单;
- 进行定时的CPU间负载均衡处理;
2. 大致流程
-
idle进程(swapper进程)的处理。
idle进程没有时间片可言,主要思路是如果当前逻辑CPU的可执行队里已有进程,应该尽快调度。
这里所指的调度还是延时调度,也就是设置TIF_NEED_RESCHED,
还会有些强制调度之类的处理,细节暂未研究;
-
实时进程的处理。
在这版本的源码中,没有调度类的概念,实时进程和普通进程是放在相同的可执行队列的。
实时进程的调度状况:
-
如果可执行的话,会一直停留在active队列,
优先级比所有普通进程高,
因此如果实时进程没有阻塞,同一逻辑CPU中的普通进程没机会运行;
-
SCHED_RR类型的实时进程在时间片消耗完后,会被放到active队列的末尾,
并且设置TIF_NEED_RESCHED位,
因此拥有不小于当前优先级的其他实时进程会被调度到;
-
SCHED_FIFO类型的实时进程,就真的除非被更高优先级的实时进程抢占,
时钟中断这边并不能让它们让出CPU;
-
-
交互式进程的处理
-
当时间片用完时,
设置TIF_NEED_RESCHED位;
但当其比expired队列所有进程优先级都高时,
并不放入expired队列中,而是重新放入active队列中;
这是对交互式进程的一种优待处理;
-
当时间片未用完时,
交互式进程的时间片会被细分为粒度,当运行完一个粒度后,
就需要重新调度,也就是设置TIF_NEED_RESCHED位;
这是对交互式进程的一种削弱,毕竟地位没有实时进程高;
-
-
普通非交互式进程的处理
-
当时间片用完时,
设置TIF_NEED_RESCHED位,并放入到expired队列中;
-
当时间片未用完时,不作任何操作;
-
-
定时负载均衡
在整个函数结束之前执行的一步,对各个CPU进行负载均衡处理。
具体有点复杂,会在load_balance()的文章进行大致介绍;
3. 源码注释
/*
* 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.
*/
/**
* 维持当前最新的time_slice计数器
* 每次时钟节拍到来时,scheduler_tick函数将被调用,以执行与调度相关的操作。
*/
void scheduler_tick(void)
{
int cpu = smp_processor_id();
runqueue_t *rq = this_rq();
task_t *p = current;
/**
* 把转换为纳秒的TSC的当前值存入本地运行队列的timestamp_last_tick中。这个时间戳由sched_clock获得。
*/
rq->timestamp_last_tick = sched_clock();
// idle进程的处理 //
if (p == rq->idle) {
/**
* wake_priorit