На 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меняется на противоположный!
К счастью, ядро предоставляет четыре макроса для сравнения двух значений счетчика импульсов таймера, которые корректно обрабатывают переполнение счетчиков. Они определены в файле следующим образом.
#define time_after(unknown, known) ((long)(known) - (long)(unknown) < 0)
#define time_before(unknown, known) \
((long) (unknown) - (long) (known) < 0)
#define time_after_eq(unknown, known) \
((long)(unknown) - (long) (known) >= 0)
#define \
time_before_eq(unknown, known) ((long)(known) - (long) (unknown) >= 0)
Параметр unknown— это обычно значение переменной jiffies, а параметр known — значение, с которым его необходимо сравнить.
Макрос time_after(unknown, known)возвращает значение true, если момент времени 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 {
/* мы не превысили лимит времени — это хорошо ... */
}
Если любопытно, каким образом эти макросы предотвращают ошибки, связанные с переполнением, то попробуйте подставить различные значения параметров. А затем представьте, что один из параметров переполнился, и посмотрите, что при этом произойдет.
Пространство пользователя и параметр HZ
Раньше изменение параметра НZприводило к аномалиям в пользовательских программах. Это происходило потому, что значения параметров, связанных со временем, экспортировались в пространство пользователя в единицах, равных количеству импульсов системного таймера в секунду. Так как такой интерфейс использовался давно, то в пользовательских приложениях считалось, что параметр HZ имеет определенное конкретное значение. Следовательно, при изменении значения параметра HZ изменялись значения, которые экспортируются в пространство пользователя, в одинаковое число раз. Информация о том, во сколько раз изменились значения, в пространство пользователя не передавалась! Полученное от ядра значение времени работы системы могло интерпретироваться как 20 часов, хотя на самом деле оно равнялось только двум часам.
Читать дальше