Чтение онлайн

на главную - закладки

Жанры

Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform

Кёртен Роб

Шрифт:

break;

case IIR_RX:

/* Считать символ */

serial_rx = in8(base_reg + REG_RX);

break;

case IIR_LSR:

/* Запомнить регистр состояния линии */

serial_lsr = in8(base_reg + REG_LS);

break;

}

 }

 /* Сюда мы не доберемся */

 return (0);

}

Обратите

внимание, что функция InterruptAttachEvent возвращает идентификатор прерывания (небольшое целое число). Мы сохранили это значение в переменной intId, чтобы впоследствии смогли с его помощью демаскировать прерывание. После того как мы подключились к прерыванию, мы должны ждать его возникновения. Поскольку в данном случае мы применили функцию InterruptAttachEvent, при каждом возникновении прерывания мы будем получать соответствуют предварительно созданное событие. Сравните это с предыдущим случаем, где применялась InterruptAttach — там решение о том, посылать событие или нет, принимал сам обработчик. При использовании функции InterruptAttachEvent ядро понятия не имеет, было ли аппаратное прерывание «существенным» для нас или нет — оно просто каждый раз генерирует событие, затем маскирует соответствующее прерывание и предоставляет нам самим возможность решать, насколько это важно и что с этим делать.

В примере с InterruptAttach мы принимали решение путем возврата либо

struct sigevent
, чтобы указать, что что-то должно произойти, либо константы NULL. Обратите внимание на изменения в программе в варианте с InterruptAttachEvent:

• «Работа ISR» теперь выполняется потоком — в функции main.

• Теперь мы должны всегда демаскировать источник прерывания после получения нашего события (потому что ядро маскирует его для нас).

• Если прерывание для нас является несущественным, мы не будем ничего предпринимать и просто пойдем дальше по программному циклу в ожидании другого прерывания.

• Если прерывание существенно для нас, мы непосредственно обрабатываем его (см. блок операторов

case IIR_MSR
).

Где в программе очищать источник прерывания — это зависит от ваших аппаратных средств и от выбранной схемы уведомления. При использовании SIGEV_INTR в сочетании с InterruptWait ядро не ставит в очередь более одного уведомления; при использовании же SIGEV_PULSE в сочетании с MsgReceive поставлены в очередь будут все. Если вы используете сигналы (например, в сочетании с SIGEV_SIGNAL), вы сами определяете, ставить их в очередь или нет. Одни аппаратные средства требуют очистки источника прерываний до начала чтения данных, другие — нет, и можно читать данные из них при выставленном сигнале прерывания.

Такой сценарий как ISR, возвращающий SIGEV_THREAD, повергает меня в дикий ужас. Настоятельно рекомендую по возможности избегать этого «приема».

В вышеприведенном примере с программированием последовательного порта мы приняли решение использовать функцию InterruptWait, которая ставит в очередь только одно событие. Аппаратура последовательного порта может выставить еще одно прерывание сразу после чтения нами регистра идентификации прерываний (IIR), но это нормально, потому что в очередь будет поставлен максимум один SIGEV_INTR. Мы подберем это уведомление на следующей итерации цикла

for
.

Различия между InterruptAttach и InterruptAttachEvent

Напрашивается

естественный вопрос: «Когда и какую функцию выбирать?»

Наиболее очевидное преимущество InterruptAttachEvent состоит в том, что применять ее значительно проще, чем InterruptAttach — поскольку нет никакого ISR, его и отлаживать не надо. Другое преимущество состоит в том, что поскольку в пространстве ядра ничего не выполняется (a ISR делал бы именно так), нет никакой опасности разрушить систему — если вы столкнетесь с ошибкой программирования, то пострадает конкретный процесс, а не система в целом. Однако, применение этой функции будет более или менее эффективным по сравнению с InterruptAttach в зависимости от того, чего вы пытаетесь достичь. Этот вопрос достаточно сложен, и сводить его к нескольким словам (типа «быстрее» или «лучше») было бы неправильно. Давайте рассмотрим несколько возможных сценариев.

