Главная » Ядро Linux » Переменная jiffie s

0

Глобальная  переменная  jiffi e s   содержит  количество  импульсов  системного таймера, которые были получены со  времени загрузки системы. При загрузке ядро устанавливает значение этого параметра в  нуль  и  он  увеличивается на  единицу при каждом прерывании системного таймера. Так  как  в секунду возникает HZ прерываний системного таймера, то  за  секунду значение переменной  jiffie s увеличивается на  HZ.  Время работы системы (uptime) поэтому равно jiffies/H Z  секунд.

Этимология слова jiffy

Происхождение слова jiffy (миг,  мгновение) точно неизвестно. Считается, что фразы типа "in a jiffy"  (в одно мгновение) появились в Англии в восемнадцатом веке. В быту термин jiffy [миг) означает неопределенный, но очень короткий промежуток времени.

В научных приложениях слово jiffy используется для обозначения различных интервалов времени (обычно порядка 10 ms). В физике это слово иногда используется для указания интервала времени, который требуется свету, чтобы пройти определенное расстояние (обычно, фут, сантиметр, или расстояние, равное размеру нуклона).

В вычислительной технике термин jiffy — это обычно интервал времени между двумя соседними импульсами системного таймера, которые были успешно обработаны. В электричестве jiffy — период переменного тока. В США jiffy— это 1/60 секунды.

В приложении к операционным системам, в частности к Unix, jiffy— это интервал времени между двумя соседними успешно обработанными импульсами системного таймера. Исторически это значение равно 100 ms. Как уже было показано, интервал времени jiffy в операционной системе Linux может иметь разные значения.

Переменная  jiffie s  определена  в  файле   <linux/jiffies.h >   следующим  образом.

extern unsigned long volatile jiffies;

Определение этой переменной достаточно специфичное,  и оно  будет  рассмотрено  более подробно в  следующем разделе. Сейчас давайте рассмотрим пример кода ядра.  Пересчет из  секунд в  значение переменной  jiffie s  можно выполнить следующим образом.

(секунды * HZ)

Отсюда  следует, что преобразование из  значения переменной jiffie s в секунды можно  выполнить, как показано ниже.

(jiffies / HZ)

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

unsigned long time_starnp = jiffies;    /* сейчас */

unsigned long next_tick = jiffies + 1; /* через один импульс таймера от текущего момента */

unsigned long later = jiffies + 5*HZ;  /* через пять секунд от текущего

момента */

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

Заметим, что переменная jiffie s имеет  тип  unsigned   long  и  использовать какой-либо другой тип  будет неправильным.

Внутреннее представление переменной jiffie s

Переменная  jiffie s  исторически  всегда  представлялась  с  помощью  типа unsigned   long и, следовательно, имеет  длину  32 бит для 32-разрядных аппаратных платформ и 64 бит для 64-разрядных. В случае  32-разрядного значения переменной jiffie s и частоты  появления временных отметок   100 раз  в секунду, переполнение этой  переменной будет происходить примерно каждые  497 дней, что является вполне возможным событием. Увеличение значения параметра HZ до 1000 уменьшает период  переполнения до 47.9 дней!  В случае  64-разрядного типа  переменной jiffies , переполнение этой  переменной невозможно за время  существования чего-либо при любых возможных значениях параметра HZ  для любой  аппаратной платформы.

Из соображений производительности и по историческим причинам — в основном, для совместимости с уже существующим кодом  ядра — разработчики ядра предпочли  оставить  тип  переменной jiffie s — unsigned   long.  Для решения проблемы пришлось немного подумать  и применить возможности компоновщика.

Как  уже говорилось, переменная jiffies определяется в следующем виде  и имеет тип  unsigned    long.

extern unsigned long volatile jiffies;

Вторая   переменная  определяется в  файле   <linux/jiffies.h >  в  следующем виде.

extern u64 jiffies_64;

Директивы компоновщика ld (1), которые используются для сборки главного образа ядра  (для  аппаратной платформы х86 описаны в файле  arch/i386/kernel / vmlinux.lds.S) ,  указывают компоновщику, что  переменную  jiffie s  необходимо совместить с  началом переменной  jiffies_64 .

