Управління переривань

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

і У табл 62 наведено повний список цих інтерфейсів

Причини, за якими необхідно управляти системою обробки переривань, в основному, зводяться до необхідності забезпечення синхронізації Шляхом заборони переривань можна гарантувати, що обробник переривання не витисне поточний виконуваний код Більше того, заборона переривань також забороняє і витіснення коду ядра Проте ні заборона доставки переривань, ні заборона преемптівності ядра не дають ніякого захисту від конкурентного звернення інших процесорів Так як операційна система Linux підтримує багатопроцесорні системи, в більшості випадків код ядра повинен захопити деяку блокування, щоб запобігти доступ іншого процесора до спільно використовуваних даних Ці блокування зазвичай захоплюються в комбінації з забороною переривань на поточному процесорі Блокування надає захист від доступу іншого процесора, а заборона переривань забезпечує захист від конкурентного доступу з можливого обробника переривання У розділах 8 і 9 обговорюються різні аспекти проблем синхронізації і вирішення цих проблем

Проте розуміння інтерфейсів ядра для управління переривань є важливим

2 Після прочитання я глави 10, Таймери та управління часом, чи можна сказати, скільки часу

(В одиницях HZ) машина працювала без перевантаження виходячи з числа переривань й таймера

Заборона і дозвіл переривань

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

local_irq_disable()

/ * Переривання заборонені . * /

local_irq_enable()

Ці функції зазвичай реалізуються у вигляді однієї інструкції мовою асемблера (що, звичайно, залежить від апаратної платформи) Для платформи х86 функція local_irq_disabl e () – це просто машинна інструкція cli, а функція 1оса1_ irq_enabl e () – просто інструкція sti Для хакерів, не знайомих з платформою х86, st i і cl i – це асемблерні виклики, які відповідно дозволяють встановити (set) або очистити (clear) прапор дозволу переривань(allow interrupt flag) Іншими словами, вони дозволяють або забороняють доставку переривань на що викликала їх процесорі

Функція local_irq_disabl e () є небезпечною у разі, коли перед її викликом переривання вже були заборонені При цьому відповідний їй виклик функції local_irq_enabl e () дозволить переривання незалежно від того, були вони заборонені спочатку (до виклику local_irq_disabl e ()) чи ні Для того щоб уникнути такої ситуації, необхідний механізм, який дозволяє відновлювати стан системи обробки перериванні в первинне значення Ця вимога має загальний характер, тому що деякий ділянку коду ядра в одному випадку може виконуватися при дозволених переривання, а в іншому випадку-прі заборонених, залежно від послідовності викликів функцій Наприклад, нехай показаний фрагмент коду є частиною функції Ця функція викликається двома іншими функціями, і в першому випадку перед викликом переривання забороняються, а в другому – ні Так як при збільшенні обсягу коду ядра стає складно відстежувати всі можливі варіанти виклику функції, значно безпечніше зберігати стан системи переривань перед тим, як забороняти переривання Замість дозволу переривань просто відновлюється первинний стан системи обробки переривань таким чином

unsigned long flags

local_irq_save(flags)

/ * Переривання заборонені * /

local_irq_restore(flags)

/ * Стан системи переривань відновлено в первісне значення . * /

Потрібно зауважити, що ці функції є макросами, тому передача параметра flag s виглядає як передача за значенням Цей параметр містить залежні від апаратної платформи дані, які в свою чергу містять стан системи переривань Так як, принаймні для однієї апаратної платформи (SPARC), в цій неременной зберігається інформація про стек, то параметр flag sне можнапередавати в інші функції (іншими словами, він повинен залишатися в одному і тому ж стекового фреймі) З цієї причини виклики функцій збереження і відновлення повинні виконуватися в тілі однієї функції

Всі описані функції можуть викликатися як з обробника переривань, так і з контексту процесу

Більше немає глобального виклику cli ()

Раніше ядро ​​надавало функцію, за допомогою якої можна було заборонити переривання на всіх процесорах системи Більш того, якщо який-небудь процесор викликав цю функцію, то він повинен був чекати, поки переривання не будуть вирішені Ця функція називалася cli (), а відповідна їй роздільна функція – sti () дуже х86-центрично (Хоча і було доступно для всіх апаратних платформ) Ці інтерфейси були вилучені під час розробки ядер серії 25, і, отже, всі операції по синхронізації обробки переривань повинні використовувати комбінацію функцій з управління локальними перериваннями і функцій роботи зі спін-блокуваннями (обговорюються в главі 9, Засоби синхронізації в ядрі) Це означає, що код, який раніше мав всього лише глобально заборонити переривання для того, щоб отримати монопольний доступ до спільно використовуваних даних, тепер повинен виконати кілька більше роботи

