Содержание

TNKernel : Задачи

Введение

Задача в TNKernel это часть программного кода, которая с точки зрения программиста выполняется одновременно с другими задачами, что обеспечивается разделением процессорного времени между ними. Каждая задача может быть представлена как независимое приложение, которое владеет уникальными ресурсами (регистры процессора, указатель стека и т.п.). Эти ресурсы называются контекстом задачи, а время в течении которого задача выполняется можно назвать временем в контексте задачи.

Когда текущая задача приостанавливает выполнение (в случае прерывания или вызова сервиса), осуществляется переключение контекста - контекст текущей задачи сохраняется в ее стеке, а контекст наиболее приоритетной задачи из готовых к выполнению восстанавливается. Этот механизм в TNKernel называется "диспетчером".

Определение наиболее приоритетной задачи в момент переключения контекста осуществляется на основании набора правил, а механизм, который обеспечивает соблюдение этих правил называется "планировщиком".

В TNKernel используется приоритетное вытесняющее планирование, основанное на приоритете, назначаемом каждой задаче, при этом чем меньше величина, тем выше уровень приоритета. В TNKernel доступно 32 уровня приоритета для 32-битных контроллеров (ARM, MIPS) и 16 уровней приоритета для 16-битных контроллеров (PIC24/dsPIC).

Приоритеты 0 (самый высокий) и 31(15) (самый низкий) зарезервированы для системных задач. Для пользовательских задач доступны приоритеты от 1 до 30(14) включительно. В TNKernel несколько задач могут иметь одинаковый приоритет.

Состояния задач

Задачи в TNKernel могут находится в одном из четырех состояний:

RUNNING
Задача выполняется в данный момент
READY
Задача готова к выполнению, но не может получить , так как в данный момент выполняется задача с более высоким (или равным) приоритетом. В TNKernel оба состояния RUNNING и READY называются RUNNABLE
WAIT/SUSPEND
Когда задача находится в состоянии WAIT/SUSPEND она не может начать выполнение до тех пор пока не выполнится условие, которого задача ожидает. При входе в состояние WAIT/SUSPEND контекст задачи сохраняется, при выходе из этого состояния контекст восстанавливается. Состояние WAIT/SUSPEND делится на три типа:

WAITING Задача находится в состоянии WAIT/SUSPEND до тех пор пока не наступит событие, которого она ожидает - завершится таймаут, освободится семафор, установится флаг и т.п.
SUSPENDED Задача перемещена в состояние WAIT/SUSPEND другой задачей или самостоятельно путем вызова специального сервиса
WAITING_SUSPENDED Задача находится как в состоянии WAITING, так и в состоянии SUSPENDED (ожидает события и приостановлена специальным сервисом). Если задача освобождается от состояния WAITING (ожидаемое событие наступило), то она остается в состоянии SUSPENDED и наоборот.

DORMANT
Задача уже создана, но еще ни разу не запускалась
Выполнение задачи завершено с помощью специального таймера.


Можно так же выделить состояние задачи, в котором она еще не создана - состояние NON-EXISTENT.

Граф перехода между состояниями изображен на рисунке:

Сервисы, вызов которых приводит к изменению состояния задачи указаны возле направлений перехода. Для простоты префикс tn_task_ и префикс i (вызов из прерывания) опущены.

Переход задачи из состояния READY в состояние RUNNING происходит тогда, когда ее приоритет наивысший из приоритетов всех задач находящихся в состоянии READY.

Переход задачи из состояния RUNNING в состояние READY происходит тогда, когда ее приоритет меньше чем приоритет одной из задач, находящихся в состоянии READY.

Задача переходит из состояния RUNNING в состояние WAITING при вызове любого из системных сервисов, блокирующих выполнение задачи. Например, если задача пытается захватить семафор, а он занят другой задачей, ей ничего не остается как ожидать его освобождения в состоянии WAITING.

Задача переходит из состояния WAITING в состояние READY когда событие, которого она ожидала произошло. Например, освободился семафор или окончился таймаут ожидания.

Правила планирования

В TNKernel во время выполнения задачи с наивысшим приоритетом ни одна из других задач не может получить управление до тех пор, пока эта задача не перейдет в состояние WAITING/SUSPEND или DORMANT.

Если несколько задач с разными приоритетами готовы к выполнению (т.е. находятся в состоянии READY), управление получит задача с наивысшим приоритетом.

Если несколько задач с одинаковым приоритетом готовы к выполнению, то управление получит задача, которая перешла в состояние READY раньше остальных, т.е. первой стоит в очереди готовых к выполнению.

Пример: пусть задача А имеет приоритет 1, задачи Б, В, Г, Д - приоритет 3, задачи Е и Ж - приоритет 4, задача З - приоритет 5. Если все задачи находятся в состоянии READY последовательность выполнения будет следующая:

1. Задача А с наиболее высоким приоритетом (приоритет 1)
2. Задачи Б, В, Г и Д в той последовательности в которой они перешли в состояние READY (приоритет 3)
3. Задачи Е и Ж в той последовательности, в которой они перешли в состояние READY (приоритет 4)
4. Задача З, так как она имеет наиболее низкий приоритет (приоритет 5)

В TNKernel задачи с одинаковым приоритетом могут получать управление в соответствии с правилами round-robin планировщика (планировщика карусельного типа). В этом случае каждой задаче выделяется квант времени. Квант времени может быть выбран для каждого приоритета.

Системные задачи

В TNKernel существует две системные задачи, которые создаются при запуске системы.

Одна из этих задач (timer_task) имеет наивысший приоритет (0) и обеспечивает функционирование системного таймера.