Jiffies = jiffies_64;

Следовательно, переменная  jiffie s — это  просто  32 младших  разряда  полной

64-разрядной переменной  jiffies_64 .  Так  как  в большинстве случаев  переменная

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

В случае  применения  64-разрядного значения,  переполнение не  может  возникнуть  за  время   существования чего-либо.  В  следующем  разделе   будут  рассмотрены проблемы, связанные с переполнением (хотя  переполнение счетчика импульсов системного таймера и  не  желательно, но  это  вполне нормальное и  ожидаемое событие).  Код, который используется для  управления ходом  времени,  использует все  64 бит, и  это  предотвращает возможность переполнения  64-разрядного значения.  На рис.  10.1  показана  структура переменных  jiffie s  и  jiffies_64 .

Переменная jiffies_6 4 (и переменная j i f f i e s на 64-разрядной машине)

Переменная jif f ie s на 32-разрядной машине

Рис. 10.1. Структура переменных jiffies и jiffies_64

Код,  который  использует переменную  jiffies , просто   получает   доступ   к  тридцати   двум   младшим  битам   переменной  jiffies_64 .   Функция  get_jiffies_6 4  () может  быть  использована для  получения полного 64-разрядного значения5 . Такая  необходимость возникает редко,  следовательно большая часть  кода  просто  продолжает считывать младшие  32  разряда непосредственно  из  переменной  jiffies .

Н а  64-разрядных  аппаратных платформах переменные  jiffies_6 4  и  jiffie s просто   совпадают. Код  может  либо  непосредственно  считывать значение  переменной   jiffies , либо  использовать функцию  get_jiffies_6 4  () , так  как  оба  этих  способа  позволяют получить аналогичный  эффект.

Переполнение переменной jiffie s

Переменная  jiffies , так  же  как  и  любое  целое  число  языка программирования С, после  достижения максимально возможного значения переполняется. Для  32-разрядного беззнакового  целого   числа  максимальное значение равно  2 32 -1 . Поэтому перед  тем  как  счетчик импульсов системного таймера переполнится,  должно прийти  4294967295  импульсов таймера. Если  значение счетчика равно  этому  значению и счетчик увеличивается на  1, то  значение счетчика становится равным нулю.

Рассмотрим пример переполнения.

unsigne d  lon g  timeou t  =  jiffie s  +  HZ/2; /*   значение  лимита  времени равно   0.5  с  */

5   Необходим а специальная функция ,  так как  на  32-разрядных  аппаратны х платформа х нельзя  атомарн о обращаться  к двум машинным словам  64-разрядного значения,  Специальная функция,  перед тем как считать значение , блокирует счетчик импульсов системного таймера с помощью блокировки        xtime_lock.

/* выполним некоторые действия и проверим, не слишком ли это много заняло времени . . . */

if (timeout < jiffies) {

/* мы превысили лимит времени — это ошибка … */

} else {

}

/* мы не превысили лимит времени — это хорошо … */

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

