Введение в QNX/Neutrino 2. Руководство по программированию приложений реального времени в QNX Realtime Platform
Шрифт:
Происходящее по сути заключается в том, что вы вызываете переключение контекста для перехода к «Потоку 2», он опрашивает аппаратуру и понимает, что делать ничего не требуется, и это влечет за собой еще одно лишнее переключение контекста обратно в «Поток 1».
Вот что произошло бы, если бы вы применили функцию InterruptAttach, но не пожелали планировать другой поток (т.е. просто вернулись обратно):
Поток управления при использовании InterruptAttach
Ядро знает, что выполнялся «Поток 1», и что ISR не сказал ему что-либо сделать, поэтому после прерывания оно может смело вернуть управление «Потоку 1».
Для справки: вот что делает функция InterruptAttachEvent (это не реальный исходный текст, поскольку функция InterruptAttachEvent в действительности связывает с ядром структуру данных — она не реализована как отдельная вызываемая функция!):
Так какую функцию применять? От редко возникающих прерываний почти всегда можно отмахнуться применением InterruptAttachEvent. Поскольку прерывания будут происходить редко, даже лишние перепланирования потоков значительного воздействия на общую производительность системы не окажут. Единственный момент, когда это проявится — это если на данном прерывании будут «сидеть» еще и другие устройства; в этом случае, поскольку функция InterruptAttachEvent маскирует источник прерывания, то это прерывание останется заблокированным до тех пор, пока источник не будет демаскирован обратно. Если при этом первое устройство требует много времени на обслуживание, остальным придется все это время ждать демаскирования. По большому счету, это проблема аппаратной организации системы — не следует размещать медленные устройства на одной линии прерывания с быстрыми.
При выборе функции для более часто возникающих прерываний требуется учесть множество факторов:
• Ненужные прерывания — если их число будет существенным, лучше применять InterruptAttach и отфильтровывать их прямо в ISR, Возьмем, например, тот же случай с последовательным устройством. Поток может выдать команду: «Дай мне 64 байта». Если ISR запрограммирован с учетом того, что пока не будут приняты все 64 байта, ничего полезного не произойдет, все возникающие в процессе приема прерывания будут отфильтрованы. ISR возвратит событие только после окончания приема всех 64 байт.
• Время реакции — если ваши аппаратные
• Буферизация — если ваша аппаратура имеет встроенные средства буферизации, вы можете обойтись функцией InterruptAttachEvent и очередью из единственного события, как в случае с комбинацией SIGEV_INTR и InterruptWait. Этот метод позволяет прерываниям возникать с такой частотой, как им захочется» при этом позволяя вашему потоку выбирать значения из буфера с такой скоростью, с какой он сможет. Поскольку данные буферизуются на аппаратном уровне, никаких проблем со временем реакции на прерывание не будет.
Функции, которые может вызывать ISR
Следующий вопрос, за который следует взяться, — это список функций, которые может вызывать ISR.
Небольшое отступление. Исторически, причина основных затруднений при написании обработчиков прерываний заключалась (и в большинстве других операционных систем до сих пор заключается) в том, что ISR работают в особом окружении.
Одна из конкретных причин, усложняющих написание ISR, состоит в том, что с точки зрения ядра ISR на самом деле не является «полноправным» потоком. С позиции ядра это, если хотите, такой таинственный «аппаратный» поток. Это означает, что ISR не имеет права делать никаких манипуляций «на уровне потока» — таких как, например, обмен сообщениями, синхронизация, системные вызовы, дисковый ввод/вывод, и т.д.
Не усложняет ли это написание ISR? Конечно. И поэтому решение заключается в том, чтобы в самом теле обработчика выполнять минимум работы, а все остальное делать уже на уровне потока, где есть доступ ко всем сервисам.
Ваши цели при написании ISR должны заключаться в следующем:
• считать переменчивую (в оригинале было «transient» — прим. ред.) информацию;
• очистить источник прерывания;
• возможно, запланировать поток, который сделает реальную работу.
Такая «архитектура « держится на том, что QNX/Neutrino обеспечивает очень быстрые времена переключения контекста. Вы знаете, что сможете быстро переключиться в ваш обработчик для выполнения работы, критичной по времени. Вы также знаете, что когда обработчик возвратит событие для запуска потока, то поток тоже активизируется очень быстро. И именно эта философия «ничего не делайте в теле ISR» делает обработчики прерываний в QNX/Neutrino столь простыми!
Итак, какие же вызовы можно использовать в теле ISR? Вот официальный список:
• функции семейства atomic_* (например, atomic_set);
• функции семейства mem* (типа memcpy);
• большинство функций семейства str* (типа strcmp). Остерегайтесь, однако, потому что не все эти функции являются безопасными — например, strdup вызывает malloc, в которой используется мутекс, а это запрещено.
Вообще, что касательно строковых функций, перед их использованием надо индивидуально смотреть их описание в руководстве по Си-библиотеке;