Абсолютна час

Поточне значення абсолютного часу (time of day, wall time, час дня) визначено у файлі kernel / timer з наступним чином

struct timespec xtime

Структура даних timespe c визначена у файлі в наступному вигляді

struct timespec {

time_t tv_sec /* seconds */

long tv_nsec /* nanoseconds */

}

Поле xtimetv_se c містить кількість секунд, які пройшли з 1 січня

1970 (UTC, Universal Coordinated Time, загальне скоординоване час) Зазначена дата називаєтьсяepoch (Початок епохи) У більшості Unix-подібних операційних систем рахунок часу ведеться з початку епохи У полі xtimetv_nse c зберігається кількість наносекунд, які пройшли в останній секунді

Читання або запис змінної xtime вимагає захоплення блокування xtime_lock Це блокування – не звичайна спін-блокування, а секвентпая блокування, яка розглядається в розділі 9, Засоби синхронізації в ядрі.

Для оновлення значення змінної xtime необхідно захопити секвентной блокування на запис таким чином

write_seqlock(&ampxtime_lock)

/ * Оновити значення змінної xtime .. * /

write_sequnlock(&ampxtime_lock)

Зчитування значення змінної xtim e вимагає застосування функцій read _

seqbegin () і read_seqretr y () так

do {

unsigned long lost

seq = read_seqbegin(&ampxtime_lock)

usec = timer-&gtget_offset() lost = jiffies wall_jiffies if (lost)

usec += lost * (1000000 / HZ)

sec = xtimetv_sec

usec += (xtimetv_nsec / 1000)

} while (read_seqretry(&ampxtime_lock, seq))

Цей цикл повторюється до тих пір, поки не буде гарантії того, що під час зчитування даних не було запису Якщо під час виконання циклу приходить переривання таймера і змінна xtime оновлюється під час виконання циклу, що повертається номер послідовності буде неправильним і цикл повториться знову

Головний користувальницький інтерфейс для отримання значення абсолютного часу – це системний виклик gettimeofda y (), який реалізований як функція sys_gettimeofday () так

asmlinkage long sys_gettimeofday(struct  timeval *tv, struct timezone *tz)

{

if (likely(tv =NULL)) { struct timeval_ktv do_gettimeofday(&ampktv)

if (copy_to_userftv, &ampktv, sizeof(ktv))

return -EFAULT

}

if (unlikely(tz =NULL)) {

if (copy_to_user(tz, &ampsys_tz, sizeof(sys_tz)))

return -EFAULT

}

return 0

}

Якщо з простору користувача передано ненульове значення параметра tv, то викликається апаратно-залежна функція do_gettimeofday () Ця функція головним чином виконує цикл зчитування змінної xtime, який був тільки що розглянутий Аналогічно, якщо параметр tz не дорівнює нулю, користувачеві повертається значення часового поясу (time zone), в якому знаходиться операційна система Цей параметр зберігається у змінній sys_tz Якщо при копіюванні в простір користувача значення абсолютного часу або годинного пояса виникли помилки, то функція повертає значення-EFAULT У випадку успіху повертається нульове значення

Ядро надає системний виклик time () 6, проте системний виклик gettimeofday () повністю перекриває його можливості Бібліотека функцій мови С також надає інші функції, повязані з абсолютним часом, такі як ftime () і ctirae ()

Системний виклик settimeofday () дозволяє встановити абсолютний час у вказане значення Для того щоб його виконати, процес повинен мати можливість використання CAP_SYS_TIME

Якщо не вважати поновлення змінної xtime, то ядро ​​не так часто використовує абсолютний час, як простір користувача Один важливий виняток-це код файлових систем, який зберігають в індексах файлів значення моментів часу доступу до файлів

Таймери

Таймери (Timers), або, як їх ще іноді називають, динамічні таймери, або таймери ядра, необхідні для управління ходом часу в ядрі Коду ядра часто необхідно відкладати виконання деяких функцій на пізніший час Тут навмисно обрано не дуже чітке поняття Пізніше. Призначення механізму нижніх половин – це не затримувати виконання, а не виконувати роботу прямо зараз У звязку з цим необхідний інструмент, який дозволяє затримати виконання роботи на деякий інтервал часу Якщо цей інтервал часу не надто маленький, але і не дуже великий, то вирішення проблеми – Таймери ядра