В данном  примере  может возникнуть  несколько  потенциальных  проблем,  связанных с переполнением. Рассмотрим  одну из них. Что произойдет,  если переменная  jiffie s переполнится  и снова начнет увеличиваться  с нуля после того, как ей было присвоено  значение переменной  timeout? При этом условие гарантированно не выполнится, так как значение  переменной  jiffie s будет меньше,  чем значение переменной  timeout , хотя логически оно должно быть больше.  По идее значение переменной  jiffie s должно  быть огромным  числом,  всегда большим  значения  переменной  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_afte r (unknown,    known)  возвращает значение  true , если момент времени  unknown  происходит после момента  времени  known,  в противном  случае возвращается  значение  false . Макрос  time_befor e (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  часов, хотя на самом деле оно равнялось только двум часам.

Чтобы исправить это,  код ядра должен нормировать все  значения переменной jiffies ,  которые экспортируются в  пространство пользователя.  Нормировка  реализуется путем определения константы USER_HZ,  равной значению параметра HZ, которое  ожидается в пространстве пользователя. Та  как для аппаратной платформы х86  значение параметра HZ  исторически равно 100, то  значение константы USER_ HZ=100. Макрос   jiffies_to_clock_t( )   используется для нормировки значения счетчика импульсов системного таймера, выраженного в  единицах HZ, в  значение счетчика импульсов, выраженное в  единицах USER_HZ. Используемый макрос зависит от  того, кратны ли значения параметров HZ и USER_HZ  один другому. Если кратны, то  этот макрос имеет следующий очень простой вид.

#define jiffies_to_clock_t(x)  ((х) / (HZ / USER_HZ))

Если не кратны, то  используется более сложный алгоритм.

Функция jiffies_64_to_clock_ t ()  используется для конвертирования 64-битового значения переменной jiffie s  из единиц HZ в единицы USER_HZ.

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

unsigned long start = jiffies;

unsigned long total_time;

/* выполнить некоторую работу … */

total_time = jiffies start;

printk("ЭTO заняло %lu импульсов таймера\n", jiffies_to_clock_t(total_time));

В пространстве пользователя передаваемое значение должно  быть  таким, каким оно было бы, если бы выполнялось равенство HZ=USER_HZ. Если это равенство не  справедливо, то макрос  выполнит нужную  нормировку и все  будут счастливы. Конечно, этот пример несколько нелогичный: больше  смысла  имело  бы печатать значение времени не в импульсах системного таймера, а в секундах  следующим образом.

printk("Эт о заняло %lu  секунд\n",  tota l  time  / HZ);

Аппаратные часы и таймеры

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

Часы реального времени

Часы  реального времени (real-time clock, RTC)  представляют собой  энергонезависимое устройство для сохранения системного времени. Устройство RTC  продолжает отслеживать время, даже когда  система  отключена, благодаря небольшой батарее, которая обычно  находится на системной плате.  Для аппаратной платформы PC устройство RTC  интегриронано в КМОП-микросхему BIOS.  При  этом  используется общая  батарея  и для работы  устройства RTC  и для сохранения установок BIOS.

При  загрузке  ядро  считывает информацию из устройства RTC  и использует ее для инициализации значения абсолютного времени, которое  хранится в переменной xtime.  Обычно ядро  не считывает это значение снова, однако  для некоторых поддерживаемых аппаратных платформ, таких  как  х8б, значение абсолютного времени периодически записывается в устройство RTC.  Тем  не менее, часы  реального времени  важны  в первую  очередь  на этапе  загрузки  системы, когда  инициализируется переменная xtime.

Системный таймер

Системный таймер  играет  более  значительную роль для отслеживания хода времени  ядром.  Независимо от аппаратной платформы, идея, которая лежит  в основе системного таймера, одна и та же — это обеспечение механизма управления прерываниями, которые возникают периодически с постоянной частотой. Для некоторых аппаратных платформ это реализуется с помощью электронных часов, которые генерируют колебания с программируемой частотой. В других аппаратных платформах используется декрементный счетчик  (decrementer), куда можно  записать некоторое начальное значение, которое  будет периодически, с фиксированной частотой, уменьшаться на единицу, пока  значение счетчика не станет  равным нулю.  Когда  значение  счетчика становится равным нулю, генерируется прерывание. В любом  случае эффект получается один  и тот же.

Для аппаратной платформы х86 главный системный таймер — это программируемый  интервальный таймер  (programmable interval timer, PIT).  Таймер  PIT существует

на всех машинах платформы PC.  Co времен  операционной системы DOS  он используется  для управления прерываниями. Ядро  программирует таймер  PIT  при  загрузке, для того чтобы  периодически генерировать прерывание номер  нуль с частотой HZ.  Этот таймер— простое  устройство с ограниченными возможностями, но, тем не менее, хорошо  выполняющее свою  работу.  Другие эталоны времени для аппаратной платформы х86 включают таймер  APIC  (Advanced  Programmable Interrupt Controller, расширенный программируемый контроллер прерываний) и счетчик  отметок  времени (TSC, Time Stamp Counter).

Источник: Лав,  Роберт. Разработка ядра  Linux, 2-е  издание. : Пер.  с англ.  — М.  : ООО  «И.Д.  Вильяме» 2006. — 448 с. : ил. — Парал. тит. англ.

По теме:

  • Комментарии