Иллюстрированный самоучитель по теории операционных систем

Вытесняющая многозадачность

Пример 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'.
* Описание несколько запутанно, но не лишено глубокого смысла,
*/
Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.