6 Дл я деякий х апаратно х платформ м функціяsys_time ()   не реалізована, а замість цього вона емулюється бібліотекою функцій мови С на підставі викликуgettimeofday ()

Таймери дуже легко використовувати Необхідно виконати деякі початкові дії, вказати момент часу закінчення очікування, вказати функцію, яка буде виконана, коли закінчиться інтервал часу очікування, і активізувати таймер Зазначена функція буде виконана, коли закінчиться інтервал часу таймера Таймери не є циклічними Коли закінчується інтервал часу очікування, таймер ліквідується Це одна з причин, чому таймери називають динамічними7 Таймери постійно створюються і ліквідуються, на кількість таймерів не існує обмежень Використання таймерів дуже популярно у всіх частинах ядра

Використання таймерів

Таймери представлені за допомогою структур time r list, яка визначена у файлі таким чином

struct tirner_list {

struct list_head entry / * Таймери зберігаються у звязаному списку * /

unsigned long expires / * Час закінчення терміну очікування в імпульсах системного таймера (jiffies) * /

spinlock_t lock / * Блокування для захисту даного таймера * /

void (* function) (unsigned long) / * Функція-обробник таймера * / unsigned long data / * Єдиний аргумент обробника * / struct tvec_t_base_s * base / * Внутрішні дані таймера, не чіпати * /

}

На щастя, використання таймерів не вимагає глибокого розуміння призначення полів цієї структури Насправді, вкрай не рекомендується використовувати поля цієї структури не за призначенням, щоб зберегти сумісність з можливими майбутніми змінами коду Ядро надає сімейство інтерфейсів для роботи з таймерами, щоб спростити цю роботу Всі необхідні визначення знаходяться у файлі Більшість реалізацій знаходиться у файлі kernel / timerс

Перший крок у створенні таймера – це його оголошення в наступному вигляді

struct timer_list my_timer

Далі мають бути ініціалізовані поля структури, які призначені для внутрішнього використання Це робиться за допомогою допоміжної функції перед викликом будь-яких функцій, які працюють з таймером

init_timer(&ampmy_timer)

Далі необхідно заповнити всі інші поля структури, наприклад, наступним чином

my_timerexpire s = jiffie s + delay / * Інтервал часу таймера закінчується через delay імпульсів * /

7 Інша причин а полягає в тому, що в ядрах старих версій (до 23) існували статичні таймери Такі таймер и створювалися в брешемо я компіляції, а не в брешемо я виконання Він і мали обмежені е можливості та через їх відсутність зараз ніхто не засмучується

my_timerdata = 0 / * У функцію-обробник Судет переданий параметр, рівний нулю * /

my_timerfunction = my_function / * Функція, яка буде виконана,

коли інтервал часу таймера закінчиться * /

Значення поля my_timerexpire s вказує час очікування в імпульсах системного таймера (необхідно вказувати абсолютна кількість імпульсів) Коли поточне значення змінної jiffie s стає більшим чи рівним значенню поля my_time r expires, викликається функція-обробник my_timerfunctio n з параметром my_timerdata Як видно з опису структури timer_list, функція-обробник повинна відповідати наступному прототипу

void my_timer_function(unsigned long data)

Параметр dat a дозволяє реєструвати декілька таймерів з одним обробником і відрізняти таймери з різними значеннями цього параметра Якщо в аргументі немає необхідності, то можна просто вказати нульове (або будь-яке інше) значення

Остання операція – це активізація таймера

add_timer(&ampmy_timer)

І таймер запускається Слід звернути увагу на важливість значення поля expired Ядро виконує обробник, коли поточне значення лічильника імпульсів системного таймера більше,ніж вказане значення часу спрацьовування таймера, або дорівнює йому Хоча ядро ​​і гарантує, що ніякої обробник таймера не виконуватиметься до закінчення терміну очікування таймера, проте можливі затримки з виконанням обробника таймера Зазвичай обробники таймерів виконуються в момент часу, близький до моменту часу спрацьовування, проте вони можуть бути відкладені і до наступного імпульсу системного таймера Отже, таймери не можна використовувати для роботи в жорсткому режимі реального часу

