このページを編集する際は、編集に関する方針に従ってください。

概要

実装

/*

* schedule() is the main scheduler function.
*/

asmlinkage void __sched schedule(void) {

	long *switch_count;
	task_t *prev, *next;
	runqueue_t *rq;
	prio_array_t *array;
	struct list_head *queue;
	unsigned long long now;
	unsigned long run_time;
	int cpu, idx;
	/*
	 * Test if we are atomic.  Since do_exit() needs to call into
	 * schedule() atomically, we ignore that path for now.
	 * Otherwise, whine if we are scheduling when we should not be.
	 */
	if (likely(!(current->exit_state & (EXIT_DEAD | EXIT_ZOMBIE)))) {
		if (unlikely(in_atomic())) {
			printk(KERN_ERR "scheduling while atomic: "
				"%s/0x%08x/%d\n",
				current->comm, preempt_count(), current->pid);
			dump_stack();
		}
	}
	profile_hit(SCHED_PROFILING, __builtin_return_address(0));
  • __builtin_return_address(0)はschedule()関数の復帰アドレスが返ってくる
  • 詳細はprofile_hit()/linux2.6を参照
  • __builtin_return_address(x)は,実行中の関数の復帰アドレス,または,実行中の関数を呼び出すまでに,途中で呼び出されてきた関数の中の一つの復帰アドレスを返す
    • 引数xは,呼び出しスタック中においてさかのぼるべきフレームの数
  • SCHED_PROFILINGは2で定義されている

need_resched:

	preempt_disable();
	prev = current;
	release_kernel_lock(prev);

need_resched_nonpreemptible:

	rq = this_rq();
	/*
	 * The idle thread is not allowed to schedule!
	 * Remove this check after it has been exercised a bit.
	 */
	if (unlikely(current == rq->idle) && current->state != TASK_RUNNING) {
		printk(KERN_ERR "bad: scheduling from the idle thread!\n");
		dump_stack();
	}
	schedstat_inc(rq, sched_cnt);
	now = sched_clock();
	if (likely(now - prev->timestamp < NS_MAX_SLEEP_AVG))
		run_time = now - prev->timestamp;
	else
		run_time = NS_MAX_SLEEP_AVG;
	/*
	 * Tasks with interactive credits get charged less run_time
	 * at high sleep_avg to delay them losing their interactive
	 * status
	 */
	if (HIGH_CREDIT(prev))
		run_time /= (CURRENT_BONUS(prev) ? : 1);
  • HIGH_CREDIT(p)は( (p)->interactive_credit > CREDIT_LIMIT)と定義されている
  • 詳細はCURRENT_BONUS()/linux2.6を参照
	spin_lock_irq(&rq->lock);
	if (unlikely(current->flags & PF_DEAD))
		current->state = EXIT_DEAD;
	/*
	 * if entering off of a kernel preemption go straight
	 * to picking the next task.
	 */
	switch_count = &prev->nivcsw;
	if (prev->state && !(preempt_count() & PREEMPT_ACTIVE)) {
		switch_count = &prev->nvcsw;
		if (unlikely((prev->state & TASK_INTERRUPTIBLE) &&
				unlikely(signal_pending(prev))))
			prev->state = TASK_RUNNING;
		else {
			if (prev->state == TASK_UNINTERRUPTIBLE)
				rq->nr_uninterruptible++;
			deactivate_task(prev, rq);
		}
	}
	cpu = smp_processor_id();
	if (unlikely(!rq->nr_running)) {
  • 現在使用中のCPUのランキューに実行可能タスクが無い場合:

go_idle:

		idle_balance(cpu, rq);
		if (!rq->nr_running) {
			next = rq->idle;
			rq->expired_timestamp = 0;
			wake_sleeping_dependent(cpu, rq);
  • バランス処理行った結果、ドメイン内CPUのランキューのカレントタスクがアイドル状態で、ランキューの中に実行可能なタスクが存在する場合、プロセッサ間通信を用いて再スケジューリングを要請する
			/*
			 * wake_sleeping_dependent() might have released
			 * the runqueue, so break out if we got new
			 * tasks meanwhile:
			 */
			if (!rq->nr_running)
				goto switch_tasks;
		}
	} else {
  • 現在使用中のCPUのランキューに実行可能タスクが有る場合:
		if (dependent_sleeper(cpu, rq)) {
  • 実行中のタスクがアイドル状態でありランキューに実行可能なタスクがある、すべてのドメイン内CPUに対してプロセッサ間通信を行い再スケジュールを要請する。現在使用中のCPUの最も優先度が高いタスクがリアルタイムタスクではなく、すべてのドメイン内CPUのカレントタスクがカーネルスレッドを実行していないときは1を返す。
			next = rq->idle;
			goto switch_tasks;
  • 次に実行すべきタスクにアイドルプロセスを設定して、タスクを切り替える
		}
		/*
		 * dependent_sleeper() releases and reacquires the runqueue
		 * lock, hence go into the idle loop if the rq went
		 * empty meanwhile:
		 */
		if (unlikely(!rq->nr_running))
			goto go_idle;
	}
	array = rq->active;
	if (unlikely(!array->nr_active)) {
  • アクティブランキューの優先度スロットにタスクがない場合は、activeキューとexpiredキューを交換する
		/*
		 * Switch the active and expired arrays.
		 */
		schedstat_inc(rq, sched_switch);
  • ランキューrqの指定されたフィールドsched_switchをインクリメントする
		rq->active = rq->expired;
		rq->expired = array;
		array = rq->active;
		rq->expired_timestamp = 0;
		rq->best_expired_prio = MAX_PRIO;
	} else
		schedstat_inc(rq, sched_noswitch);
  • アクティブランキューの優先度スロットにタスクがある場合はランキューrqの指定されたフィールドsched_noswitchをインクリメントする
	idx = sched_find_first_bit(array->bitmap);
	queue = array->queue + idx;
	next = list_entry(queue->next, task_t, run_list);
  • list_headのポインタから、それが組み込まれているオブジェクトのポインタを割り出す
	if (!rt_task(next) && next->activated > 0) {
		unsigned long long delta = now - next->timestamp;
		if (next->activated == 1)
			delta = delta * (ON_RUNQUEUE_WEIGHT * 128 / 100) / 128;
		array = next->array;
		dequeue_task(next, array);
		recalc_task_prio(next, next->timestamp + delta);
		enqueue_task(next, array);
	}
	next->activated = 0;

switch_tasks:

	if (next == rq->idle)
		schedstat_inc(rq, sched_goidle);
	prefetch(next);
	clear_tsk_need_resched(prev);
	rcu_qsctr_inc(task_cpu(prev));
	prev->sleep_avg -= run_time;
	if ((long)prev->sleep_avg <= 0) {
		prev->sleep_avg = 0;
		if (!(HIGH_CREDIT(prev) || LOW_CREDIT(prev)))
			prev->interactive_credit--;
	}
	prev->timestamp = prev->last_ran = now;
	sched_info_switch(prev, next);
  • タスク切り替えに伴い、スケジューリングに関する統計情報を更新する
	if (likely(prev != next)) {
		next->timestamp = now;
		rq->nr_switches++;
		rq->curr = next;
		++*switch_count;
		prepare_arch_switch(rq, next);
  • prepare_arch_switchはx86系CPUでは何もしない
		prev = context_switch(rq, prev, next);
  • prevからnextへコンテキストを切り替える
  • この関数の実行が終了した時点でnextがアクティブタスクになる
		barrier();
  • GCCコンパイラでは何の操作もしない
		finish_task_switch(prev);
	} else
		spin_unlock_irq(&rq->lock);
  • スピンロックを開放して、外部割込みを有効する。プリエンプティブを有効にして可能であればプリエンプティブを実行する
	prev = current;
	if (unlikely(reacquire_kernel_lock(prev) < 0))
		goto need_resched_nonpreemptible;
  • 引数で指定されたタスクのロック深度が0以上である場合、グローバルロックを取得を試みる。
  • 返り値が0以下である場合(再スケジューリング要求がある場合)はneed_resched_nonpreemptibleへジャンプ
	preempt_enable_no_resched();
  • プリエンプション機能を有効にする。ただし例え、preempt_countが0であっても自らプリエンプション(実行権の移譲を行うこと)することはない。
	if (unlikely(test_thread_flag(TIF_NEED_RESCHED)))
		goto need_resched;
  • 現在実行中のタスクのthread_info構造体flagsメンバのTIF_NEED_RESCHED番目のビットの値を返す

}

呼出元


履歴

  • 作者:ひら
  • 日付:2005/1/5
  • 対象:2.6.10
    更新日更新者更新内容

コメント



トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2009-11-24 (火) 07:18:03 (3129d)