====== 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~~