Системные часы: прошедшее время и таймеры

В целях планирования ядро использует системные часы для определения того, как долго выполняется задача. Мы уже рассматривали системные часы в гл. 5 и использовали их в качестве примера для обсуждения прерываний. Здесь мы рассмотрим часы реального времени, их применение и реализацию, но для начала давайте определимся с основными понятиями часов.

Циклическая блокировка и семафоры

Когда два или более процесса требуют специального доступа к разделяемому ресурсу, они вынуждены устроить состязание за управление данным блоком кода. Базовой формой блокировки в Linux является циклическая блокировка.
Циклическая блокировка (spinlock) получила свое имя благодаря тому факту, что она выполняется циклически или крутится (spin), ожидая наступления блока. Благодаря такой работе циклической блокировки желательно не вставлять в циклически блокируемый код никаких повторных блокировок. Иначе может произойти ступор системы.

Неявное приоритетное прерывание обслуживания ядра

В Linux 2.6 реализовано новое неявное приоритетное прерывание обслуживания ядра. Когда задача ядра получает управление процессором, его обслуживание может приоритетно прерываться только другой задачей ядра, если она не содержит никаких блокировок. Каждая задача имеет поле preempt__count, помечающее задачу как приоритетно прерываемую. Счетчик увеличивается каждый раз, когда задача блокируется, и уменьшается, когда разблокируется. Функция schedule () отключает приоритетное прерывание обслуживания, когда определяет, какую задачу запустить следующей.

Неявное пользовательское приоритетное ферывание обслуживания

Жогда ядро завершает обработку задачи пользовательского пространства и готово пере-лвп> управление задаче пользовательского пространства, оно сперва проверяет, какой за-jne можно передать управление. Это не должна быть задача пользовательского пространства, которая передает управление ядру. Например, если задача А порождает сис-шный вызов, после завершения системного вызова ядро передает управление системой жиаче В.
Каждая задача в системе имеет флаг «необходимости перепланировки», который усевается, когда задачу следует перепланировать:
include/linux/sched.h

Явное приоритетное прерывание обслуживания в ядре

Проще всего понять явное приоритетное прерывание обслуживания в ядре. Оно происхо-JW в пространстве ядра, когда код ядра вызывает schedule (). Код ядра может schedule () двумя способами: либо с помощью прямого вызова schedule (), шао с помощью блокировки.
Когда происходит явное приоритетное прерывание обслуживания в ядре, как, например, в драйвере устройства, ожидающем в wait_queue, управление просто передается и для выполнения выбирается новая задача.

Приоритетное прерывание обслуживания

Приритетное прерывание обслуживания - это переключение одной задачи на другую. Ми упоминали как schedule () и scheduler_tick() решают, на какую задачу далее, но мы еще не описали, как Linux решает, когда выполнить. В ядре 2.6 представлено приоритетное прерывание обслуживания, швчающее, что как программы пользовательского пространства, так и программы ядра могут быть переключены в одно и то же время. Так как в Linux 2.6 при-iqpiiLiHoe прерывание обслуживания является стандартным, мы опишем, как работает прерывание обслуживания для ядра и пользовательских задач в Linux.

Вывод из активного состояния

Мы уже обсуждали, как задача попадает в планировщик после вызова fork и как задача перемещается из массивов активных приоритетов в истекшие в очереди выполнения на процессоре. Но когда же задача удаляется из очереди выполнения?
Задача может быть удалена из очереди выполнения двумя основными способами:
• задача прерывается ядром и изменяет состояние на незапущенное, после чего в задачу перестают поступать сигналы (см. строку 2240 в kernel /sched. с);
• на SMP-машинах задача может быть удалена из очереди выполнения и помещена в другую очередь выполнения (см. строку 3384 в kernel/sched. с).

Динамический расчет приоритета

В предыдущем подразделе мы касались специфики динамического расчета приоритетов задач. Приоритет задачи основан на ее поведении в прошлом, а также на определенном пользователем значении nice. Функцией, динамически определяющей новый приоритет задачи, является recalc_task_prio ():
383 384 385 386 387 388 389 390 391 392 393 394

- p->timestamp;
unsigned long long sleep_time = now
unsigned long sleep_time;
if ( sleep_time > NS_MAX_SLEEP_AVG)
sleep_time = NS_MAX_SLEEP_AVG; else
sleep_tirae = (unsigned long) sleep_time;
if (likely(sleep_time > 0)) { /*

Занятие процессора

Процессы могут занимать процессор простым вызовом функции schedule (). Она обычно используется в коде ядра и драйвером устройства, которое хочет заснуть или дождаться поступления сигнала1. Другие задачи тоже постоянно хотят использовать процессор, и системный таймер должен сообщать им, когда они смогут выполниться. Ядро Linux периодически захватывает процессор, при этом активные процессы останавливаются, и затем выполняет несколько зависящих от времени задач. Одна из этих задач, scheduler_tick (), позволяет ядру заставить процесс приостановиться.

Отслеживание прохождения switch_to() на РРС

Результат реализации switch_to() на РРС не обязательно идентичен вызову х86; он берет указатели на current и next задачи и возвращает указатель на предыдущую выполнявшуюся задачу:

include/asm-ppc/system.h
38 extern struct task_struct * switch_to(struct task_struct *,
39 struct task_struct *);
90 #define switch_to(prev, next, last)
((last) = switch_to((prev), (next)))
91
92 struct thread_struct;
93 extern struct task_struct *_switch(struct thread_struct *prev,
94 struct thread_struct *next),-

В строке 88 switch_to () получает параметр типа task_struct и на строке 93

RSS-материал