Написання обробника переривання

Наступне опис є типовим для обробника переривання

static irqreturn_t intr_handler (int irq, void *dev_id, struct pt_regs *regs)

Зауважимо, що воно має відповідати аргументу, який передається у функцію request_ir q () Перший параметр, irq, – це чисельне значення номера переривання, яке обслуговується обробником Зараз цей параметр практично не використовується, крім хіба що при друку повідомлень Для версій ядра, менших

20, не було параметра dev_id, тому параметр ir q використовувався, щоб розрізняти пристрої, які обслуговуються одним драйвером, і тому використовують один і той же обробник переривань (як приклад можна розглянути компютер з декількома контролерами жорсткого диска одного типу)

Другий параметр, dev_id, – це покажчик, що дорівнює значенню, яке було передано у функцію request_ir q () при реєстрації обробника переривання Якщо значення цього параметра є унікальним, що необхідно для підтримки спільно використовуваних переривань, то його можна використовувати як ідентифікатор для того, щоб відрізняти один від одного різні пристрої, які потенційно можуть використовувати один обробник У звязку з тим, що структура (контекст) пристрою (device structure) є як унікальної, так і, можливо, корисною при використанні в обробнику, зазвичай як параметра dev_id передають покажчик на цю структуру

Останній параметр, regs, – це покажчик на структуру, яка містить значення регістрів процесора і стан процесора, які були збережені перед початком обслуговування переривання Цей параметр використовується рідко, в основному

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

Значення, що повертається обробників переривань має спеціальний тип irqreturn_t Оброблювач може повертати два спеціальні значення: IRQ_NONE або IRQ_HANDLED Перше значення повертається, якщо обробник переривання виявив, що пристрій, який він обслуговує, не є джерелом переривання Друге значення повертається, якщо обробник викликаний правильно і пристрій, який він обслуговує, є джерелом переривання Крім цього, може бути використаний макрос IRQ_RETVAL (x) Якщо значення параметра х не дорівнює нулю, то макрос повертає значення IRQ_HANDLED, інакше повертається значення, рівне IRQ_NONE Ці спеціальні значення дозволяють дати ядру інформацію про те, генерує чи пристрій паразитні (необроблювані) переривання Якщо все обробники переривання, які обслуговують дану лінію, повертають значення IRQ_NONE, то ядро ​​може виявити проблему Зауважимо, що цей дивний тип значення, irqreturn_t, просто відповідає типу int Підстановка типу використовується для того, щоб забезпечити сумісність з більш ранніми версіями ядра, у яких не було подібної функції До серії ядер

26 обробник переривання мав повертається значення типу void У коді нових драйверів можна застосувати перевизначення типу typede f irqreturn_ t в тип void і драйвери можуть працювати з ядрами серії 24 без подальшої модифікації

Оброблювач переривань може позначатися як static, так як він ніколи не викликається безпосередньо в інших файлах коду

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

Реєнтерабельним і обробники переривань

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

Спільно використовувані обробники

Спільно використовувані (shared) обробники виконуються практично так само, як і не спільно використовувані Існує, однак, три головні відмінності

• Флат SA_SHIRQ повинен бути встановлений в параметрі flag s при виклику функції request_ir q ()

• Аргумент dev_id цієї ж функції повинен бути унікальним для кожного зареєстрованого обробника Достатнім є передача покажчика на структуру, яка описує пристрій Зазвичай так і надходять, оскільки структура контексту пристрою є унікальною для кожного пристрою, і, крім того, дані цієї структури потенційно можуть бути корисними при виконанні обробника Для спільно використовуваного обробника не можна присвоювати параметру dev_id значення NULL

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

Усі драйвери, які розраховані на спільно використовувану лінію переривання, повинні задовольняти зазначеним вище вимогам Якщо хоча б один з пристроїв, які спільно використовують лінію переривання, не чинить це коректно, то всі інші пристрої також не зможуть спільно використовувати лінію Якщо функція request_ir q () викликається із зазначенням прапора SH_SHIRQ, то цей виклик буде успішним тільки в тому випадку, якщо для даної лінії переривання ще немає зареєстрованих обробників або все обробники для даної лінії зареєстровані з зазначенням прапора SH_SHIRQ Зауважимо, що для ядер серії 26, на відміну від більш ранніх серій, можна змішувати спільно використовувані обробники з різними значеннями прапора SA_INTERRUPT

