linuxプロセススケジューリング-周期スケジューラ
4708 ワード
周期的なスケジューラはscheduler_tickで実現します.システムがアクティブな場合、カーネルは周波数HZに従って自動的に呼び出されます.スケジューリング待ちのプロセスがない場合は、コンピュータの電力供給が不足している場合に、スケジューラを閉じて電力消費を減らすこともできます.
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
この関数は、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
この関数は非常に簡単で、実際の仕事はentity_です.tick完了
650 update_Curr更新現在
652実行可能プロセスの数が1より大きい場合にのみ、プロセス優先権の問題を考慮する必要があります.そうしないと、1つのプロセスは優先権を必要としません.653 check_preempt_tickこの関数は、プロセスの実行時間が遅延サイクルを超えているかどうかを判断し、遅延サイクルを超えている場合は、コアスケジューラが適切な場所で再スケジュールされるように、再スケジュール(TIF_NEED_RESCHEDフラグを設定)します.たとえば、システム呼び出しが返されると、TIF_がチェックされます.NEED_RESCHEDフラグは、セットされている場合、カーネルがプライマリスケジューラscheduleを呼び出す.また、デバイスドライバが長時間繰り返しタスクを実行する場合、繰り返しサイクル毎にドライバはTIF_をチェックするNEED_RESCHEDの値は、必要に応じてスケジューラscheduleを呼び出してCPUをアクティブに破棄します.
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をアクティブに破棄します.