linuxプロセススケジューリング-周期スケジューラ

4708 ワード

周期的なスケジューラはscheduler_tickで実現します.システムがアクティブな場合、カーネルは周波数HZに従って自動的に呼び出されます.スケジューリング待ちのプロセスがない場合は、コンピュータの電力供給が不足している場合に、スケジューラを閉じて電力消費を減らすこともできます.
3469 /*
3470  * This function gets called by the timer code, with HZ frequency.
3471  * We call it with interrupts disabled.
3472  *
3473  * It also gets called by the fork code, when changing the parent's
3474  * timeslices.
3475  */
3476 void scheduler_tick(void)
3477 {
3478     int cpu = smp_processor_id();
3479     struct rq *rq = cpu_rq(cpu);
3480     struct task_struct *curr = rq->curr;
3481     u64 next_tick = rq->tick_timestamp + TICK_NSEC;
3482 
3483     spin_lock(&rq->lock);
3484     __update_rq_clock(rq);
3485     /*
3486      * Let rq->clock advance by at least TICK_NSEC:
3487      */
3488     if (unlikely(rq->clock < next_tick))
3489         rq->clock = next_tick;
3490     rq->tick_timestamp = rq->clock;
3491     update_cpu_load(rq);
3492     if (curr != rq->idle) /* FIXME: needed? */
3493         curr->sched_class->task_tick(rq, curr);
3494     spin_unlock(&rq->lock);
3495 
3496 #ifdef CONFIG_SMP
3497     rq->idle_at_tick = idle_cpu(cpu);
3498     trigger_load_balance(rq, cpu);
3499 #endif
3500 }

3484 __update_rq_clockは準備キュークロックの更新を処理します.通常、この関数はrq->clockを現在のクロックtickに更新します.
3488~3499通常、新しいクロックは元のクロックに1つのブータを追加することを期待していますが、_update_rq_clockで得られたクロック値のステップが1つのステップより小さい場合、1つのステップに設定されます.
3490現在のクロックもtick_に保存timestamp、この値はclockと少し冗長で、完全に処理の便利さのために増加したメンバー変数です.
3491 update_cpu_loadはrqデータ構造のcpu_を更新するために使用されるload配列フィールド、このcpu_loadは負荷移行時に使用されます.cpu loadについてはlinuxプロセススケジューリング-負荷等化を参照
3493 task_tickの実装方法は、最下位のスケジューラクラスに依存する.完全公平スケジューラでは、このメソッドでプロセスが長すぎるかどうかを検出し、長すぎる遅延を回避します.
__update_rq_clock
382 /*
 383  * Update the per-runqueue clock, as finegrained as the platform can give
 384  * us, but without assuming monotonicity, etc.:
 385  */
 386 static void __update_rq_clock(struct rq *rq)
 387 {
 388     u64 prev_raw = rq->prev_clock_raw;
 389     u64 now = sched_clock();
 390     s64 delta = now - prev_raw;
 391     u64 clock = rq->clock;
 392 
 393 #ifdef CONFIG_SCHED_DEBUG
 394     WARN_ON_ONCE(cpu_of(rq) != smp_processor_id());
 395 #endif
 396     /*
 397      * Protect against sched_clock() occasionally going backwards:
 398      */
 399     if (unlikely(delta < 0)) {
 400         clock++;
 401         rq->clock_warps++;
 402     } else {
 403         /*
 404          * Catch too large forward jumps too:
 405          */
 406         if (unlikely(clock + delta > rq->tick_timestamp + TICK_NSEC)) {
 407             if (clock < rq->tick_timestamp + TICK_NSEC)
 408                 clock = rq->tick_timestamp + TICK_NSEC;
 409             else
 410                 clock++;
 411             rq->clock_overflows++;
 412         } else {
 413             if (unlikely(delta > rq->clock_max_delta))
 414                 rq->clock_max_delta = delta;
 415             clock += delta;
 416         }
 417     }
 418 
 419     rq->prev_clock_raw = now;
 420     rq->clock = clock;
 421 }

この関数は、per-CPU実行キューのrq->prev_を更新します.clock_rawとrq->clock、ここのクロックはすべてリアルタイムクロックです.
388 prev_raw前回呼び出し_update_req_clockの時計;Nowは現在のシステム時間です
399~401時計がたまに間違えるのを防ぐには、簡単にclockに1を加え、統計量clock_を増やします.warps
403~411クロックでもフロントホップが発生する可能性があるので、この場合はclockの値を修正し、統計量clock_を増やすoverflows
task_tick_fair
/*
 * scheduler tick hitting a task of our scheduling class:
 */
static void task_tick_fair(struct rq *rq, struct task_struct *curr)
{
    struct cfs_rq *cfs_rq;
    struct sched_entity *se = &curr->se;

    for_each_sched_entity(se) {
        cfs_rq = cfs_rq_of(se);
        entity_tick(cfs_rq, se);
    }
}

この関数は非常に簡単で、実際の仕事はentity_です.tick完了
 645 static void entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
 646 {
 647     /*
 648      * Update run-time statistics of the 'current'.
 649      */
 650     update_curr(cfs_rq);
 651 
 652     if (cfs_rq->nr_running > 1 || !sched_feat(WAKEUP_PREEMPT))
 653         check_preempt_tick(cfs_rq, curr);
 654 }

650 update_Curr更新現在
652実行可能プロセスの数が1より大きい場合にのみ、プロセス優先権の問題を考慮する必要があります.そうしないと、1つのプロセスは優先権を必要としません.653 check_preempt_tickこの関数は、プロセスの実行時間が遅延サイクルを超えているかどうかを判断し、遅延サイクルを超えている場合は、コアスケジューラが適切な場所で再スケジュールされるように、再スケジュール(TIF_NEED_RESCHEDフラグを設定)します.たとえば、システム呼び出しが返されると、TIF_がチェックされます.NEED_RESCHEDフラグは、セットされている場合、カーネルがプライマリスケジューラscheduleを呼び出す.また、デバイスドライバが長時間繰り返しタスクを実行する場合、繰り返しサイクル毎にドライバはTIF_をチェックするNEED_RESCHEDの値は、必要に応じてスケジューラscheduleを呼び出してCPUをアクティブに破棄します.