Задача в TNKernel это часть программного кода, которая с точки зрения программиста выполняется одновременно с другими задачами, что обеспечивается разделением процессорного времени между ними. Каждая задача может быть представлена как независимое приложение, которое владеет уникальными ресурсами (регистры процессора, указатель стека и т.п.). Эти ресурсы называются контекстом задачи, а время в течении которого задача выполняется можно назвать временем в контексте задачи.
Когда текущая задача приостанавливает выполнение (в случае прерывания или вызова сервиса), осуществляется переключение контекста - контекст текущей задачи сохраняется в ее стеке, а контекст наиболее приоритетной задачи из готовых к выполнению восстанавливается. Этот механизм в TNKernel называется "диспетчером".
Определение наиболее приоритетной задачи в момент переключения контекста осуществляется на основании набора правил, а механизм, который обеспечивает соблюдение этих правил называется "планировщиком".
В TNKernel используется приоритетное вытесняющее планирование, основанное на приоритете, назначаемом каждой задаче, при этом чем меньше величина, тем выше уровень приоритета. В TNKernel доступно 32 уровня приоритета для 32-битных контроллеров (ARM, MIPS) и 16 уровней приоритета для 16-битных контроллеров (PIC24/dsPIC).
Приоритеты 0 (самый высокий) и 31(15) (самый низкий) зарезервированы для системных задач. Для пользовательских задач доступны приоритеты от 1 до 30(14) включительно. В TNKernel несколько задач могут иметь одинаковый приоритет.
Задачи в TNKernel могут находится в одном из четырех состояний:
RUNNING
READY
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() | Получение информации о задаче в прерывании |