Коли ядро ​​отримує переривання, то воно послідовно викликає все обробники, зареєстровані для даної лінії Тому важливо, щоб обробник переривання був в змозі визначити, який пристрій є джерелом цього переривання Оброблювач повинен швидко завершитися, якщо відповідне йому Пристрій не генерувала це переривання Така умова вимагає, щоб апаратний пристрій мало регістр стану (status register) або інший аналогічний механізм, яким обробник може скористатися для перевірки Насправді більшість пристроїв дійсно мають дану функцію

Справжній обробник переривання

Давайте розглянемо справжній обробник переривання, який використовується в драйвері пристрою RTC (real-time clock, годинник реального часу), що знаходиться у файлі drivers / char / rtcс Пристрій RTC є в багатьох обчислювальних системах, включаючи персональні компютери (PC) Це окреме від системного таймера пристрій, який використовується для установки системних годин, для подачі сигналів таймера (alarm) або для реалізації генераторів періодичних сигналів (periodic timer) Встановлення системних годин зазвичай проводиться шляхом запису значень в спеціальний регістр або діапазон адрес (Номерів портів) введення-виведення (I / O range) Подача сигналів таймера або генератор періодичних сигналів звичайно реалізуються через переривання Переривання еквівалентно деякого сигналу таймера: воно генерується, коли закінчується період часу сигнального таймера При завантаженні драйвера пристрою RTC викликається функція rtcinit () для іні-

ціалізації драйвера Одна з її обовязків – це реєстрація обробника переривання Робиться це в такий спосіб

if (request_irq(RTC_IRQ, rtc_interrupt, SA_INTERRUPT, &quotrtc&quot, NULL) { printk(KERN_ERR &quotrtc: cannot register IRQ %d\n&quot, rtc_irq) return -EIO

}

З даного приклад а видно, що номі р лини і переривання я – це константа RTC_IRQ, значення якої визначається окремо для кожної апаратної платформи за допомогою препроцесора Наприклад, для персональних компютерів це значення завжди відповідає IRQ 8 Другий параметр-це обробник переривання, rt c interrupt, при виконанні якого заборонені всі переривання у звязку із зазначенням прапора SA_INTERRUPT З четвертого параметра можна укласти, що драйвер буде мати імя rtc Так як наш пристрій не може використовувати лінію переривання спільно з іншими пристроями і обробник переривання не використовується для будь-яких інших цілей, як параметр dev_i d передається значення NULL

І нарешті, власне сам обробник переривання

/*

* Дуже маленький обробник переривання Він виконується з

* Встановленим прапором SA_INTERRUPT, проте існує

* Можливість конфлікту з виконанням функції set_rtc_mmss ()

* (Обробник переривання rtc і обробник переривання системного

* Таймера можуть виконуватися одночасно на двох різних

* Процесорах) Отже, необхідно серіалізіровать доступ

* До мікросхеми за допомогою спін-блокування rtc_lock, що повинно

* Бути зроблено для всіх апаратних платформ в коді роботи з

* Таймером (Тіло функції set_rtc_mmss () шукайте в файлах

* /arch/XXXX/kernel/timec)

*/

static irqreturn_t rtc_interrupt(int irq, void *dev_id, struct pt_regs *regs)

/*

* Переривання може виявитися перериванням таймера, перериванням

* Завершення оновлення або періодичним перериванням

* Стан (причина) переривання зберігається в самому

* Молодшому байті, а загальна кількість переривання – в залишилася

* Частини змінної rtc_irq_data

*/

spin_lock (&amprtc_lock)

rtc_irq_data += 0x100

rtc_irq_data &amp= ~Oxff

rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) &amp OxFO)

if (rtc_status &amp RTC_TIMER_ON)

rnod_timer(&amprtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100)

spin_unlock(&amprtc_lock)

