, которые используются для сборки главного образа ядра (для аппаратной платформы x86 описаны в файле
arch/i386/kernel/vmlinux.lds.S
), указывают компоновщику, что переменную
jiffies
необходимо совместить с началом переменной
jiffies_64
.
jiffies = jiffies_64;
Следовательно, переменная
jiffies
— это просто 32 младших разряда полной 64-разрядной переменной
jiffies_64
. Так как в большинстве случаев переменная
jiffies
используется для измерения промежутков времени, то для большей части кода существенными являются только младшие 32 бит.
В случае применения 64-разрядного значения, переполнение не может возникнуть за время существования чего-либо. В следующем разделе будут рассмотрены проблемы, связанные с переполнением (хотя переполнение счетчика импульсов системного таймера и не желательно, но это вполне нормальное и ожидаемое событие). Код, который используется для управления ходом времени, использует все 64 бит, и это предотвращает возможность переполнения 64-разрядного значения. На рис. 10.1 показана структура переменных
jiffies
и
jiffies_64
.
Рис. 10.1. Структура переменных
jiffies
и
jiffies_64
Код, который использует переменную
jiffies
, просто получает доступ к тридцати двум младшим битам переменной
jiffies_64
. Функция
get_jiffies_64
может быть использована для получения полного 64-разрядного значения [57] . Такая необходимость возникает редко, следовательно большая часть кода просто продолжает считывать младшие 32 разряда непосредственно из переменной
jiffies
.
57
Необходима специальная функция, так как на 32-разрядных аппаратных платформах нельзя атомарно обращаться к двум машинным словам 64-разрядного значения, Специальная функция, перед тем как считать значение, блокирует счетчик импульсов системного таймера с помощью блокировки
xtime_lock
.
На 64-разрядных аппаратных платформах переменные
jiffies_64
и
jiffies
просто совпадают. Код может либо непосредственно считывать значение переменной
jiffies
, либо использовать функцию
get_jiffies_64
, так как оба этих способа позволяют получить аналогичный эффект.
Переполнение переменной
jiffies
Переменная
jiffies
, так же как и любое целое число языка программирования С, после достижения максимально возможного значения переполняется. Для 32-разрядного беззнакового целого числа максимальное значение равно 2³²- 1. Поэтому перед тем как счетчик импульсов системного таймера переполнится, должно прийти 4294967295 импульсов таймера. Если значение счетчика равно этому значению и счетчик увеличивается на 1, то значение счетчика становится равным нулю.
Рассмотрим пример переполнения.
unsigned long timeout = jiffies + HZ/2; /* значение лимита времени
равно 0.5 с */
/* выполним некоторые действия и проверим, не слишком ли это много
заняло времени ... */
if (timeout < jiffies) {
/* мы превысили лимит времени — это ошибка ... */
} else {
/* мы не превысили лимит времени — это хорошо ... */
}
Назначение этого участка кода — установить лимит времени
до наступления некоторого события в будущем, а точнее полсекунды от текущего момента. Код может продолжить выполнение некоторой работы — возможно, записать некоторые данные в аппаратное устройство и ожидать ответа. После выполнения, если весь процесс превысил лимит установленного времени, код соответственным образом обрабатывает ошибку.
В данном примере может возникнуть несколько потенциальных проблем, связанных с переполнением. Рассмотрим одну из них. Что произойдет, если переменная
jiffies
переполнится и снова начнет увеличиваться с нуля после того, как ей было присвоено значение переменной
timeout
? При этом условие гарантированно не выполнится, так как значение переменной
jiffies
будет меньше, чем значение переменной
timeout
, хотя логически оно должно быть больше. По идее значение переменной
jiffies
должно быть огромным числом, всегда большим значения переменной
timeout
. Так как эта переменная переполнилась, то теперь ее значение стало очень маленьким числом, которое, возможно, отличается от нуля на несколько импульсов таймера. Из-за переполнения результат выполнения оператора
if
меняется на противоположный!
К счастью, ядро предоставляет четыре макроса для сравнения двух значений счетчика импульсов таймера, которые корректно обрабатывают переполнение счетчиков. Они определены в файле
, если момент времени unknown происходит после момента времени
known
, в противном случае возвращается значение
false
. Макрос
time_before(unknown, known)
возвращает значение true, если момент времени
unknown
происходит раньше, чем момент времени known, в противном случае возвращается значение
false
. Последние два макроса работают аналогично первым двум, за исключением того, что возвращается значение "истинно", если оба параметра равны друг другу.
Версия кода из предыдущего примера, которая предотвращает ошибки, связанные с переполнением, будет выглядеть следующим образом.
unsigned long timeout = jiffies + HZ/2; /* значение лимита времени
равно 0.5 с */
/* выполним некоторые действия и проверим, не слишком ли это много
заняло времени ... */
if (time_after(jiffies, timeout}) {
/* мы превысили лимит времени — это ошибка ... */
} else {
/* мы не превысили лимит времени — это хорошо ... */
}
Если любопытно, каким образом эти макросы предотвращают ошибки, связанные с переполнением, то попробуйте подставить различные значения параметров. А затем представьте, что один из параметров переполнился, и посмотрите, что при этом произойдет.