Чтение онлайн

на главную

Жанры

Разработка ядра Linux
Шрифт:

void run_local_timers(void) {

 raise_softirq(TIMER_SOFTIRQ);

}

Отложенное прерывание с номером

TIMER_SOFTIRQ
обрабатывается функцией
run_timer_softirq
. Эта функция выполняет на локальном процессоре обработчики всех таймеров, для которых истек период времени ожидания (если такие есть).

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

одной группы в другую, по мере того как приближается момент времени срабатывания. Такое разбиение на группы гарантирует, что в большинстве случаев при выполнении обработчика отложенного прерывания, ответственного за выполнение обработчиков таймеров, ядро будет выполнять мало работы для поиска таймеров, у которых истек период ожидания. Следовательно, код управления таймерами очень эффективен.

Задержка выполнения

Часто коду ядра (особенно драйверам) необходимо задерживать выполнение действий на некоторый период времени без использования таймеров или механизма нижних половин. Это обычно необходимо для того, чтобы дать аппаратному обеспечению время на завершение выполнения задачи. Такой интервал времени обычно достаточно короткий. Например, в спецификации сетевой интерфейсной платы может быть указано время изменения режима работы Ethernet-контроллера, равное 2 микросекундам, т.е. после установки желаемой скорости передачи драйвер должен ожидать хотя бы в течение двух микросекунд перед тем, как продолжить работу.

Ядро предоставляет несколько решений этой задачи, в зависимости от семантики задержки. Эти решения имеют разные свойства. Некоторые решения во время задержки загружают процессор, не давая возможности выполнять другую, более полезную работу. Другие решения не загружают процессор, но не дают гарантии того, что код возобновит выполнение точно в необходимый момент времени [60] .

Задержка с помощью цикла

Наиболее простое для реализации (хотя обычно не оптимальное) решение — это использование задержки с помощью цикла или ожидания в состоянии занятости (busy loop, busy waiting). Эта техника работает, только если интервал времени задержки является кратным периоду системного таймера или когда точность не очень важна.

60

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

Идея проста — выполнить постоянный цикл, пока не будет получено необходимое количество импульсов системного таймера, как в следующем примере.

unsigned long delay = jiffies + 10; /* десять импульсов таймера */

while (time_before(jiffies, delay))

 ;

Цикл будет выполняться, пока значение переменной

jiffies
не станет больше, чем значение переменной
delay
, что может произойти только после того, как будут получены 10 импульсов системного таймера. Для аппаратной платформы x86 со значением параметра
HZ
, равным 1000, этот интервал равен 10 миллисекунд.

Аналогично можно поступить следующим образом.

unsigned long delay = jiffies + 2*HZ; /* две секунды */

while (time_before(jiffies, delay))

 ;

В этом случае цикл будет выполняться, пока не поступит

2*HZ
импульсов системного таймера, что всегда равно 2 секундам, независимо от частоты системного таймера.

Такой подход не очень хорош для всей системы. Пока код ожидает, процессор загружен выполнением бесполезного цикла и никакой полезной работы при этом не выполняется! На самом деле к такому "глупому" подходу нужно прибегать по возможности реже, и он показан здесь, потому что является понятным и простым способом

осуществить задержку. Его можно встретить в чьем-нибудь не очень хорошем коде.

Лучшим решением является перепланирование для того, чтобы процессор мог выполнить полезную работу, пока ваш код ожидает:

unsigned long delay = jiffies + 5*HZ;

while (time_before(jiffies, delay))

 cond_resched;

Вызов функции

cond_resched
планирует выполнение другого процесса, но только в случае, если установлен флаг
need_resched
. Другими словами, данное решение позволяет активизировать планировщик, но только в случае, когда есть более важное задание, которое нужно выполнить. Следует обратить внимание, что. поскольку используется планировщик, такое решение нельзя применять в контексте прерывания, а только в контексте процесса. Задержки лучше использовать только в контексте процесса, поскольку обработчики прерываний должны выполняться по возможности быстро (а цикл задержки не дает такой возможности!). Более того, любые задержки выполнения, по возможности, не должны использоваться при захваченных блокировках и при запрещенных прерываниях.

Поклонники языка С могут поинтересоваться, какие есть гарантии, что указанные циклы будут действительно выполняться? Обычно компилятор С может выполнить чтение указанной переменной всего один раз. В обычной ситуации нет никакой гарантии, что переменная

