Книга: Разработка ядра Linux
Переполнение переменной jiffies
Переполнение переменной 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
меняется на противоположный!
К счастью, ядро предоставляет четыре макроса для сравнения двух значений счетчика импульсов таймера, которые корректно обрабатывают переполнение счетчиков. Они определены в файле <linux/jiffies.h>
следующим образом.
#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 {
/* мы не превысили лимит времени — это хорошо ... */
}
Если любопытно, каким образом эти макросы предотвращают ошибки, связанные с переполнением, то попробуйте подставить различные значения параметров. А затем представьте, что один из параметров переполнился, и посмотрите, что при этом произойдет.
- Переменная jiffies
- Внутреннее представление переменной jiffies
- Использование переменной окружения ISC_PATH
- 14.2. Переполнение буфера
- Объявление переменной на внутреннем уровне
- 12.3.3. Использование переменной окружения TMPDIR
- Объявление переменной перечислимого типа
- Листинг 4.14. (condvar.c) Управление работой потока с помощью сигнальной переменной
- Приложение D Маски подсети переменной длины
- D.2 Маски подсетей с переменной длиной
- Объявление простой переменной
- Объявление переменной на внешнем уровне