Scheduler -10- (Idle Scheduer)

 

Idle-task Scheduler

어떠한 스케줄러보다 우선 순위가 낮아 다른 스케줄러에서 태스크가 동작해야 하는 상황이되면 언제나 preemption 된다. idle 태스크는 core 마다 1개씩 지정되어 있어 다른 cpu로의 migration이 필요 없다.

  • 각 core의 부트업 처리가 완료되면 cpu_startup_entry() 함수를 호출하여 idle 태스크로의 역할을 수행한다.
    • 부트업 프로세스가 사용하는 메모리 환경을 그대로 사용한다. (init_mm 공유)
    • 참고: cpu_startup_entry() | 문c

 

select_task_rq_idle()

kernel/sched/idle_task.c

select_task_rq_idle(struct task_struct *p, int cpu, int sd_flag, int flags)
{
        return task_cpu(p); /* IDLE tasks as never migrated */
}

migration을 허용하지 않는다.

 

check_preempt_curr_idle()

kernel/sched/idle_task.c

/*
 * Idle tasks are unconditionally rescheduled:
 */
static void check_preempt_curr_idle(struct rq *rq, struct task_struct *p, int flags)
{
        resched_curr(rq);
}

항상 preemption 한다.

 

dequeue_task_idle()

kernel/sched/idle_task.c

/*
 * It is not legal to sleep in the idle task - print a warning
 * message if some code attempts to do it:
 */
static void
dequeue_task_idle(struct rq *rq, struct task_struct *p, int flags)
{
        raw_spin_unlock_irq(&rq->lock);
        printk(KERN_ERR "bad: scheduling from the idle thread!\n");
        dump_stack();
        raw_spin_lock_irq(&rq->lock);
}

idle 태스크는 엔큐 및 디큐 개념이 없다. 따라서 호출되는 경우 경고 메시지를 출력한다.

 

task_tick_idle()

kernel/sched/idle_task.c

static void task_tick_idle(struct rq *rq, struct task_struct *curr, int queued)
{
}

task 틱마다 아무런 동작도 하지 않는다.

 

update_curr_idle()

kernel/sched/idle_task.c

static void update_curr_idle(struct rq *rq)
{
}

아무런 동작도 하지 않는다.

 

switched_to_idle()

kernel/sched/idle_task.c

static void switched_to_idle(struct rq *rq, struct task_struct *p)
{
        BUG(); 
}

설계상 이 idle 태스크로의 스위칭은 불가능하다.

 

prio_changed_idle()

kernel/sched/idle_task.c

static void
prio_changed_idle(struct rq *rq, struct task_struct *p, int oldprio)
{
        BUG(); 
}

idle 태스크 자체가 가장 낮은 순위의 태스크로 별도로 우선 순위를 변경할 수 없다.

 

get_rr_interval_idle()

kernel/sched/idle_task.c

static unsigned int
get_rr_interval_idle(struct rq *rq, struct task_struct *task)
{
        return 0;
}

idle 태스크는 preemption되지 않는 경우 계속 수행되어야 하므로 idle 스케줄러는 인터벌을 사용하지 않는다.

 

 

다음 idle 태스크 선택

pick_next_task_idle()

kernel/sched/idle_task.c

static struct task_struct *
pick_next_task_idle(struct rq *rq, struct task_struct *prev)
{
        put_prev_task(rq, prev);

        schedstat_inc(rq, sched_goidle);
        return rq->idle;
}

기존 태스크에 대한 런타임 계산 등을 끝내고 idle 태스크를 반환한다.

 

put_prev_task_idle()

kernel/sched/idle_task.c

static void put_prev_task_idle(struct rq *rq, struct task_struct *prev)
{
        idle_exit_fair(rq);
        rq_last_tick_reset(rq);
}

idle 태스크의 실행 시각이 끝난 경우 이에 대한 처리를 수행한다.

  • 코드 라인 3에서 런큐의 러너블 로드 평균을 갱신한다.
  • 코드 라인 4에서 nohz full 을 허용한 시스템인 경우 last_sched_tick에 현재 시각(jiffies)를 기록한다.

 

Idle-task 스케줄러 OPS

kernel/sched/idle_task.c

/*
 * Simple, special scheduling class for the per-CPU idle tasks:
 */
const struct sched_class idle_sched_class = {
        /* .next is NULL */
        /* no enqueue/yield_task for idle tasks */

        /* dequeue is not valid, we print a debug message there: */
        .dequeue_task           = dequeue_task_idle,

        .check_preempt_curr     = check_preempt_curr_idle,

        .pick_next_task         = pick_next_task_idle,
        .put_prev_task          = put_prev_task_idle,

#ifdef CONFIG_SMP
        .select_task_rq         = select_task_rq_idle,
#endif

        .set_curr_task          = set_curr_task_idle,
        .task_tick              = task_tick_idle,

        .get_rr_interval        = get_rr_interval_idle,

        .prio_changed           = prio_changed_idle,
        .switched_to            = switched_to_idle,
        .update_curr            = update_curr_idle,
};

 

참고

답글 남기기

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.