Раніше розробники драйверів могли вважати, що якщо в їх обробнику переривання і в будь-якому іншому коді, який має доступ до спільно використовуваних даних, викликається функція cli (), то це дозволяє отримати монопольний доступ Функція cli () дозволяла гарантувати, що жоден з обробників переривань (у тому числі й інші екземпляри поточного обробника) не виконується Більш того, якщо будь-який інший процесор входить в ділянку коду, захищений за допомогою функції cl i (), то він не продовжить роботу, поки перший процесор не вийде з ділянки коду, захищеного за допомогою функції cli (), тобто не викличе функцію

st i ()

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

Заборона певної лінії переривання

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

void disable_irq(unsigned int irq)

void disable_irq_nosync(unsigned int irq)

void enable_irq(unsigned int irq)

void synchronize_irq(unsigned int irq)

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

лiнiї не доставлятимуться, але і те, що все виконуються обробники закінчили роботу Функція disable_irq_nosyn c () не має останнього властивості

Функція synchronize_ir q () буде очікувати, поки не завершиться вказаний обробник, якщо він, звичайно, виконується

Виклики цих функцій повинні бути згруповані, тобто кожним викликом функції disable_ir q () або disable_irq_nosyn c () повинен відповідати виклик функції enable_ir q () Тільки після останнього виклику функції enable_ir q () лінія запиту на переривання буде знову дозволена Наприклад, якщо функція disable_ir q () послідовно викликана два рази, то лінія запиту на переривання не буде дозволена, поки функція enable_ir q () теж не буде викликана два рази

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

Було б також поганим тоном забороняти лінію переривання, яка спільно використовується кількома обработчиками Заборона лінії переривання забороняє доставку переривань для всіх пристроїв, які використовують цю лінію Тому в драйверах нових пристроїв не рекомендується використовувати ці інтерфейси3 Так як пристрої PCI повинні згідно специфікації підтримувати спільне використання ліній переривань, вони взагалі не повинні використовувати ці інтерфейси Тому функція disable_ir q () і дружні їй зазвичай використовуються для застарілих пристроїв, таких як паралельний порт персонального компютера

Стан системи обробки переривань

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

Макрос irq_disable d (), який визначений у файлі , повертає нульове значення, якщо обробка переривань на локальному процесорі заборонена В іншому випадку повертається нуль Два наступних макросу дозволяють

визначити контекст, в якому в даний момент виконується ядро

in_interrupt()

in_irq()

Найбільш корисний з них – це перший макрос Він повертає нульове значення, якщо ядро ​​виконується в контексті переривання Це включає виконання як обробника переривання, так і обробника нижньої половини Макрос in_irq () повертає ненульове значення, тільки якщо ядро ​​виконує обробник переривання

3Многіе старі пристрої, зокрема пристрої ISA, не надають можливості визначити, чи є вони джерелом переривання Через це лінії переривання для ISA-пристроїв часто вже не можуть бути спільно використовуваними Оскільки специфікація шини PCI вимагає обовязкової підтримки спільно використовуваних переривань, сучасні пристрої PCI підтримують спільне використання переривань У сучасних компютерах практично всі лінії переривань можуть бути спільно використовуваними

Найбільш часто необхідно перевіряти, чи виконується код в контексті процесу, тобто необхідно перевірити, що код виконується НЕ в контексті переривання Це потрібно досить часто, коли кодом необхідно виконувати щось, що може бути виконано тільки з контексту процесу, наприклад перехід в призупинене стан Якщо макрос in_interrup t () повертає нульове значення, то ядро ​​виконується в контексті процесу

Таблиця 62 Список функцій управління переривань

Функція Опис

local_irq_disable() local_irq_enable() local_irq_save(unsigned long  flags)

local_irq_restore(unsigned   long flags)

disable_irq(unsigned  int  irq)

disable_irq_nosync(unsigned int irq) enable_irq(unsigned  int  irq) irqs_disabled()

in_interrupt()

in_irq()

Заборонити доставку переривань на локальному процесорі

Дозволити доставку переривань на локальному процесорі

Зберегти поточний стан системи обробки переривань на локальному процесорі і заборонити переривання

Відновити вказаний стан системи переривань на локальному процесорі

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

Заборонити зазначену лінію переривання

Дозволити зазначену лінію переривань

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

Повернути ненульове значення, якщо виконання провадиться в контексті переривання, і нуль – якщо в контексті процесу

Повернути ненульове значення, якщо виконання провадиться в контексті переривання, і нуль – в іншому випадку

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

*

*