Вытесняющая многозадачность
Пример 8.4. Планировщик Linux 2.5.
/* * 'schedule!)' – функция планировщика. Это очень простой и * приятный планировщик: он не совершенен, но несомненно работает * в большинстве случаев. * The goto is "interesting". * * ЗАМЕЧАНИЕ!! Задача 0 является 'пустой' задачей, которая вызывается * когда ни одна другая задача не может выполняться. Она не может быть * "убита" и не может "спать". Информация о состоянии в task[0] никогда * не используется. */ asmlinkage void schedule(void) { struct schedule_data * sched_data; struct task_struct *prev, *next, *p; struct list_head *tmp; int this__cpu, c; if (!current › active_mm) BUG(); need_resched_back: prev = current; this_cpu = prev › processor; if (in_interrupt()) goto scheduling_in_interrupt; release_kernel_lock(prev, this_cpu); /* Выполнить административную работу здесь, пока мы не держим * ни одной блокировки. */ if (softirq_active(this_cpu) & softirq_mask(this_cpu)) goto handle_softirq; handle_softirq_back: /* * 'shed data" защищена неявно, тем фактом, что мы можем исполнять * только один процесс на одном ЦПУ. */ sched__data = & aligned_data[this_cpu].schedule_data; spin_lock_irq (&runqueue__lock); /* Переместить исчерпанный процесс RR в конец [очереди] */ if (prev › policy == SCHED_RR) goto move_rr_last; pove_rr_back: switch (prev › state) { case TASKJLNTERRUPTIBLE: if (signal_pending (prev)) { prev › state = TASK_RUNNING; break; } default: dei_f rom_runqueue (prev); case TASK_RUNNING:; } prev › need_resched = 0; /* * это собственно планировщик: */ repeat_schedule: /* * Выбрать процесс по умолчанию… */ next = idle_task(this_cpu); с = -1000; if (prev › state == TASK_RUNNING) goto still_running; still_running_back: list_for_each (tmp, srunqueue_head) { p = list_entry (tmp, struct task_struct, run_list); if (can_schedule (p, this_cpu)) { int weight = goodness (p, this_cpu, prev › active_mm); if (weight > c) с = weight, next = p; /* Следует ли перевычислить счетчики? */ if (!c) goto recalculate; /* * с этого момента ничто ке может помешать нам * переключиться на следующую задачу, отметить этот * факт в sched_data. */ sched_data › curr = next; tifdef CONFIG_SMP riext › has_cpu = I; next › processor = this_cpu; lendif spin_unlock__irq (&runqueue_lock); if (prev == next) goto same_process; ttifdef CONFIG_SMP /* * Поддерживать значение 'last schedule' для каждого процесса * (его необходимо пересчитать лаже если мы планируем тот же * процесс). Сейчас это значение используется только в SMP, и оно * приблизительно, поэтому мы не обязаны поддерживать его, * пока захвачена блокировка runqueue. */ sched_data › last_schedule = get_cycles(); /* * Мы снимаем блокировку планировщика рано (это глобальная * блокировка), поэтому мы должны защитить предыдущий процесс * от повторного планирования во время switch_to(). */ ttendif /* CONFIG_SMP */ kstat.context_swtch++; /* * Переключение контекста воздействует на три процесса: prev .. = › (last › next) * Это 'prev', 'далеко предшествующий' размещенному в стеке 'next1, * но функция switch_to() устанавливает prev на (только что * работавший) процесс 'last'. * Описание несколько запутанно, но не лишено глубокого смысла, */