Іноді може знадобитися змінити момент часу спрацьовування таймера, який вже активізований У ядрі реалізована функція mod_time r (), яка дозволяє змінити момент часу спрацьовування активного таймера

i

mod_timer (& my_timer, jiffies + new_delay) / * Установка нового часу спрацьовування * /

Функція mod_time r () дозволяє також працювати з таймером, який ініціалізованим, але не активний Якщо таймер не активний, то функція mod_timer () активізує його Ця функція повертає значення 0, якщо таймер був неактивним, і значення 1, якщо таймер був активним У будь-якому випадку перед поверненням з функції mod_time r () таймер будуть активізований, і його час спрацьовування буде встановлено в вказане значення

Для того щоб деактивизировать таймер до моменту його спрацьовування, необхідно використовувати функцію del_time r () так

del_timer(&ampmy_timer)

Ця функція працює як з активними, так і неактивними таймерами Якщо таймер вже неактивний, то функція повертає значення 0, в іншому випадку повертається значення 1 Слід звернути увагу, що немає необхідності викликати цю

функцію для таймерів, інтервал очікування яких минув, так як вони автоматично деактівіпіруются

При видаленні таймерів потенційно може виникнути стан конкуренції Коли функція del_time r () повертає управління, вона гарантує тільки те, що таймер буде неактивним (тобто його обробник не буде виконано у майбутньому) Однак на багатопроцесорної машині обробник таймера може виконуватися в цей момент на іншому процесорі Для того щоб деактивизировать таймер і почекати, поки завершиться його обробник, який потенційно може виконуватися, необхідно використовувати функцію del_timer_syn c ():

del_timer_sync(&ampmy_timer)

На відміну від функції del_timer (), функція del_timer_sync () не може викликатися з контексту переривання

Стани конкуренції, повязані з таймерами

Так як таймери виконуються асинхронно по відношенню до виконуваного в даний момент кодом, то потенційно можуть виникнути декілька типів станів конкуренції за ресурси По-перше, ніколи не можна використовувати наступний код, як заміну функції inod_timer ()

del_timer (my_timer)

my_timer-&gtexpires = jiffies + new_delay

add_timer(my_timer)

По-друге, практично у всіх випадках слід використовувати функцію del_time r _sync (), а не функцію del_time r () В іншому випадку не можна гарантувати, що обробник таймера в даний момент не виконується Уявіть собі, що після видалення таймера код звільнить память або яким-небудь іншим чином втрутиться в ресурси, які використовує обробник таймера Тому синхронна версія більш краща

Нарешті, необхідно гарантувати захист всіх спільно використовуваних ДАНП, до яких звертається функція-обробник таймера Ядро виконує цю функцію асинхронно по відношенню до іншого коду Спільно використовувані дані повинні захищатися так, як розглядалося в главах 8 і 9

Реалізація таймерів

Ядро виконує обробники таймерів в контексті обробника відкладеного переривання після завершення обробки переривання таймера Обробник переривання таймера викликає функцію update_process_time s (), яка в свою чергу викликає функцію run_local_timer s (), що має наступний вигляд

void run_local_timers(void)

{

raise_softirq(TIMER_SOFTIRQ)

}

Відкладене переривання з номером TIMER_SOFTIRQ обробляється функцією run_tirner_softir q () Ця функція виконує на локальному процесорі обробники всіх таймерів, для яких закінчився період часу очікування (якщо такі є)

Таймери зберігаються у звязаному списку Однак у ядрі було б нерозумним переглядати весь список у пошуках таймерів, для яких минув час очікування, або підтримувати список в відсортованому стані на підставі часу спрацьовування таймерів В останньому випадку вставка і видалення таймерів зайняли б багато часу Замість цього таймери розбиваються на 5 груп на підставі часу спрацьовування Таймери переміщаються з однієї групи в іншу, у міру того як наближається момент часу спрацьовування Таке розбиття на групи гарантує, що в більшості випадків при виконанні обробника відкладеного переривання, відповідального за виконання обробників таймерів, ядро ​​буде виконувати мало роботи для пошуку таймерів, у яких закінчився період очікування Отже, код управління таймерами дуже ефективний

Джерело: Лав, Роберт Розробка ядра 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>

*

*