Уведомление задачи (task notification)
Прерывание приняло пакет и должно разбудить задачу-обработчик. Классический путь — бинарный семафор, но у FreeRTOS есть более короткий: уведомление отправляется прямо задаче, без промежуточного объекта ядра.
Обновлено Редакция GAW
Уведомление задачи (direct to task notification) — событие FreeRTOS, которое отправляется прямо задаче, а не через промежуточный объект ядра: очередь, семафор или группу событий. У каждой задачи есть массив уведомлений; каждое уведомление хранит состояние («ожидает» или «не ожидает») и 32-битное значение. Задача может заблокироваться в ожидании уведомления так же, как ждала бы семафор, и не тратит при этом процессорное время. Механизм появился в FreeRTOS V8.2.0, массив вместо одного значения — в V10.4.0.
Что умеет уведомление
Отправитель не только переводит уведомление в состояние «ожидает», но и может изменить его значение одним из способов:
| Действие | Чему соответствует |
|---|---|
| увеличить значение на 1 | бинарный или счётный семафор |
| установить биты | лёгкая группа событий |
| перезаписать значение | почтовый ящик (mailbox), очередь длиной 1 |
| перезаписать, только если прошлое значение прочитано | очередь длиной 1 без потери данных |
Для варианта «семафор» есть макросы xTaskNotifyGive() (увеличивает значение, из прерывания — vTaskNotifyGiveFromISR()) и ulTaskNotifyTake() (ждёт, пока значение станет ненулевым). Второй параметр ulTaskNotifyTake() решает, что делать со значением на выходе: pdTRUE сбрасывает его в 0 (бинарный семафор), pdFALSE уменьшает на 1 (счётный). Время ожидания задаётся в тиках, миллисекунды переводит pdMS_TO_TICKS(). Для остальных вариантов служат xTaskNotify() и xTaskNotifyWait(); чтение через xTaskNotifyWait() сбрасывает состояние в «не ожидает».
Цена и ограничения
По измерениям разработчиков FreeRTOS, разблокировка задачи уведомлением на 45 % быстрее, чем бинарным семафором (сравнение с семафором V8.1.2, GCC -O2, без configASSERT; против семафора V8.2.0 и новее выигрыш 35 %), и требует меньше ОЗУ. За это приходится платить двумя ограничениями: событие может принять только одна задача, а при замене очереди отправитель не может ждать в заблокированном состоянии, если отправка не проходит сразу. Для типового случая — прерывание будит задачу-обработчик — оба ограничения не мешают; это основной приём отложенной обработки прерываний, который сокращает время в обработчике и задержку прерывания для остальных.
Конфигурация
Уведомления включены по умолчанию: configUSE_TASK_NOTIFICATIONS равно 1, если не определено; при 0 код исключается из сборки и экономится 5 байт на элемент массива на задачу. Размер массива задаёт configTASK_NOTIFICATION_ARRAY_ENTRIES (по умолчанию и в шаблоне FreeRTOSConfig.h — 1). Функции без суффикса Indexed работают с индексом 0: xTaskNotifyGive(task) эквивалентно xTaskNotifyGiveIndexed(task, 0). Индекс 0 занимают потоковые буферы и message-буферы, поэтому задаче, которая читает такой буфер, для собственных уведомлений нужен индекс больше 0. Как уведомления сочетаются с критическими секциями и приоритетами прерываний, разобрано в статье «Прерывания и параллельность».
Частые вопросы
Чем уведомление задачи быстрее семафора?
Семафор — отдельный объект ядра со своим списком ожидающих задач, а уведомление хранится прямо в блоке управления задачей: состояние и 32-битное значение. По измерениям разработчиков FreeRTOS разблокировка задачи уведомлением на 45 % быстрее, чем бинарным семафором версии V8.1.2 (GCC, -O2, без configASSERT), и на 35 % быстрее улучшенного семафора из V8.2.0 и новее; ОЗУ тоже нужно меньше. Платить приходится ограничением: получатель только один, а при замене очереди отправитель не может ждать в блокировке, если отправка невозможна сразу.
В чём разница между xClearCountOnExit = pdTRUE и pdFALSE в ulTaskNotifyTake()?
При pdTRUE значение уведомления после возврата сбрасывается в 0 — это поведение бинарного семафора: сколько бы раз прерывание ни «дало» уведомление, задача обработает их за один проход и должна сама вычерпать периферию. При pdFALSE значение уменьшается на 1 — это счётный семафор: каждое xTaskNotifyGive() будет обработано отдельным проходом цикла. Функция возвращает значение, которое было до сброса или декремента.
Сколько уведомлений может быть у одной задачи?
Столько, сколько задано в configTASK_NOTIFICATION_ARRAY_ENTRIES; по умолчанию и в шаблоне конфигурации — 1. Массив появился в FreeRTOS V10.4.0, до этого у задачи было одно значение. Функции без суффикса Indexed работают с индексом 0, и этот же индекс используют потоковые и message-буферы, поэтому для собственных уведомлений в задаче, которая читает такой буфер, берите индекс больше 0. Задача может ждать только одно уведомление за раз.
Источники
- FreeRTOS. FreeRTOS Kernel documentation: Direct to task notifications (Kernel features), 2026 — разд. Description: доступно с FreeRTOS V8.2.0, несколько уведомлений на задачу — с V10.4.0; у каждой задачи массив уведомлений, у каждого — состояние pending/not pending и 32-битное значение; число элементов задаёт configTASK_NOTIFICATION_ARRAY_ENTRIES; отправка может перезаписать значение (всегда или только если прочитано), установить биты или увеличить на 1; xTaskNotifyWait() сбрасывает состояние, xTaskNotifyStateClear() — явно; задача ждёт только одно уведомление из массива; configUSE_TASK_NOTIFICATIONS = 0 экономит 5 байт на элемент массива на задачу; потоковые и message-буферы занимают индекс 0; разд. Performance Benefits and Usage Restrictions: разблокировка задачи уведомлением на 45 % быстрее и требует меньше ОЗУ, чем бинарным семафором (измерено против семафора V8.1.2, GCC -O2, без configASSERT; против семафора V8.2.0 и новее — на 35 %); ограничения: только одна задача-получатель; при замене очереди отправитель не может блокироваться в ожидании отправки; стр. API xTaskNotifyGive и ulTaskNotifyTake (Backward compatibility): функции без суффикса Indexed всегда работают с индексом 0, xTaskNotifyGive(task) эквивалентно xTaskNotifyGiveIndexed(task, 0)
- FreeRTOS. FreeRTOS Kernel API reference: ulTaskNotifyTake / ulTaskNotifyTakeIndexed, 2026 — макрос для использования уведомления как более быстрого и лёгкого бинарного или счётного семафора, аналог xSemaphoreTake(); xClearCountOnExit = pdTRUE — значение сбрасывается в 0 (бинарный семафор), pdFALSE — уменьшается на 1 (счётный); возвращает значение до сброса; возвращается, когда значение не ноль; xTicksToWait — время ожидания в тиках, pdMS_TO_TICKS() переводит миллисекунды; в состоянии Blocked задача не тратит процессорное время; отправлять нужно через xTaskNotifyGive() или xTaskNotify() с eIncrement; из прерывания — vTaskNotifyGiveFromISR() с pxHigherPriorityTaskWoken и portYIELD_FROM_ISR(); xTaskNotifyGive() из ISR вызывать нельзя (страница xTaskNotifyGive)
- FreeRTOS. FreeRTOS-Kernel V11.3.3: examples/template_configuration/FreeRTOSConfig.h и include/FreeRTOS.h (GitHub, MIT) — шаблон: configTASK_NOTIFICATION_ARRAY_ENTRIES 1, configUSE_TASK_NOTIFICATIONS 1; FreeRTOS.h: при отсутствии определения configUSE_TASK_NOTIFICATIONS = 1 и configTASK_NOTIFICATION_ARRAY_ENTRIES = 1
Нашли ошибку? ·
Смотрите также
- Семафор (двоичный и счётный)Объект RTOS для сигнала о событиях и учёта свободных ресурсов
- Очередь RTOSПотокобезопасный FIFO для обмена данными между задачами и прерываниями в RTOS
- Группа событий (event group)Набор битов-флагов FreeRTOS, на любой или все из которых задача может ждать в блокировке
- Потоковый буфер (stream buffer)Примитив FreeRTOS для потока байтов между одним писателем и одним читателем с ожиданием по уровню
- Отложенная обработка прерыванийКороткий обработчик прерывания и основная работа в задаче, которую он будит
- Задержка прерыванияВремя от запроса до обработчика: Cortex-M3 и AVR
- Прерывания и общие данныеISR на AVR и Cortex-M, атомарность, критические секции, кольцевой буфер