~~NOCACHE~~ ====== TNKernel : Задачи ====== ===== Введение ===== Задача в TNKernel это часть программного кода, которая с точки зрения программиста выполняется //одновременно// с другими задачами, что обеспечивается разделением процессорного времени между ними. Каждая задача может быть представлена как независимое приложение, которое владеет уникальными ресурсами (регистры процессора, указатель стека и т.п.). Эти ресурсы называются контекстом задачи, а время в течении которого задача выполняется можно назвать временем //в контексте задачи//. Когда текущая задача приостанавливает выполнение (в случае прерывания или вызова сервиса), осуществляется //переключение контекста// - контекст текущей задачи сохраняется в ее стеке, а контекст наиболее приоритетной задачи из готовых к выполнению восстанавливается. Этот механизм в TNKernel называется "диспетчером". Определение наиболее приоритетной задачи в момент переключения контекста осуществляется на основании набора правил, а механизм, который обеспечивает соблюдение этих правил называется "планировщиком". В TNKernel используется приоритетное вытесняющее планирование, основанное на приоритете, назначаемом каждой задаче, при этом чем меньше величина, тем выше уровень приоритета. В TNKernel доступно 32 уровня приоритета для 32-битных контроллеров (ARM, MIPS) и 16 уровней приоритета для 16-битных контроллеров (PIC24/dsPIC). Приоритеты 0 (самый высокий) и 31(**15**) (самый низкий) зарезервированы для системных задач. Для пользовательских задач доступны приоритеты от 1 до 30(**14**) включительно. В TNKernel несколько задач могут иметь одинаковый приоритет. ~~UP~~ ===== Состояния задач ===== Задачи в TNKernel могут находится в одном из четырех состояний: ; ''RUNNING'' : Задача выполняется в данный момент ; ''READY'' : Задача готова к выполнению, но не может получить , так как в данный момент выполняется задача с более высоким (или равным) приоритетом. В TNKernel оба состояния ''RUNNING'' и ''READY'' называются ''RUNNABLE'' ; ''WAIT/SUSPEND'' : Когда задача находится в состоянии ''WAIT/SUSPEND'' она не может начать выполнение до тех пор пока не выполнится условие, которого задача ожидает. При входе в состояние ''WAIT/SUSPEND'' контекст задачи сохраняется, при выходе из этого состояния контекст восстанавливается. Состояние ''WAIT/SUSPEND'' делится на три типа: {| class = "fpl" |- | ''WAITING'' | Задача находится в состоянии ''WAIT/SUSPEND'' до тех пор пока не наступит событие, которого она ожидает - завершится таймаут, освободится семафор, установится флаг и т.п. |- | ''SUSPENDED'' | Задача перемещена в состояние ''WAIT/SUSPEND'' другой задачей или самостоятельно путем вызова специального сервиса |- | ''WAITING_SUSPENDED'' | Задача находится как в состоянии ''WAITING'', так и в состоянии ''SUSPENDED'' (ожидает события и приостановлена специальным сервисом). Если задача освобождается от состояния ''WAITING'' (ожидаемое событие наступило), то она остается в состоянии ''SUSPENDED'' и наоборот. |} ; ''DORMANT'' : Задача уже создана, но еще ни разу не запускалась : Выполнение задачи завершено с помощью специального таймера. \\ Можно так же выделить состояние задачи, в котором она еще не создана - состояние ''NON-EXISTENT''. Граф перехода между состояниями изображен на рисунке: {{ tnkernel:ref:task:tn_task_states_diagram.png }} Сервисы, вызов которых приводит к изменению состояния задачи указаны возле направлений перехода. Для простоты префикс ''tn_task_'' и префикс ''i'' (вызов из прерывания) опущены. Переход задачи из состояния ''READY'' в состояние ''RUNNING'' происходит тогда, когда ее приоритет наивысший из приоритетов всех задач находящихся в состоянии ''READY''. Переход задачи из состояния ''RUNNING'' в состояние ''READY'' происходит тогда, когда ее приоритет меньше чем приоритет одной из задач, находящихся в состоянии ''READY''. Задача переходит из состояния ''RUNNING'' в состояние ''WAITING'' при вызове любого из системных сервисов, блокирующих выполнение задачи. Например, если задача пытается захватить семафор, а он занят другой задачей, ей ничего не остается как ожидать его освобождения в состоянии ''WAITING''. Задача переходит из состояния ''WAITING'' в состояние ''READY'' когда событие, которого она ожидала произошло. Например, освободился семафор или окончился таймаут ожидания. ~~UP~~ ===== Правила планирования ===== В TNKernel во время выполнения задачи с наивысшим приоритетом ни одна из других задач не может получить управление до тех пор, пока эта задача не перейдет в состояние ''WAITING/SUSPEND'' или ''DORMANT''. Если несколько задач с //разными приоритетами// готовы к выполнению (т.е. находятся в состоянии ''READY''), управление получит задача с наивысшим приоритетом. Если несколько задач с //одинаковым приоритетом// готовы к выполнению, то управление получит задача, которая перешла в состояние ''READY'' раньше остальных, т.е. первой стоит в очереди готовых к выполнению. > **Пример:** пусть задача **А** имеет приоритет 1, задачи **Б**, **В**, **Г**, **Д** - приоритет 3, задачи **Е** и **Ж** - приоритет 4, задача **З** - приоритет 5. Если все задачи находятся в состоянии ''READY'' последовательность выполнения будет следующая: \\ > 1. Задача **А** с наиболее высоким приоритетом (приоритет 1) > 2. Задачи **Б**, **В**, **Г** и **Д** в той последовательности в которой они перешли в состояние ''READY'' (приоритет 3) > 3. Задачи **Е** и **Ж** в той последовательности, в которой они перешли в состояние ''READY'' (приоритет 4) > 4. Задача **З**, так как она имеет наиболее низкий приоритет (приоритет 5) В TNKernel задачи с одинаковым приоритетом могут получать управление в соответствии с правилами round-robin планировщика (планировщика карусельного типа). В этом случае каждой задаче выделяется квант времени. Квант времени может быть выбран для каждого приоритета. ~~UP~~ ===== Системные задачи ===== В TNKernel существует две системные задачи, которые создаются при запуске системы. Одна из этих задач (''timer_task'') имеет наивысший приоритет (0) и обеспечивает функционирование системного таймера. Вторая задача (''idle_task'') имеет минимальный приоритет (31/15) и получает управление тогда, когда нет пользовательских задач готовых к выполнению. Эта задача может использоваться для сбора статистики или перевода процессора в состояние пониженного потребления энергии. Размеры стеков системных задач определяет пользователь, исходя из особенностей приложение. Кроме того, необходимо объявить функцию ''idle_user_cb()'', которая циклически вызывается из задачи ''idle_task''. ~~UP~~ ===== Структура управления задачей ===== Каждая задача ассоциируется со структурой управления: 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; В состав структуры управления задачей входят следующие элементы: {| class = "fpl" |- | ''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. ~~UP~~ ===== Сервисы управления задачами ===== TNKernel имеет следующий набор функций (сервисов) для управления задачами: ^ Сервис ^ Описание ^ Свойства ^ | **Создание и удаление задачи** ||| | \ \ [[tnkernel:ref:task:tn_task_create|tn_task_create()]] | Создание задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} | | \ \ [[tnkernel:ref:task:tn_task_delete|tn_task_delete()]] | Удаление задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} | | **Перезапуск задачи** ||| | \ \ [[tnkernel:ref:task:tn_task_exit|tn_task_exit()]] | Выход из текущей задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_terminate|tn_task_terminate()]] | Завершение работы задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_activate|tn_task_activate()]] | Перезапуск задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_iactivate|tn_task_iactivate()]] | Перезапуск задачи из прерывания | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Останов и восстановление задачи** ||| | \ \ [[tnkernel:ref:task:tn_task_suspend|tn_task_suspend()]] | Останов задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_isuspend|tn_task_isuspend()]] | Останов задачи в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_resume|tn_task_resume()]] | Восстановление задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_iresume|tn_task_iresume()]] | Восстановление задачи в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Приостановка выполнения и пробуждение задачи** ||| | \ \ [[tnkernel:ref:task:tn_task_sleep|tn_task_sleep()]] | Приостановка выполнения задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_wakeup|tn_task_wakeup()]] | Пробуждение приостановленной задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_iwakeup|tn_task_iwakeup()]] | Пробуждение приостановленной задачи в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Форсированный вывод задачи из состояния ''WAITING''** ||| | \ \ [[tnkernel:ref:task:tn_task_release_wait|tn_task_release_wait()]] | Форсированный вывод задачи из состояния ''WAITING'' | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | \ \ [[tnkernel:ref:task:tn_task_irelease_wait|tn_task_irelease_wait()]] | Форсированный вывод задачи из состояния ''WAITING'' в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Изменение приоритета задачи** ||| | \ \ [[tnkernel:ref:task:tn_task_change_priority|tn_task_change_priority()]] | Изменение приоритета задачи | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} | | **Получение информации о задаче** ||| | \ \ [[tnkernel:ref:task:tn_task_reference|tn_task_reference()]] | Получение информации о задаче | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} | | \ \ [[tnkernel:ref:task:tn_task_ireference|tn_task_ireference()]] | Получение информации о задаче в прерывании | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} | ~~UP~~