====== TNKernel : Системные сервисы ======
===== Введение =====
Системные сервисы не относятся напрямую к каким-либо объектом RTOS, они позволяют управлять системой в целом или получать текущие параметры системы. К системным сервисам относятся функция запуска ''tn_start_system()'', функция управления карусельным планированием ''tn_sys_tslice_ticks()'', функция обработчика системного таймера ''tn_tick_int_processing()'', функции реализации критической секции ''tn_sys_enter_critical()'' и ''tn_sys_exit_critical()'', функции обслуживания системного времени ''tn_sys_time_set()'' и ''tn_sys_time_get()''.
~~UP~~
===== Запуск системы =====
Функция запуска системы ''tn_start_system()'' обеспечивает инициализацию всех внутренних структур RTOS, создание двух [[tnkernel:ref:task:intro#Системные задачи|системных задач]] и первое переключение контекста на системную задачу таймера. Фукция ''tn_start_system()'' вызывается только один раз и является функцией без возврата.
В порте TNKernel для контроллеров PIC24/dsPIC в функцию передается несколько параметров, позволяющих более гибко настроить систему (выбрать размеры стеков системных задач и т.п.). В оригинальной версии функция запуска параметров не имеет.
Диаграма запуска TNKernel изображена на рисунке:
{{ tnkernel:ref:sys:tn_startup_diagram.png }}
Сервис запуска ''tn_start_system()'' как правило вызывается из функции ''main()''.
Системные прерывания должны быть запрещены до момента вызова ''tn_start_system()''.
Функция ''tn_start_system()'' создает две системные задачи ''tn_idle_task()'' и ''tn_timer_task()'' и запускает задачу ''tn_timer_task()'', которая при старте последовательно вызывает две callback-функции - ''appl_init_callback()'' и ''cpu_interrupt_enbl_callback()''. Указатели на эти функции передаются в систему в вызове ''tn_start_system()''.
Функция ''appl_init_callback()'' служит для инициализации системного таймера, периферийных модулей, создания необходимых задач. В функции ''cpu_interrupt_enbl_callback()'' разрешается прерывание от системного таймера и после выхода из нее система начинает нормальное функционирование.
~~UP~~
===== Системный таймер =====
Для того чтобы реализовать функции ожидания или таймауты, в системе должен присутствовать таймер, доступный планировщику. Назовем такой таймер **системным таймером**. Как правило, период таймера постоянен на всем протяжении работы и это период называется **системным тиком**. Системный тик - это минимальная единица времени доступная планировщику. Все времена, таймауты указываются именно в системных тиках а не в абсолютных единицах времени.
Обычно системный таймер реализуется на основании одного из аппаратных таймеров микроконтроллера, или таймера, являющегося частью ядра. В обработчике прерывания от этого таймера необходимо вызвать функцию ''tn_tick_int_processing()'', которая и обеспечивает "ход" системного времени.
Функция ''tn_tick_int_processing()'' должна вызываться только в обработчике прерывания.
~~UP~~
===== Управление Round-Robin =====
Round-Robin или карусельное планирование - это принцип переключения задач с **одинаковым** приоритетом при котором каждой задаче выделяется определенный квант времени (с дискретностью один системный тик). После того как задача отработает свой квант, планировщик запускает следующую в очереди готовых к выполнению задачу.
В TNKernel включен сервис ''tn_sys_tslice_ticks()'', позволяющий настраивать длительность кванта выполнения для каждого приоритета или отключать карусельное планирование.
Карусельное планирование отключено по умолчанию для всех приоритетов.
~~UP~~
===== Запрещение переключения контекста =====
//Критическая секция// это часть задачи, в которой осуществляется доступ к разделяемому ресурсу. Если один и тот же ресурс (например, глобальную переменную) используют две или более задач, критические секции называют конкурирующими. В этом случае необходимо защитить критическую секцию таким образом, чтобы доступ к ресурсу являлся атомарным.
Один из способов защиты критической секции - это использование мютекса. Однако часто мютекс является избыточным объектом для реализации критической секции и в этом случае используют парные функции запрещения и разрешения переключения контекста.
В TNKernel для PIC24/dsPIC переключение контекста запрещает функция ''tn_sys_enter_critical()'' и разрешает функция ''tn_sys_exit_critical()''.
Вызов функций ''tn_sys_enter_critical()'' и ''tn_sys_exit_critical()'' может быть несимметричным и вложенным. Например, допустимо следующее:
void foo (void)
{
tn_sys_enter_critical();
/* ... */
}
void TN_TASK task_1 (void *param)
{
for (;;)
{
/* ... */
tn_sys_enter_critical();
foo();
tn_sys_exit_critical();
/* ... */
}
}
Однако рекомендуется использовать симметричный вызов функций запрещения и разрешения планирования, так как обратное может привести к логическим ошибкам.
Запрещение переключения контекста не приветствуется, это является вмешательством в работу планировщика. Однако функции ''tn_sys_enter_critical()'' и ''tn_sys_exit_critical()'' могут быть полезны для выполнения относительно быстрых операций, для которых использование мютекса слишком избыточно. Примером такой операции может служить [[tnkernel:faq#Разделяемые ресурсы|вывод в порт]] контроллера.
~~UP~~
===== Системное время =====
Под системным временем подразумевается беззнаковая целая переменная, инкрементируемая каждый системный тик. Значение переменной может быть получено путем вызова функции ''tn_sys_time_get()'' и установлено с помощью вызова ''tn_sys_time_set()''.
Системное время можно использовать для подсчета времени выполнения какого-либо действия (с точностью плюс-минус системный тик) или для "подстройки" периода "вызова" задач.
Допустим в системе присутствует задача, период "вызова" которой должен быть строго постоянным. Обычно такое поведение реализуется следующим образом.
void TN_TASK task_1 (void *param)
{
for (;;)
{
foo();
tn_task_sleep(FOO_PERIOD);
}
}
Возможна ситуация, когда длительность выполнения функции ''foo()'' может превышать системный тик - в этом случае периодичность нарушается. Способов решения такой проблемы несколько - например, дополнительная задача, освобождающая семафор с фиксированным периодом или использование объекта типа "таймер". Однако первое ведет к дополнительной трате ресурсов, а второе в TNKernel пока не реализовано.
Используя системное время проблему можно решить следующим образом:
void TN_TASK task_1 (void *param)
{
TN_SYS_TIM_T t;
for (;;)
{
t = tn_sys_time_get();
foo();
t = tn_sys_time_get() - t;
if (t < FOO_PERIOD)
tn_task_sleep(FOO_PERIOD - t);
else
tn_task_sleep(1);
}
}
~~UP~~
===== Системные сервисы =====
TNKernel имеет следующий набор системных сервисов:
^ Сервис ^ Описание ^ Свойства ^
| **Основные сервисы** |||
| \ \ [[tnkernel:ref:sys:tn_start_system|tn_start_system()]] | Запуск системы | до начала работы системы |
| \ \ [[tnkernel:ref:sys:tn_tick_int_processing|tn_tick_int_processing()]] | Обслуживание системного таймера | {{tnkernel:ref:attr_call_int.png|Разрешен вызов только в прерывании}} {{tnkernel:ref:attr_call_ct_sw.png|Может привести к переключению контекста}} |
| \ \ [[tnkernel:ref:sys:tn_sys_tslice_ticks|tn_sys_tslice_ticks()]] | Управление round-robin планированием | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} |
| \ \ [[tnkernel:ref:sys:tn_sys_context_get|tn_sys_context_get()]] | Получение текущего контекста системы | {{tnkernel:ref:attr_call_task_and_int.png|Разрешен вызов в контексте задачи и в прерывании}} |
| **Запрещение переключения контекста** |||
| \ \ [[tnkernel:ref:sys:tn_sys_enter_critical|tn_sys_enter_critical()]] | Вход в критическую секцию | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} |
| \ \ [[tnkernel:ref:sys:tn_sys_exit_critical|tn_sys_exit_critical()]] | Выход из критической секции | {{tnkernel:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} |
| **Системное время** |||
| \ \ [[tnkernel:ref:sys:tn_sys_time_get|tn_sys_time_get()]] | Получение системного времени | {{tnkernel:ref:attr_call_task_and_int.png|Разрешен вызов в контексте задачи и в прерывании}} |
| \ \ [[tnkernel:ref:sys:tn_sys_time_set|tn_sys_time_set()]] | Установка системного времени | {{tnkernel:ref:attr_call_task_and_int.png|Разрешен вызов в контексте задачи и в прерывании}} |
~~UP~~