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

Задача простоя (idle task)

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

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

Задача простоя (idle task) — задача, которую планировщик FreeRTOS создаёт сам при запуске, чтобы в системе всегда была хотя бы одна готовая к выполнению задача. Её приоритет — наименьший возможный, tskIDLE_PRIORITY, равный 0, поэтому она получает процессор только тогда, когда все прикладные задачи заблокированы или приостановлены. Во FreeRTOS у неё две обязанности: освобождать память, выделенную ядром удалённым задачам, и вызывать хук простоя.

Освобождение памяти

Когда задача удаляется через vTaskDelete(), её блок управления и стек возвращаются в кучу не сразу, а задачей простоя. Поэтому в приложении, которое удаляет задачи, задачу простоя нельзя лишать процессорного времени: если какая-то задача крутится без блокировок, память удалённых задач не освобождается. Во всех остальных случаях голодание задачи простоя безвредно — других активных функций у неё нет. Прикладные задачи могут делить с ней приоритет 0; тогда при configIDLE_SHOULD_YIELD = 1 (значение по умолчанию и в шаблоне конфигурации) задача простоя сразу уступает им процессор, а при 0 — использует весь свой квант времени.

Хук простоя

Хук простоя (idle hook) — функция void vApplicationIdleHook(void), которую задача простоя вызывает в каждом своём цикле, если в FreeRTOSConfig.h задано configUSE_IDLE_HOOK = 1 (в шаблоне — 0). Правила:

  • хук не должен блокироваться: никаких vTaskDelay(), ожиданий очереди или семафора с таймаутом — иначе в системе не останется готовой задачи;
  • если приложение использует vTaskDelete(), хук должен периодически возвращаться, чтобы задача простоя успевала чистить память;
  • типичное применение — перевести процессор в режим пониженного потребления: раз хук работает, значит, делать больше нечего.

Альтернатива хуку — отдельная задача с приоритетом tskIDLE_PRIORITY: гибче, но дороже по ОЗУ. Сон в хуке ограничен периодическим тиком, который каждый раз будит процессор; снять это ограничение позволяет бестактовый режим.

В ESP-IDF: по задаче на ядро

На двухъядерных ESP32 IDF FreeRTOS создаёт отдельную закреплённую задачу простоя для каждого ядра с теми же обязанностями. Стандартный хук vApplicationIdleHook() включается опцией CONFIG_FREERTOS_USE_IDLE_HOOK, но он один на систему и одинаков для обоих ядер. Поэтому ESP-IDF добавляет свои хуки: esp_register_freertos_idle_hook() и esp_register_freertos_idle_hook_for_cpu() регистрируют их во время работы, до 8 хуков каждого типа на ядро, причём на разных ядрах наборы могут отличаться. Запрет на блокирующие вызовы сохраняется.

Задачи простоя в ESP-IDF — ещё и «сердцебиение» для сторожевого таймера задач: по умолчанию он наблюдает именно за ними, и если какая-то задача долго не уступает процессор, задача простоя не успевает отметиться, и срабатывает TWDT. Как строить защиту сторожевыми таймерами в целом, разобрано в статье «Стратегии сторожевого таймера». Задача программных таймеров, в отличие от задачи простоя, работает с настраиваемым приоритетом (в шаблоне конфигурации — максимальным) и к простою отношения не имеет.

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

Можно ли вызывать vTaskDelay() в хуке простоя?

Нет. В системе всегда должна быть хотя бы одна готовая к выполнению задача, а блокировка задачи простоя это правило нарушит. Документация FreeRTOS запрещает в vApplicationIdleHook() любые вызовы, которые могут заблокировать: vTaskDelay(), ожидание очереди или семафора с ненулевым таймаутом. Нужна задержка — создайте отдельную задачу с приоритетом tskIDLE_PRIORITY.

Почему задачу простоя нельзя «голодом морить»?

Потому что именно она освобождает память, которую ядро выделило задачам, удалённым через vTaskDelete(). Если высокоприоритетная задача никогда не блокируется, задача простоя не получает процессор, и память удалённых задач не возвращается в кучу. По той же причине хук простоя должен периодически возвращаться, а не крутиться в бесконечном цикле. В ESP-IDF за этим следит Task Watchdog: по умолчанию он наблюдает за задачами простоя каждого ядра.

Сколько задач простоя в ESP-IDF?

По одной на ядро, и каждая закреплена за своим ядром; обязанности те же, что у единственной задачи простоя обычного FreeRTOS. Стандартный хук vApplicationIdleHook() при этом один и вызывается на обоих ядрах, поэтому ESP-IDF добавляет свои: esp_register_freertos_idle_hook_for_cpu() регистрирует до 8 хуков на ядро во время работы, и на разных ядрах они могут быть разными.

Источники

  1. FreeRTOS. FreeRTOS Kernel documentation: The idle task and Hook functions (Kernel features), 2026 — стр. Idle task: создаётся автоматически при запуске планировщика, чтобы всегда была хотя бы одна готовая задача; имеет наименьший приоритет и не отнимает время у прикладных задач; освобождает память, выделенную ядром удалённым задачам, поэтому при использовании vTaskDelete() её нельзя лишать процессорного времени; прикладные задачи могут делить приоритет tskIDLE_PRIORITY, поведение задаёт configIDLE_SHOULD_YIELD; хук простоя вызывается в каждом цикле задачи простоя; два способа выполнять код на приоритете простоя — хук или отдельная задача (гибче, но больше ОЗУ); хук не должен вызывать блокирующие функции (vTaskDelay(), очередь или семафор с временем ожидания); configUSE_IDLE_HOOK = 1, прототип void vApplicationIdleHook( void ); обычно хук переводит процессор в режим энергосбережения; стр. Hook functions: хук вызывается многократно, пока работает задача простоя, при использовании vTaskDelete() хук должен периодически возвращаться
  2. FreeRTOS. FreeRTOS-Kernel V11.3.3: include/task.h, examples/template_configuration/FreeRTOSConfig.h, include/FreeRTOS.h (GitHub, MIT) — task.h: #define tskIDLE_PRIORITY ( ( UBaseType_t ) 0U ); шаблон: configUSE_IDLE_HOOK 0, configUSE_TICK_HOOK 0, configIDLE_SHOULD_YIELD 1 — задача простоя уступает прикладной задаче с приоритетом 0, при 0 использует весь свой квант; FreeRTOS.h: configIDLE_SHOULD_YIELD равно 1, если не определено
  3. Espressif. ESP-IDF Programming Guide v6.1, ESP32: FreeRTOS (IDF); FreeRTOS (Supplemental Features); Watchdogs — разд. Idle Tasks: Vanilla FreeRTOS неявно создаёт задачу простоя с приоритетом 0 при старте планировщика, она освобождает память удалённых задач и выполняет хук простоя; в IDF FreeRTOS на каждое ядро создаётся отдельная закреплённая задача простоя с теми же обязанностями; стр. Supplemental Features, разд. Tick and Idle Hooks: хук FreeRTOS включается опцией CONFIG_FREERTOS_USE_IDLE_HOOK, регистрируется на этапе компиляции, один на систему и одинаков для всех ядер; хуки ESP-IDF регистрируются во время работы через esp_register_freertos_idle_hook() и esp_register_freertos_idle_hook_for_cpu(), до 8 хуков каждого типа на ядро, могут различаться по ядрам; callback не должен вызывать блокирующие функции; стр. Watchdogs: TWDT по умолчанию следит за задачами простоя каждого ядра и так выявляет задачи, которые долго не уступают процессор; опции CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0/CPU1
Страница помогла?

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