Системные сервисы не относятся напрямую к каким-либо объектом 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()
.
Функция запуска системы tn_start_system()
обеспечивает инициализацию всех внутренних структур RTOS, создание двух системных задач и первое переключение контекста на системную задачу таймера. Фукция tn_start_system()
вызывается только один раз и является функцией без возврата.
В порте TNKernel для контроллеров PIC24/dsPIC в функцию передается несколько параметров, позволяющих более гибко настроить систему (выбрать размеры стеков системных задач и т.п.). В оригинальной версии функция запуска параметров не имеет.
Диаграма запуска TNKernel изображена на рисунке:
Сервис запуска 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()
разрешается прерывание от системного таймера и после выхода из нее система начинает нормальное функционирование.
Для того чтобы реализовать функции ожидания или таймауты, в системе должен присутствовать таймер, доступный планировщику. Назовем такой таймер системным таймером. Как правило, период таймера постоянен на всем протяжении работы и это период называется системным тиком. Системный тик - это минимальная единица времени доступная планировщику. Все времена, таймауты указываются именно в системных тиках а не в абсолютных единицах времени.
Обычно системный таймер реализуется на основании одного из аппаратных таймеров микроконтроллера, или таймера, являющегося частью ядра. В обработчике прерывания от этого таймера необходимо вызвать функцию tn_tick_int_processing()
, которая и обеспечивает "ход" системного времени.
tn_tick_int_processing()
должна вызываться только в обработчике прерывания.
Round-Robin или карусельное планирование - это принцип переключения задач с одинаковым приоритетом при котором каждой задаче выделяется определенный квант времени (с дискретностью один системный тик). После того как задача отработает свой квант, планировщик запускает следующую в очереди готовых к выполнению задачу.
В TNKernel включен сервис tn_sys_tslice_ticks()
, позволяющий настраивать длительность кванта выполнения для каждого приоритета или отключать карусельное планирование.
Критическая секция это часть задачи, в которой осуществляется доступ к разделяемому ресурсу. Если один и тот же ресурс (например, глобальную переменную) используют две или более задач, критические секции называют конкурирующими. В этом случае необходимо защитить критическую секцию таким образом, чтобы доступ к ресурсу являлся атомарным.
Один из способов защиты критической секции - это использование мютекса. Однако часто мютекс является избыточным объектом для реализации критической секции и в этом случае используют парные функции запрещения и разрешения переключения контекста.
В 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()
могут быть полезны для выполнения относительно быстрых операций, для которых использование мютекса слишком избыточно. Примером такой операции может служить вывод в порт контроллера.
Под системным временем подразумевается беззнаковая целая переменная, инкрементируемая каждый системный тик. Значение переменной может быть получено путем вызова функции 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); } }
TNKernel имеет следующий набор системных сервисов:
Сервис | Описание | Свойства |
---|---|---|
Основные сервисы | ||
tn_start_system() | Запуск системы | до начала работы системы |
tn_tick_int_processing() | Обслуживание системного таймера | |
tn_sys_tslice_ticks() | Управление round-robin планированием | |
tn_sys_context_get() | Получение текущего контекста системы | |
Запрещение переключения контекста | ||
tn_sys_enter_critical() | Вход в критическую секцию | |
tn_sys_exit_critical() | Выход из критической секции | |
Системное время | ||
tn_sys_time_get() | Получение системного времени | |
tn_sys_time_set() | Установка системного времени |