jiffies
будет считываться на каждой итерации цикла. Нам же необходимо, чтобы значение переменной
jiffies
считывалось на каждой итерации цикла, так как это значение увеличивается в другом месте, а именно в прерывании таймера. Именно поэтому данная переменная определена в файле
<linux/jiffies.h>
с атрибутом
volatile
. Ключевое слово
volatile
указывает компилятору, что эту переменную необходимо считывать из того места, где она хранится в оперативной памяти, и никогда не использовать копию, хранящуюся в регистре процессора. Это гарантирует, что указанный цикл выполнится, как и ожидается.

Короткие задержки

Иногда коду ядра (и снопа обычно драйверам) необходимы задержки на очень короткие интервалы времени (короче, чем период системного таймера), причем интервал должен отслеживаться с достаточно высокой точностью. Это часто необходимо для синхронизации с аппаратным обеспечением, для которого описано некоторое минимальное время выполнения действий, и которое часто бывает меньше одной миллисекунды. В случае таких малых значений времени невозможно использовать задержки на основании переменной

jiffies
, как показано в предыдущем примере. При частоте системного таймера, равной 100 Гц, значение периода системного таймера достаточно большое — 10 миллисекунд! Даже при частоте системного таймера 1000 Гц, период системного таймера равен одной миллисекунде. Ясно, что необходимо другое решение, которое обеспечивает более короткие и точные задержки.

Ядро предоставляет две функции для обеспечения микросекундных и миллисекундных задержек, которые определены в файле

<linux/delay.h>
и не используют переменную
jiffies
.

void udelay(unsigned long usecs);

void mdelay(unsigned long msecs);

Первая функция позволяет задержать выполнение на указанное количество микросекунд с использованием цикла. Вторая функция задерживает выполнение на указанное количество миллисекунд. Следует вспомнить, что одна секунда равна 1000 миллисекундам, что эквивалентно 1000000 микросекунд. Использование этих функций тривиально.

udelay(150); /* задержка на 150 μs */

Функция

udelay
выполнена на основе цикла, для которого известно, сколько итераций необходимо выполнить за указанный период времени. Функция
mdelay
выполнена на основе функции
udelay
. Так как в ядре известно, сколько циклов процессор может выполнить в одну секунду (смотрите ниже замечание по поводу характеристики BogoMlPS), функция
udelay
просто масштабирует это значение для того, чтобы скорректировать количество итераций цикла для получения указанной задержки.

Поделиться:
Популярные книги

Восход. Солнцев. Книга X

Скабер Артемий
10. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга X

Мастер 7

Чащин Валерий
7. Мастер
Фантастика:
фэнтези
боевая фантастика
попаданцы
технофэнтези
аниме
5.00
рейтинг книги
Мастер 7

Неудержимый. Книга XIV

Боярский Андрей
14. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Неудержимый. Книга XIV

Все не случайно

Юнина Наталья
Любовные романы:
современные любовные романы
7.10
рейтинг книги
Все не случайно

Гром над Тверью

Машуков Тимур
1. Гром над миром
Фантастика:
боевая фантастика
5.89
рейтинг книги
Гром над Тверью

Идущий в тени 5

Амврелий Марк
5. Идущий в тени
Фантастика:
фэнтези
рпг
5.50
рейтинг книги
Идущий в тени 5

Кровь, золото и помидоры

Распопов Дмитрий Викторович
4. Венецианский купец
Фантастика:
альтернативная история
5.40
рейтинг книги
Кровь, золото и помидоры

Неудержимый. Книга VIII

Боярский Андрей
8. Неудержимый
Фантастика:
фэнтези
попаданцы
аниме
6.00
рейтинг книги
Неудержимый. Книга VIII

Чужое наследие

Кораблев Родион
3. Другая сторона
Фантастика:
боевая фантастика
8.47
рейтинг книги
Чужое наследие

Пистоль и шпага

Дроздов Анатолий Федорович
2. Штуцер и тесак
Фантастика:
альтернативная история
8.28
рейтинг книги
Пистоль и шпага

Мастер 4

Чащин Валерий
4. Мастер
Фантастика:
героическая фантастика
боевая фантастика
попаданцы
5.00
рейтинг книги
Мастер 4

Темный Патриарх Светлого Рода 6

Лисицин Евгений
6. Темный Патриарх Светлого Рода
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Темный Патриарх Светлого Рода 6

Лорд Системы 14

Токсик Саша
14. Лорд Системы
Фантастика:
фэнтези
попаданцы
рпг
5.00
рейтинг книги
Лорд Системы 14

Падение Твердыни

Распопов Дмитрий Викторович
6. Венецианский купец
Фантастика:
попаданцы
альтернативная история
5.33
рейтинг книги
Падение Твердыни