Перейти к содержанию

Отложенная обработка прерываний (deferred interrupt processing)

Обработчик прерывания выполняется в обход всех задач и мешает другим прерываниям. Поэтому в нём делают только то, что нельзя отложить: забирают данные и сбрасывают флаг. Всё остальное передают задаче, которую обработчик будит. Такой приём называют отложенной обработкой прерываний.

Обновлено Редакция GAW

Отложенная обработка прерываний (deferred interrupt processing) — приём, при котором обработчик прерывания (ISR) делает только неотложное — фиксирует причину, забирает данные, сбрасывает флаг — и будит задачу, а основная обработка выполняется в этой задаче. Это основной способ работы с прерываниями в программах на RTOS: в задаче доступен весь API системы, а её приоритет можно выбрать относительно остальных задач.

Зачем держать обработчик коротким

Руководство FreeRTOS перечисляет причины:

  • задачи, даже самые приоритетные, выполняются только тогда, когда не обслуживается ни одно прерывание;
  • обработчики вносят дрожание во время старта и выполнения задач;
  • на ряде архитектур во время обработчика новые прерывания не принимаются, и растёт задержка прерываний;
  • данные, общие для обработчика и задач, приходится защищать;
  • вложенные прерывания усложняют систему, а чем короче обработчик, тем реже вложенность.

Как устроено

ШагГдеЧто
1обработчикзафиксировать причину, забрать данные, сбросить флаг
2обработчикразбудить задачу: семафор, очередь или уведомление
3обработчикесли разбуженная задача приоритетнее текущей — portYIELD_FROM_ISR()
4задачавыполнить основную обработку с полным API RTOS

Если приоритет задачи выше всех остальных, она начинает работать сразу после выхода из обработчика, и обработка идёт без разрыва, как будто внутри него. На Cortex-M переключение на неё выполняет отложенное исключение PendSV. Самый быстрый способ разбудить задачу во FreeRTOS — уведомление задачи; семафор и очередь удобнее, когда событий несколько или нужно передать данные.

Когда откладывать

FreeRTOS советует откладывать обработку, если она нетривиальна, требует действий, недоступных в обработчике (вывод в консоль, выделение памяти), или её длительность заранее неизвестна. Сохранить результат АЦП можно прямо в обработчике, а прогнать его через программный фильтр — уже в задаче. Без RTOS тот же принцип реализуют флагом и кольцевым буфером, которые обработчик заполняет, а основной цикл разбирает; подробнее — в статье «Прерывания и общие данные».

Частые вопросы

Что обязательно делать в самом обработчике прерывания?

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

Не будет ли обработка в задаче медленнее?

Если задаче, в которую отложена обработка, дать приоритет выше всех остальных и в конце обработчика вызвать portYIELD_FROM_ISR(), она начнёт работать сразу после выхода из прерывания. Руководство FreeRTOS отмечает, что обработка тогда идёт так же непрерывно, как если бы она была внутри обработчика, но прерывания при этом не заблокированы.

Когда откладывать обработку не нужно?

Когда она короткая и предсказуемая. FreeRTOS приводит пример: если прерывание только сохраняет результат АЦП, это почти наверняка лучше сделать прямо в обработчике. А если результат ещё надо пропустить через программный фильтр, фильтр уместнее выполнять в задаче.

Источники

  1. R. Barry, FreeRTOS Team. Mastering the FreeRTOS Real Time Kernel — A Hands-On Tutorial Guide, Release 1.1.0 (Amazon Web Services, 2023) — разд. 7.3: ISR держат как можно короче — задачи выполняются, только когда не обслуживается ни одно прерывание; ISR вносят дрожание во время старта и выполнения задач; на части архитектур во время ISR новые прерывания не принимаются; общие ресурсы требуют защиты; вложенность усложняет систему; ISR должен зафиксировать причину и сбросить прерывание, остальное можно выполнить в задаче — это отложенная обработка; если приоритет такой задачи выше остальных, обработка идёт сразу после ISR, как будто внутри него; откладывать полезно, если обработка нетривиальна, требует действий, недоступных в ISR (вывод в консоль, выделение памяти), или её длительность заранее неизвестна; разд. 7.2: API-функции с суффиксом FromISR, portYIELD_FROM_ISR(); 7.4 — двоичный семафор; 7.6 — отложенная работа в задаче-демоне таймеров; уведомление задачи — самый эффективный способ разбудить её из ISR
  2. FreeRTOS Kernel V11.1.0, include/semphr.h (GitHub FreeRTOS/FreeRTOS-Kernel) — xSemaphoreGive() нельзя вызывать из прерывания — для этого xSemaphoreGiveFromISR(); параметр pxHigherPriorityTaskWoken устанавливается в pdTRUE, если отдача семафора разблокировала задачу с приоритетом выше текущей, и тогда перед выходом из прерывания нужно запросить переключение контекста

Нашли ошибку?