Основные концепции задач
С точки зрения системы задача — это наименьшая единица операции, конкурирующая за системные ресурсы. TencentOS tiny — это операционная система, которая поддерживает многозадачность. Задачи могут использовать системные ресурсы, такие как ЦП и память, или ожидать их, и выполняться независимо от других задач. Теоретически любое количество задач может иметь одинаковый приоритет, чтобы они были готовы Несколько задач с одинаковым приоритетом в состоянии будут совместно использовать процессор в режиме переключения временных интервалов.
нообращать вниманиеДело в том, что в TencentOS tiny невозможно создать задачу с тем же приоритетом, что и бездействующая задача.
K_TASK_PRIO_IDLE
, задачам с одинаковым приоритетом должно быть разрешено использовать планирование временных интервалов, открытьTOS_CFG_ROUND_ROBIN_EN
.
Вкратце: задачи TencentOS tiny можно рассматривать как набор независимых задач. Каждая задача выполняется в своей среде. В любой момент времени выполняется только одна задача, и крошечный планировщик TencentOS решает, какую задачу запустить. от宏观
Получается, что все задачи выполняются одновременно.
Задачи в TencentOS представляют собой механизмы упреждающего планирования. Задачи с высоким приоритетом могут прерывать задачи с низким приоритетом. Задачи с низким приоритетом можно планировать только после того, как задачи с высоким приоритетом заблокированы или завершены. В то же время TencentOS также поддерживает циклическое планирование временных интервалов.
Система может поддерживать 10 приоритетов по умолчанию,0~TOS_CFG_TASK_PRIO_MAX
, это определение макроса можно изменить, чем больше значение приоритета, тем ниже приоритет задачи,(TOS_CFG_TASK_PRIO_MAX - (k_prio_t)1u)
Он имеет самый низкий приоритет и назначается бездействующим задачам.
#define K_TASK_PRIO_IDLE (k_prio_t)(TOS_CFG_TASK_PRIO_MAX - (k_prio_t)1u)
#define K_TASK_PRIO_INVALID (k_prio_t)(TOS_CFG_TASK_PRIO_MAX)
статус задачи
Статус задачи TencentOS tiny выглядит следующим образом.
- Состояние готовности (КTASKSTATE_READY): задача находится в списке готовых, готовая задача имеет возможность выполнения, просто подождите, пока планировщик запланирует, и вновь созданная задача будет инициализирована до состояния готовности.
- Рабочее состояние (КTASKSTATE_READY): Это состояние указывает на то, что задача выполняется, и в это время она занимает процессор. На самом деле задача в это время все еще находится в списке готовности. Планировщик TencentOS всегда выбирает запуск задачи в состоянии готовности с наивысший приоритет.Когда задача запущена, ее состояние задачи становится состоянием выполнения.
- Состояние сна (КTASKSTATE_SLEEP): Если задача в данный момент спит, чтобы отказаться от права использования ЦП, то можно сказать, что задача находится в спящем состоянии, задача не находится в списке готовности, а задача находится в списке сна (или список задержек).
- Состояние ожидания (КTASKSTATE_PEND): задача ожидает состояния, такого как семафор, очередь или ожидание события.
- Подвешенное состояние (КTASKSTATE_SUSPENDED): задача приостановлена, и в настоящее время она невидима для планировщика.
- Выходное состояние (КTASKSTATE_DELETED): задача завершена и удалена.
- Дождитесь состояния тайм-аута (KTASKSTATE_PENDTIMEOUT): задача ожидает семафора, очереди или истечения времени ожидания события.
- Приостановка сна (KTASKSTATESLEEPПРИОСТАНОВЛЕНО): состояние, когда задача приостановлена во время сна.
- Ожидание состояния ожидания (KTASKSTATEPENDПРИОСТАНОВЛЕНО): состояние, в котором задача приостановлена во время ожидания семафора, очереди или события.
- Ожидание состояния ожидания тайм-аута (KTASKSTATEPENDTIMEOUTПРИОСТАНОВЛЕНО): задача ожидает семафора, очереди или истечения времени ожидания события, но в это время задача приостановлена.
// ready to schedule
// a task's pend_list is in readyqueue
#define K_TASK_STATE_READY (k_task_state_t)0x0000
// delayed, or pend for a timeout
// a task's tick_list is in k_tick_list
#define K_TASK_STATE_SLEEP (k_task_state_t)0x0001
// pend for something
// a task's pend_list is in some pend object's list
#define K_TASK_STATE_PEND (k_task_state_t)0x0002
// suspended
#define K_TASK_STATE_SUSPENDED (k_task_state_t)0x0004
// deleted
#define K_TASK_STATE_DELETED (k_task_state_t)0x0008
// actually we don't really need those TASK_STATE below, if you understand the task state deeply, the code can be much more elegant.
// we are pending, also we are waitting for a timeout(eg. tos_sem_pend with a valid timeout, not TOS_TIME_FOREVER)
// both a task's tick_list and pend_list is not empty
#define K_TASK_STATE_PENDTIMEOUT (k_task_state_t)(K_TASK_STATE_PEND | K_TASK_STATE_SLEEP)
// suspended when sleeping
#define K_TASK_STATE_SLEEP_SUSPENDED (k_task_state_t)(K_TASK_STATE_SLEEP | K_TASK_STATE_SUSPENDED)
// suspened when pending
#define K_TASK_STATE_PEND_SUSPENDED (k_task_state_t)(K_TASK_STATE_PEND | K_TASK_STATE_SUSPENDED)
// suspended when pendtimeout
#define K_TASK_STATE_PENDTIMEOUT_SUSPENDED (k_task_state_t)(K_TASK_STATE_PENDTIMEOUT | K_TASK_STATE_SUSPENDED)
Структура данных задач обслуживания в TencentOS
готовый список
TencentOS tiny поддерживает готовый список для монтирования всех задач в готовом состоянии в системе.readyqueue_t
Список типов, чьи переменные-члены следующие:
readyqueue_t k_rdyq;
typedef struct readyqueue_st {
k_list_t task_list_head[TOS_CFG_TASK_PRIO_MAX];
uint32_t prio_mask[K_PRIO_TBL_SIZE];
k_prio_t highest_prio;
} readyqueue_t;
task_list_head
это тип спискаk_list_t
Массив , TencentOS tiny выделяет список для каждой приоритетной задачи, а система поддерживает максимальный приоритетTOS_CFG_TASK_PRIO_MAX
prio_mask
Это массив масок приоритетов, представляющий собой массив переменных 32-битного типа, а количество членов массива определяетсяTOS_CFG_TASK_PRIO_MAX
Принять решение:
#define K_PRIO_TBL_SIZE ((TOS_CFG_TASK_PRIO_MAX + 31) / 32)
когдаTOS_CFG_TASK_PRIO_MAX
Когда оно не превышает 32, имеется только одна переменная-член массива, представляющая собой 32-разрядное значение переменной, тогда каждый бит переменной представляет собой приоритет. например, когдаTOS_CFG_TASK_PRIO_MAX
64 года,prio_mask[0]
Каждый бит (бит) переменной представляет0-31
приоритет, покаprio_mask[1]
Каждый бит переменной представляет32-63
приоритет.
highest_prio
Это запись самого высокого приоритета из текущего списка приоритетов, что удобно для индексации.task_list_head
.
Список задержек
В этот список будут монтироваться задачи, связанные с системным временем, это может быть сон, ожидание семафоров, события, очереди сообщений и т.д.
k_list_t k_tick_list;
блок управления полетом
В многозадачной системе выполнение задач планируется системой. Для плавного планирования задач система определяет дополнительный блок управления задачами для каждой задачи.Этот блок управления задачами эквивалентен идентификационной карточке задачи, которая содержит всю информацию о задаче, такую как указатель стека задачи. , название задачи, формальные параметры задачи и т.д. С помощью этого блока управления задачами все последующие операции системы над задачей могут быть реализованы через этот блок управления задачами. Блок управления задачами TencentOS выглядит следующим образом:
typedef struct k_task_st {
k_stack_t *sp; /**< 任务栈指针,用于切换上下文*/
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
knl_obj_t knl_obj; /**< 只是为了验证,测试当前对象是否真的是一项任务。*/
#endif
char *name; /**< 任务名称 */
k_task_entry_t entry; /**< 任务主体 */
void *arg; /**< 任务主体形参 */
k_task_state_t state; /**< 任务状态 */
k_prio_t prio; /**< 任务优先级 */
k_stack_t *stk_base; /**< 任务栈基地址 */
size_t stk_size; /**< 任务栈大小 */
k_tick_t tick_expires; /**< 任务阻塞的时间 */
k_list_t tick_list; /**< 延时列表 */
k_list_t pend_list; /**< 就绪、等待列表 */
#if TOS_CFG_MUTEX_EN > 0u
k_list_t mutex_own_list; /**< 任务拥有的互斥量 */
k_prio_t prio_pending; /*< 用于记录持有互斥量的任务初始优先级,在优先级继承中使用 */
#endif
pend_obj_t *pending_obj; /**< 记录任务此时挂载到的列表 */
pend_state_t pend_state; /**< 等待被唤醒的原因(状态) */
#if TOS_CFG_ROUND_ROBIN_EN > 0u
k_timeslice_t timeslice_reload; /**< 时间片初始值(重装载值) */
k_timeslice_t timeslice; /**< 剩余时间片 */
#endif
#if TOS_CFG_MSG_EN > 0u
void *msg_addr; /**< 保存接收到的消息 */
size_t msg_size; /**< 保存接收到的消息大小 */
#endif
#if TOS_CFG_EVENT_EN > 0u
k_opt_t opt_event_pend; /**< 等待事件的的操作类型:TOS_OPT_EVENT_PEND_ANY 、 TOS_OPT_EVENT_PEND_ALL */
k_event_flag_t flag_expect; /**< 期待发生的事件 */
k_event_flag_t *flag_match; /**< 等待到的事件 */
#endif
} k_task_t;
Создать задачу
В TencentOS крошечный, любое использование__API__
Пользователю предоставляются оформленные функции, а использование__KERNEL__
Декорированный код используется ядром. Функция создания задачи TencentOS имеет несколько параметров:
параметр | значение |
---|---|
task |
блок управления полетом |
name |
название задачи |
entry |
Тема задания |
arg |
параметр задачи |
prio |
приоритет |
stk_base |
Базовый адрес стека задач |
stk_size |
размер стека задач |
timeslice |
квант времени |
Подробное объяснение параметра (источник TencentOS крошечное руководство по разработке):
- task
это кtaskуказатель типа t, ktaskt — тип структуры задачи ядра. Примечание. Указатель задачи должен указывать на k, чей жизненный цикл больше, чем жизненный цикл создаваемого тела задачи.taskt, если жизненный цикл переменной, на которую указывает указатель, короче жизненного цикла создаваемого тела задачи, например, это может быть переменная в стеке функций с экстремальным жизненным циклом, а тело задачи может все еще выполняться, пока ktaskПеременная t была уничтожена, что вызвало непредсказуемые проблемы с системным планированием.
- name
Указатель на строку имени задачи. Примечание: Как и в случае с задачей, жизненный цикл строки, на которую указывает указатель, должен быть больше, чем жизненный цикл создаваемого тела задачи.Вообще говоря, достаточно передать постоянный указатель на строку.
- entry
Запись функции для выполнения тела задачи. После того, как задача создана и переходит в состояние выполнения, запись является точкой входа для выполнения задачи, и пользователь может написать бизнес-логику в этой функции.
- arg
Аргументы, переданные в функцию ввода задачи.
- prio
приоритет задачи. Чем меньше значение prio, тем выше приоритет. Пользователи могутconfig.h, через TOSCFGTASKPRIOMAX используется для настройки максимального значения приоритета задачи, в реализации ядра приоритет простаивающей задачи будет назначен как TOSCFGTASKPRIOMAX - 1, этот приоритет может использоваться только бездействующими задачами. Таким образом, для задачи, созданной пользователем, разумным диапазоном приоритета будет [0, TOSCFGTASKPRIOМАКС-2]. Дополнительные условия обслуживанияCFGTASKЗначение конфигурации PRIO_MAX должно быть больше или равно 8.
- stk_base
Начальный адрес пространства стека, используемого задачей во время выполнения. Примечание. Как и в случае с задачей, жизненный цикл области памяти, на которую указывает этот указатель, должен быть больше, чем жизненный цикл создаваемого тела задачи. сткбаза kНачальный адрес массива типа stack_t.
- stk_size
Размер пространства стека для задачи. Примечание: потому что сткбаза kstackУказатель массива типа t, поэтому реальное пространство стека занимает в памяти размер stk.размер * размер (k_stack_t).
- timeslice
Размер кванта времени текущей задачи в соответствии с механизмом поворота кванта времени. Когда временной интервал равен 0, для временного интервала планирования задач будет установлен размер по умолчанию (TOSCFGCPUTICKPER_SECOND / 10), количество тактов системных часов (systick) / 10.
Реализация задачи создания такова: Сначала проверяются параметры, а потом отмечу:В TencentOS нельзя создать задачу с тем же приоритетом, что и бездействующая задача.K_TASK_PRIO_IDLE
. тогда позвониcpu_task_stk_init
Функция инициализирует стек задач и записывает входящие параметры в блок управления задачами. если открытоTOS_CFG_ROUND_ROBIN_EN
определение макроса, это означает, что планирование временного интервала поддерживается, тогда вам необходимо настроить информацию, связанную с временным интерваломtimeslice
в блок управления задачами. тогда позвониtask_state_set_ready
Функция переводит вновь созданную задачу в состояние готовностиK_TASK_STATE_READY
, затем позвонитеreadyqueue_add_tail
Функция для вставки задачи в готовый списокk_rdyq
середина. Если планировщик запущен, выполните планирование задач.
Лично я немного жалею, что нет динамического выделения из кучи, я предпочитаю простой интерфейс функций~!
код показывает, как показано ниже:
__API__ k_err_t tos_task_create(k_task_t *task,
char *name,
k_task_entry_t entry,
void *arg,
k_prio_t prio,
k_stack_t *stk_base,
size_t stk_size,
k_timeslice_t timeslice)
{
TOS_CPU_CPSR_ALLOC();
TOS_IN_IRQ_CHECK();
TOS_PTR_SANITY_CHECK(task);
TOS_PTR_SANITY_CHECK(entry);
TOS_PTR_SANITY_CHECK(stk_base);
if (unlikely(stk_size < sizeof(cpu_context_t))) {
return K_ERR_TASK_STK_SIZE_INVALID;
}
if (unlikely(prio == K_TASK_PRIO_IDLE && !knl_is_idle(task))) {
return K_ERR_TASK_PRIO_INVALID;
}
if (unlikely(prio > K_TASK_PRIO_IDLE)) {
return K_ERR_TASK_PRIO_INVALID;
}
task_reset(task);
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
knl_object_init(&task->knl_obj, KNL_OBJ_TYPE_TASK);
#endif
task->sp = cpu_task_stk_init((void *)entry, arg, (void *)task_exit, stk_base, stk_size);
task->entry = entry;
task->arg = arg;
task->name = name;
task->prio = prio;
task->stk_base = stk_base;
task->stk_size = stk_size;
#if TOS_CFG_ROUND_ROBIN_EN > 0u
task->timeslice_reload = timeslice;
if (timeslice == (k_timeslice_t)0u) {
task->timeslice = k_robin_default_timeslice;
} else {
task->timeslice = timeslice;
}
#endif
TOS_CPU_INT_DISABLE();
task_state_set_ready(task);
readyqueue_add_tail(task);
TOS_CPU_INT_ENABLE();
if (tos_knl_is_running()) {
knl_sched();
}
return K_ERR_NONE;
}
уничтожение задачи
Эта функция очень проста: задача уничтожается в соответствии с переданным блоком управления задачей, или может быть передан NULL для уничтожения текущей задачи. Но нельзя уничтожать неработающие задачиk_idle_task
, когда планировщик не может уничтожить себя, когда он заблокирован, он вернетсяK_ERR_SCHED_LOCKED
код ошибки. Если используется мьютекс, мьютекс освобождается при уничтожении задачи и уничтожается в соответствии с состоянием задачи, например, если задача находится в состоянии готовности, отложенном состоянии или состоянии ожидания. ,对应的状态列表
удалено в. Код реализован следующим образом:
__API__ k_err_t tos_task_destroy(k_task_t *task)
{
TOS_CPU_CPSR_ALLOC();
TOS_IN_IRQ_CHECK();
if (unlikely(!task)) {
task = k_curr_task;
}
#if TOS_CFG_OBJECT_VERIFY_EN > 0u
if (!knl_object_verify(&task->knl_obj, KNL_OBJ_TYPE_TASK)) {
return K_ERR_OBJ_INVALID;
}
#endif
if (knl_is_idle(task)) {
return K_ERR_TASK_DESTROY_IDLE;
}
if (knl_is_self(task) && knl_is_sched_locked()) {
return K_ERR_SCHED_LOCKED;
}
TOS_CPU_INT_DISABLE();
#if TOS_CFG_MUTEX_EN > 0u
// when we die, wakeup all the people in this land.
if (!tos_list_empty(&task->mutex_own_list)) {
task_mutex_release(task);
}
#endif
if (task_state_is_ready(task)) { // that's simple, good kid
readyqueue_remove(task);
}
if (task_state_is_sleeping(task)) {
tick_list_remove(task);
}
if (task_state_is_pending(task)) {
pend_list_remove(task);
}
task_reset(task);
task_state_set_deleted(task);
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
задача сон
Сон задачи очень прост, основная идея — убрать задачу из списка готовых и добавить в список отложенныхk_tick_list
, если планировщик заблокирован, вернуть код ошибки напрямуюK_ERR_SCHED_LOCKED
, если время сна равно 0, вызовитеtos_task_yield
Функция инициирует планирование задачи; вызовtick_list_add
функция поставит задачу插入延时列表
средний, время снаdelay
задается пользователем. нотребует вниманиядаЕсли время сна задачи постоянноеTOS_TIME_FOREVER
, вернет код ошибкиK_ERR_DELAY_FOREVER
, это потому, что задача sleep主动行为
, если вы постоянно спите, вы не сможете активно проснуться, а такие задачи, как ожидание событий, семафоров, очередей сообщений и т. д., являются пассивным поведением, которое может быть постоянным ожиданием. , а очередь сообщений не пуста. будет разбужена, это被动行为
, нужно различать два момента. последний звонокreadyqueue_remove
Функция удаляет задачу из списка готовых, а затем вызываетknl_sched
Когда функция инициирует планирование задачи, она может переключиться на другую задачу. Код для сна задачи выглядит следующим образом:
__API__ k_err_t tos_task_delay(k_tick_t delay)
{
TOS_CPU_CPSR_ALLOC();
TOS_IN_IRQ_CHECK();
if (knl_is_sched_locked()) {
return K_ERR_SCHED_LOCKED;
}
if (unlikely(delay == (k_tick_t)0u)) {
tos_task_yield();
return K_ERR_NONE;
}
TOS_CPU_INT_DISABLE();
if (tick_list_add(k_curr_task, delay) != K_ERR_NONE) {
TOS_CPU_INT_ENABLE();
return K_ERR_DELAY_FOREVER;
}
readyqueue_remove(k_curr_task);
TOS_CPU_INT_ENABLE();
knl_sched();
return K_ERR_NONE;
}
Следуй за мной, если хочешь!
Соответствующий код можно получить в фоне официального аккаунта. Добро пожаловать в публичный аккаунт «Развития Интернета вещей IoT»