Мінлива jiffie s

Глобальна змінна jiffi es містить кількість імпульсів системного таймера, які були отримані з часу завантаження системи При завантаженні ядро ​​встановлює значення цього параметра в нуль і він збільшується на одиницю при кожному перериванні системного таймера Так як в секунду виникає HZ переривань системного таймера, то за секунду значення змінної jiffie s збільшується на HZ Час роботи системи (uptime) тому одно jiffies / HZ секунд

Етимологія слова jiffy

Походження слова jiffy (Мить, мить) точно невідомо Вважається, що фрази типу In a jiffy (В одну мить) зявилися в Англії у вісімнадцятому столітті У побуті термін jiffy [мить) означає невизначений, але дуже короткий проміжок часу

У наукових додатках слово jiffy використовується для позначення різних інтервалів часу (зазвичай близько 10 ms) У фізиці це слово іноді використовується для вказівки інтервалу часу, який потрібно світла, щоб пройти певний відстань (звичайно, фут, сантиметр, або відстань, рівне розміру нуклона)

У обчислювальної техніки термін jiffy — це зазвичай інтервал часу між двома сусідніми імпульсами системного таймера, які були успішно оброблені У електриці jiffy — період змінного струму У США jiffy— це 1/60 секунди

У додатку до операційних систем, зокрема до Unix, jiffy— це інтервал часу між двома сусідніми успішно обробленими імпульсами системного таймера Історично це значення дорівнює 100 ms Як вже було показано, інтервал часу jiffy в операційній системі Linux може мати різні значення

визначена у файлі таким чином

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

історично завжди представлялася за допомогою типу unsigned long і, отже, має довжину 32 біт для 32-розрядних апаратних платформ і 64 біт для 64-розрядних У випадку 32-розрядного значення змінної jiffie s і частоти появи тимчасових відміток 100 разів на секунду, переповнення цієї змінної буде відбуватися приблизно кожні 497 днів, що є цілком можливим подією Збільшення значення параметра HZ до 1000 зменшує період переповнення до 479 днів У разі 64-розрядного типу змінної jiffies, переповнення цієї змінної неможливо за час існування чого-небудь за будь-яких можливих значеннях параметра HZ для будь апаратної платформи

З міркувань продуктивності і з історичних причин – в основному, для сумісності з вже існуючим кодом ядра – розробники ядра вважали за краще залишити тип змінної jiffie s – unsigned long Для вирішення проблеми довелося трохи подумати і застосувати можливості компоновщика

Як вже говорилося, змінна jiffies визначається в наступному вигляді і має тип unsigned long

extern unsigned long volatile jiffies

Друга змінна визначається у файлі в наступному вигляді

extern u64 jiffies_64

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

Jiffies = jiffies_64

Отже, змінна jiffie s – це просто 32 молодших розряду повної

64-розрядної змінної jiffies_64 Так як в більшості випадків мінлива

jiffie s використовується для вимірювання проміжків часу, то для більшої частини коду істотними є лише молодші 32 біт

У разі застосування 64-розрядного значення, переповнення не може виникнути за час існування чого-небудь У наступному розділі будуть розглянуті проблеми, повязані з переповненням (хоча переповнення лічильника імпульсів системного таймера і не бажано, але це цілком нормальне і очікувана подія) Код, який використовується для управління ходом часу, використовує всі 64 біт, і це запобігає можливість переповнення 64-розрядного значення На рис 101 показана структура змінних jiffie s і jiffies_64

Мінлива jiffies_6 4 (і мінлива jiffies на 64-розрядної машині)

Мінлива jif f ie s на 32-розрядної машині

Рис 101 Структура змінних 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 / * Значення ліміту часу одно 05 с * /

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

/ * Виконаємо деякі дії і перевіримо, чи не занадто це багато зайняло часу * /

if (timeout &lt jiffies) {

/ * Ми перевищили ліміт часу – це помилка .. * /

} else {

}

/ * Ми не перевищили ліміт часу – це добре .. * /

Призначення цієї ділянки коду – встановити ліміт часу до настання деякої події в майбутньому, а точніше півсекунди від поточного моменту Код може продовжити виконання деякої роботи – можливо, записати деякі дані в апаратний пристрій і чекати відповіді Після виконання, якщо весь процес перевищив ліміт встановленого часу, код відповідним чином обробляє помилку

У даному прикладі може виникнути кілька потенційних проблем, повязаних з переповненням Розглянемо одну з них Що відбудеться, якщо змінна jiffie s переповниться і знову почне збільшуватися з нуля після того, як їй було присвоєно значення змінної timeout При цьому умова гарантовано не виконається, так як значення змінної jiffie s буде менше, ніж значення змінної timeout , Хоча логічно воно має бути більше По ідеї значення змінної jiffie s повинно бути величезним числом, завжди великим значення змінної timeout Так як ця змінна переповнилася, то тепер її значення стало дуже маленьким числом, яке, можливо, відрізняється від нуля на кілька імпульсів таймера Через переповнення результат виконання оператора if змінюється на протилежний

На щастя, ядро ​​надає чотири макросу для порівняння двох значень лічильника імпульсів таймера, які коректно обробляють переповнення лічильників Вони визначені у файлі таким чином

#define time_after(unknown, known) ((long)(known) (long)(unknown) &lt 0)

#define time_before(unknown, known) ((long) (unknown) (long)(known) &lt 0)

#define time_after_eq(unknown, known) ((long) (unknown) (long)(known) &gt= 0)

#define

time_before_eq(unknown, known) ((long) (known) (long)(unknown) &gt= 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 / * Значення ліміту часу одно 05 с * /

/ * Виконаємо деякі дії і перевіримо, чи не занадто це багато зайняло часу .. * /

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 с : Ил – Парал тит англ

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*