Вот что происходит, когда мы используем InterruptAttach:

Поток управления при использовании InterruptAttach.

Выполняющийся поток («Поток 1») прерывается, и мы переключаемся в ядро. Ядро сохраняет контекст «Потока 1». Затем ядро смотрит, кто ответственен за обработку данного прерывания и решает, что это «ISR». Ядро настраивает контекст для «ISR» и передает ему управление. «ISR» опрашивает аппаратуру и решает возвратить

struct sigevent
. Ядро отмечает возвращаемое событие, выясняет, кто должен его обработать, и переводит их в состояние READY. Это может привести к планированию ядром другого потока, «Потока 2».

Теперь давайте сопоставим это с тем, что будет происходить при использовании InterruptAttachEvent:

Поток управления при использовании InterruptAttachEvent.

В этом случае путь обслуживания прерываний намного короче. Мы выполнили одно переключение контекста от выполнявшегося потока («Поток 1») в ядро. Вместо второго переключения контекста в ISR ядро просто «притворилось», что получило от ISR

struct sigevent
и среагировало на него, запланировав «Поток 2».

Теперь вы думаете: «Великолепно! Забудем про InterruptAttach и будем использовать простую функцию InterruptAttachEvent

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

В этом случае, особенно если бы последовательный порт принимал символы (которые вы хотели бы проигнорировать), вы бы потратили много времени впустую на перепланирование своего потока — и только ради того, чтобы он проанализировал состояние последовательного порта и принял решение, что ничего делать не надо. В данном случае все бы выглядело примерно так:

Поток управления при использовании InterruptAttachEvent с излишним перепланированием.

Поделиться:
Популярные книги

Я снова не князь! Книга XVII

Дрейк Сириус
17. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я снова не князь! Книга XVII

На границе империй. Том 10. Часть 2

INDIGO
Вселенная EVE Online
Фантастика:
космическая фантастика
5.00
рейтинг книги
На границе империй. Том 10. Часть 2

Маяк надежды

Кас Маркус
5. Артефактор
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Маяк надежды

Законы Рода. Том 3

Flow Ascold
3. Граф Берестьев
Фантастика:
фэнтези
аниме
5.00
рейтинг книги
Законы Рода. Том 3

Бастард Императора. Том 8

Орлов Андрей Юрьевич
8. Бастард Императора
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Бастард Императора. Том 8

Первый среди равных. Книга III

Бор Жорж
3. Первый среди Равных
Фантастика:
попаданцы
аниме
фэнтези
6.00
рейтинг книги
Первый среди равных. Книга III

Восход. Солнцев. Книга I

Скабер Артемий
1. Голос Бога
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Восход. Солнцев. Книга I

На границе империй. Том 7. Часть 5

INDIGO
11. Фортуна дама переменчивая
Фантастика:
боевая фантастика
космическая фантастика
попаданцы
5.00
рейтинг книги
На границе империй. Том 7. Часть 5

Real-Rpg. Еретик

Жгулёв Пётр Николаевич
2. Real-Rpg
Фантастика:
фэнтези
8.19
рейтинг книги
Real-Rpg. Еретик

Отмороженный 3.0

Гарцевич Евгений Александрович
3. Отмороженный
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Отмороженный 3.0

Газлайтер. Том 3

Володин Григорий
3. История Телепата
Фантастика:
попаданцы
альтернативная история
аниме
5.00
рейтинг книги
Газлайтер. Том 3

СД. Том 13

Клеванский Кирилл Сергеевич
13. Сердце дракона
Фантастика:
фэнтези
6.55
рейтинг книги
СД. Том 13

Хорошая девочка

Кистяева Марина
Любовные романы:
современные любовные романы
эро литература
5.00
рейтинг книги
Хорошая девочка

Вторая жизнь майора. Цикл

Сухинин Владимир Александрович
Вторая жизнь майора
Фантастика:
героическая фантастика
боевая фантастика
попаданцы
5.00
рейтинг книги
Вторая жизнь майора. Цикл