/*

* Тепер виконаємо інші дії

/*

spin_lock(&amprtc_task_lock)

if (rtc_callback)

rtc_callback-&gtfunc(rtc_callback-&gtprivate_data) spin_unlock(&amprtc_task_lock) wake_up_interruptible(&amprtc_wait)

kill_fasync(&amprtc_async_queue, SIGIO, POLL_IN)

return IRQ_HANDLED

}

Ця функція викликається щоразу, коли система отримує переривання від пристрою RTC Перш за все, слід звернути увагу на виклики функцій роботи зі спін-блокуваннями: перша група викликів гарантує, що до змінної rtc_irq_dat a НЕ БУДЕ конкурентних звернень іншими процесами на SMP-машині, а друга – захищає в аналогічній ситуації параметри структури rtc_callback Блокування обговорюються в главі 9, Засоби синхронізації в ядрі.

Мінлива rtc_ir q dat a містить інформацію про устройстпе RTC і оновлюється за допомогою функції modtime r () Про таймерах розповідається в главі 10, Таймери та управління часом.

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

Наприкінці функція обробки переривання повертає значення IRQ_HANDLED, щоб вказати, що переривання від даного пристрою оброблено правильно Так як цей обробник переривання не підтримує спільне використання ліній переривання і не існує механізму, за допомогою якого обробник переривань RTC може виявити вкладені запити на переривання, то цей обробник завжди повертає значення IRQ_HANDLED

Контекст переривання

При виконанні обробника переривання або обробника нижньої половини, ядро ​​знаходиться в контексті переривання Згадаймо, що контекст процесу – це режим, в якому працює ядро, виконуючи роботу від імені процесу, наприклад виконання системного пизова або потоку простору ядра У контексті процесу макрос curren t повертає покажчик на відповідне завдання Більше того, оскільки в контексті процесу процес повязаний з ядром, то контекст процесу може переходити в стан очікування або використовувати функції планувальника какимлибо іншим способом .

На противагу щойно розглянутому, контекст переривання не повязаний ні з одним процесом Макрос curren t в контексті переривання є незаконним (хоча він і вказує на процес, виконання якого був перерваний) Так як немає процесу, то контекст переривання не може переходити в стан очікування (sleep) – дійсно, яким чином можна перепланувати його виконання Тому деякі функції ядра не можуть бути викликані з контексту переривання Якщо функція може переводити процес в стан очікування, то її не можна викликати в обробнику переривання, що обмежує набір функцій, які можна використовувати в обробниках переривань

Контекст переривання є критичним до часу виконання, так як обробник переривання перериває виконання деякого програмного коду Код ж самого обробника повинен бути простий і швидкий Використання циклів перевірки стану чого-небудь (busy loop) украй небажано Це дуже важливий момент Завжди слід памятати, що обробник переривання перериває роботу деякого коду (можливо, навіть обробника іншої лінії запиту на переривання) У звязку зі своєю асинхронної природою обробники прерипаній повинні бути як можна більш швидкими і простими Максимально можливу частину роботи необхідно вилучити з обробника переривання і перекласти на обробник нижньої половини, який виконується в більш відповідний час

Можливість встановити стек контексту переривання є конфігурується Історично, обробник переривання не має свого стека Замість цього він повинен був використовувати стек ядра перерваного процесса1 Стек ядра має розмір дві сторінки памяті, що зазвичай відповідає 8 Кбайт для 32-розрядних апаратних платформ і 16 Кбайт для 64-розрядних платформ Так як в такому випадку обробники переривань спільно використовують стек, то вони повинні бути дуже економними щодо того, що вони в цьому стеці виділяють Звичайно, стек ядра спочатку є обмеженим, тому будь-який код ядра повинен брати це до уваги

У ранніх версіях ядер серії 26 була введена можливість обмежити розмір стека ядра від двох до однієї сторінки памяті, що дорівнює 4 Кбайт на 32-розрядних апаратних платформах Це зменшує витрати памяті, бо раніше кожен процес вимагав дві сторінки памяті ядра, яка не може бути витіснена на диск Щоб мати можливість працювати зі стеком зменшеного розміру, кожному оброблювачу переривання виділяється свій стек, окремий для кожного процесора Цей стек називається стеком переривання Хоча загальний розмір стека переривання і дорівнює половині від спочатку розміру спільно використовуваного стека, проте в результаті виходить, що сумарний розмір стека виходить більшим, тому що на кожен стек переривання виділяється ціла сторінка памяті

Обробник переривання не повинен залежати від того, які налаштування стека використовуються і чому дорівнює розмір стека ядра Завжди необхідно використовувати мінімальну кількість памяті в стеку

1как-небудь процес виконується завжди Якщо не виконується жодної процес, то виконується неодружена задача (idle task)

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

*

*