приводило к аномалиям в пользовательских программах. Это происходило потому, что значения параметров, связанных со временем, экспортировались в пространство пользователя в единицах, равных количеству импульсов системного таймера в секунду. Так как такой интерфейс использовался давно, то в пользовательских приложениях считалось, что параметр HZ имеет определенное конкретное значение. Следовательно, при изменении значения параметра HZ изменялись значения, которые экспортируются в пространство пользователя, в одинаковое число раз. Информация о том, во сколько раз изменились значения, в пространство
пользователя не передавалась! Полученное от ядра значение времени работы системы могло интерпретироваться как 20 часов, хотя на самом деле оно равнялось только двум часам.
Чтобы исправить это, код ядра должен нормировать все значения переменной
jiffies
, которые экспортируются в пространство пользователя. Нормировка реализуется путем определения константы
USER_HZ
, равной значению параметра
HZ
, которое ожидается в пространстве пользователя. Так как для аппаратной платформы x86 значение параметра
HZ
исторически равно 100, то значение константы
USER_HZ=100
. Макрос
jiffies_to_clock_t
используется для нормировки значения счетчика импульсов системного таймера, выраженного в единицах
HZ
, в значение счетчика импульсов, выраженное в единицах
USER_HZ
. Используемый макрос зависит от того, кратны ли значения параметров
HZ
и
USER_HZ
один другому. Если кратны, то этот макрос имеет следующий очень простой вид.
Если не кратны, то используется более сложный алгоритм.
Функция
jiffies_64_to_clock_t
используется для конвертирования 64-битового значения переменной
jiffies
из единиц
HZ
в единицы
USER_HZ
.
Эти функции используются везде, где значения данных, выраженных в единицах числа импульсов системного таймера в секунду, должны экспортироваться в пространство пользователя, как в следующем примере.
unsigned long start = jiffies;
unsigned long total_time;
/* выполнить некоторую работу ... */
total_time = jiffies - start;
printk("Это заняло %lu импульсов таймера\n",
jiffies_to_clock_t(total_time));
В пространстве пользователя передаваемое значение должно быть таким, каким оно было бы, если бы выполнялось равенство
HZ=USER_HZ
. Если это равенство не справедливо, то макрос выполнит нужную нормировку и все будут счастливы. Конечно, этот пример несколько нелогичный: больше смысла имело бы печатать значение времени не в импульсах системного таймера, а в секундах следующим образом.
printk("Это заняло %lu секунд\n", total time / HZ);
Аппаратные часы и таймеры
Различные аппаратные платформы предоставляют два аппаратных устройства, которые помогают вести учет времени, — это системный таймер, о котором уже было рассказано, и часы реального времени. Реализация и поведение этих устройств могут быть различными для машин
разного типа, но общее их назначение и принципы работы с ними почти всегда одинаковы.
Часы реального времени
Часы реального времени (real-time clock, RTC) представляют собой энергонезависимое устройство для сохранения системного времени. Устройство RTC продолжает отслеживать время, даже когда система отключена, благодаря небольшой батарее, которая обычно находится на системной плате. Для аппаратной платформы PC устройство RTC интегрировано в КМОП-микросхему BIOS. При этом используется общая батарея и для работы устройства RTC и для сохранения установок BIOS.
При загрузке ядро считывает информацию из устройства RTC и использует ее для инициализации значения абсолютного времени, которое хранится в переменной
xtime
. Обычно ядро не считывает это значение снова, однако для некоторых поддерживаемых аппаратных платформ, таких как x86, значение абсолютного времени периодически записывается в устройство RTC. Тем не менее, часы реального времени важны в первую очередь на этапе загрузки системы, когда инициализируется переменная
xtime
.
Системный таймер
Системный таймер играет более значительную роль для отслеживания хода времени ядром. Независимо от аппаратной платформы, идея, которая лежит в основе системного таймера, одна и та же — это обеспечение механизма управления прерываниями, которые возникают периодически с постоянной частотой. Для некоторых аппаратных платформ это реализуется с помощью электронных часов, которые генерируют колебания с программируемой частотой. В других аппаратных платформах используется декрементный счетчик (decrementer), куда можно записать некоторое начальное значение, которое будет периодически, с фиксированной частотой, уменьшаться на единицу, пока значение счетчика не станет равным нулю. Когда значение счетчика становится равным нулю, генерируется прерывание. В любом случае эффект получается один и тот же.
Для аппаратной платформы x86 главный системный таймер — это программируемый интервальный таймер (programmable interval timer, PIT). Таймер PIT существует на всех машинах платформы PC. Co времен операционной системы DOS он используется для управления прерываниями. Ядро программирует таймер PIT при загрузке, для того чтобы периодически генерировать прерывание номер нуль с частотой
HZ
. Этот таймер— простое устройство с ограниченными возможностями, но, тем не менее, хорошо выполняющее свою работу. Другие эталоны времени для аппаратной платформы x86 включают таймер APIC (Advanced Programmable Interrupt Controller, расширенный программируемый контроллер прерываний) и счетчик отметок времени (TSC, Time Stamp Counter).
Обработчик прерываний таймера
Теперь, когда мы разобрались, что такое
jiffies
и
HZ
, а также какова роль системного таймера, рассмотрим реализацию обработчика прерываний системного таймера. Обработчик прерываний таймера разбит на две части: часть, зависимую от аппаратной платформы, и независимую часть.
Подпрограмма, которая зависит от аппаратной платформы, регистрируется в качестве обработчика прерываний системного таймера и выполняется, когда срабатывает системный таймер. Конкретная работа, конечно, зависит от аппаратной платформы, но большинство обработчиков выполняют следующие действия.