Вторая задача (idle_task) имеет минимальный приоритет (31/15) и получает управление тогда, когда нет пользовательских задач готовых к выполнению. Эта задача может использоваться для сбора статистики или перевода процессора в состояние пониженного потребления энергии.

Размеры стеков системных задач определяет пользователь, исходя из особенностей приложение. Кроме того, необходимо объявить функцию idle_user_cb(), которая циклически вызывается из задачи idle_task.

Структура управления задачей

Каждая задача ассоциируется со структурой управления:

typedef struct TN_TCB_S_STRUCT
{
    TN_UWORD                * task_stk;
    CDLL_QUEUE_S              task_queue;
    CDLL_QUEUE_S              timer_queue;
    CDLL_QUEUE_S              block_queue;
    CDLL_QUEUE_S              create_queue;
    CDLL_QUEUE_S              mutex_queue;
    CDLL_QUEUE_S            * pwait_queue;
    struct TN_TCB_S_STRUCT  * blk_task;
 
    TN_UWORD                * stk_start;
    TN_UWORD                  stk_size;
 
    void                    * task_func_addr;
    void                    * task_func_param;
 
    TN_UWORD                  base_priority;
    TN_UWORD                  priority;
    TN_UWORD                  id_task;
 
    TN_WORD                   task_state;
    TN_UWORD                  task_wait_reason;
    TN_WORD                   task_wait_rc;
    TN_UWORD                  tick_count;
    TN_UWORD                  tslice_count;
 
    TN_UWORD                  ewait_pattern;
    TN_UWORD                  ewait_mode;
 
    void                    * data_elem;
 
    TN_UWORD                  activate_count;
    TN_UWORD                  wakeup_count;
    TN_UWORD                  suspend_count;
} TN_TCB_S;

В состав структуры управления задачей входят следующие элементы:

task_stk Указатель на вершину стека задачи
task_queue Элемент очереди для включения задачи в список существующих задач
timer_queue Элемент очереди для включения задачи в список задач, ожидающих события таймера (таймаут и т.п.)
block_queue Элемент очереди для включения задачи в список заблокированных задач (используется в протоколе priority ceiling системных мютексов)
create_queue Элемент очереди для включения задачи в список созданных задач
mutex_queue Список всех мютексов, заблокированных задачей
pwait_queue Указатель на очередь объектов (семафоров, флагов), ожидаемых задачей
blk_task Указатель на структуру задачи которая заблокировала эту задачу (используется в протоколе priority ceiling системных мютексов)
stk_start Указатель на базовый адрес стека задачи
stk_size Размер стека задачи
task_func_addr Указатель на функцию задачи
task_func_param Укащатель на параметр, передаваемый в функцию задачи
base_priority Базовый приоритет задачи
priority Текущий приоритет задачи
id_task Поле идентификации объекта как задачи
task_state Состояние задачи
task_wait_reason Причина нахождения в состоянии WAITING
task_wait_rc Код, возвращаемый задачей при выходе из состояния WAITING (причина, по которой задача вышла из состояния ожидания)
tick_count Время до истечения таймаута в системных тиках
tslice_count Счетчик кванта времени для round-robin планирования в системных тиках
ewait_pattern Маска ожидаемых флагов
ewait_mode Тип ожидания флагов (И или ИЛИ, т.е. все флаги, или хотя бы один из маски)
data_elem Указатель на очередь сообщений
activate_count Счетчик запросов на активацию задачи
wakeup_count Счетчик запросов на пробуждение задачи
suspend_count Счетчик запросов на останов задачи

Структура задачи доступна только при определении TN_DEBUG. Тем не менее, прямой доступ к элементам структуры задачи крайне не рекомендуется, так как это является вмешательством в работу планировщика и других сервисов RTOS.

Сервисы управления задачами

TNKernel имеет следующий набор функций (сервисов) для управления задачами:

Сервис Описание Свойства
Создание и удаление задачи
  tn_task_create() Создание задачи Разрешен вызов только в контексте задачи
  tn_task_delete() Удаление задачи Разрешен вызов только в контексте задачи
Перезапуск задачи
  tn_task_exit() Выход из текущей задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_terminate() Завершение работы задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_activate() Перезапуск задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_iactivate() Перезапуск задачи из прерывания Разрешен вызов только в прерывании Может привести к переключению контекста
Останов и восстановление задачи
  tn_task_suspend() Останов задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_isuspend() Останов задачи в прерывании Разрешен вызов только в прерывании Может привести к переключению контекста
  tn_task_resume() Восстановление задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_iresume() Восстановление задачи в прерывании Разрешен вызов только в прерывании Может привести к переключению контекста
Приостановка выполнения и пробуждение задачи
  tn_task_sleep() Приостановка выполнения задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_wakeup() Пробуждение приостановленной задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_iwakeup() Пробуждение приостановленной задачи в прерывании Разрешен вызов только в прерывании Может привести к переключению контекста
Форсированный вывод задачи из состояния WAITING
  tn_task_release_wait() Форсированный вывод задачи из состояния WAITING Разрешен вызов только в контексте задачи Может привести к переключению контекста
  tn_task_irelease_wait() Форсированный вывод задачи из состояния WAITING в прерывании Разрешен вызов только в прерывании Может привести к переключению контекста
Изменение приоритета задачи
  tn_task_change_priority() Изменение приоритета задачи Разрешен вызов только в контексте задачи Может привести к переключению контекста
Получение информации о задаче
  tn_task_reference() Получение информации о задаче Разрешен вызов только в контексте задачи
  tn_task_ireference() Получение информации о задаче в прерывании Разрешен вызов только в прерывании