====== OSA : Критичекие секции ======
===== Введение =====
Критическая секция в программе предназначена для захвата одной задачей какого-либо ресурса, если к этому ресурсу имеют доступ другие задачи. Таким ресурсом может быть, например, модуль периферии, внешняя память, наконец, процессорное время.
Вообще-то, в кооперативной RTOS редко встречаются конфликтые ситуации, т.к. передачей управления планировщику (а следовательно, и остальным задачам, осуществляется вручную), тем не менее такие ситуации возможны. Поэтому в OSA реализована возможность защиты критических секций.
Для того, чтобы пользоваться сервисами работы с критическими секциями, в ##[[osa:ref:appendix:configuration|OSAcfg.h]]## нужно определить константу ##[[osa:ref:appendix:configuration|OS_ENABLE_CRITICAL_SECTION]]##
Для защиты критических секций есть два сервиса:
##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## - вход в критическую секцию и ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## - выход из критической секции.
При входе в критическую секцию запрещаются все прерывания (их текущее состояние запоминается во внутренних системных флагах), а задача, вызвавшая сервис, становится единственной готовой к выполнению, вне зависимости от ее приоритета. При этом допустима передача управления планировщику.
Входя в критическую секцию, нужно помнить некоторые правила, описанные ниже.
~~UP~~
===== Сохранение текущего состояния флагов GIEx =====
Текущее состояние флагов GIEx сохраняется во внутреннюю переменную системы. После чего флаги GIEx сбрасываются. Поэтому, если два раза подряд вызвать сервис ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##, то в первый раз она сохранит текущее состояние влагов GIEx, а во второй - нулевое состояние, т.е. на момент второго вызова флаги уже были сброшены. В этом случае сервис ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## восстановит флаги, сохраненные во втором вызове, т.е. нулевые. Поэтому следует избегать двойного вызова сервисы входа в критическую секцию.
~~UP~~
===== Системный таймер в критической секции =====
Использовать задержки ##[[osa:ref:allservices:OS_Delay|OS_Delay]]##, таймеры, а также ожидать события с таймаутами можно только в том случае, если вызов сервиса ##[[osa:ref:allservices:OS_Timer|OS_Timer]]## находится вне прерывания, иначе система будет ждать до бесконечности. (Либо после входа в критическую секцию разрешать прерывания сервисом ##[[osa:ref:allservices:OS_EI|OS_EI]]##, если логика программы позволяет в данной ситуации оставить прерывания.) Избежать этого можно, продублировав вызов ##[[osa:ref:allservices:OS_Timer|OS_Timer]]## в основном цикле (там же, где вызывается ##[[osa:ref:allservices:OS_Sched|OS_Sched]]##).
void interrupt int_routine (void)
{
OS_EnterInt();
if (TMR2IF) // Эта часть программы не отработает в
{ // критической секции
TMR2IF = 0;
OS_Timer(); // Этот вызов обрабатывается вне критической секции
}
OS_LeaveInt();
}
void Task1 (void)
{
for (;;) {
. . .
OS_EnterCriticalSection();
OS_Delay(100);
OS_LeaveCriticalSection();
. . .
}
}
void main (void)
{
OS_Init();
OS_Task_Create(0, Task1);
for (;;)
{
OS_Sched();
if (OS_IsInCriticalSection()) {
if (TMR2IF) {
TMR2IF = 0;
OS_Timer(); // Этот вызов обрабатывается в критической секции
}
}
}
}
~~UP~~
===== Ожидание событий в критической секции =====
Все задачи, кроме той, которая вызвала сервис ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]##(), блокируются, даже если их приоритет выше и они готовы к выполнению. Поэтому, если после перехода в критическую секцию мы начинаем ждать какое-то событие, которое устанавливается в другой задаче (например, ждем освобождение ресурса, который занят другой задачей), то мы его никогда не дождемся.
Избежать этого можно, если сначала дождаться события, а затем войти в критическую секцию.
void Task1 (void)
{
for (;;) {
. . .
// Неправильный подход
OS_EnterCriticalSection();
OS_Bsem_Wait(BS_USART_FREE);
. . .
// Правильный подход
OS_Bsem_Wait(BS_USART_FREE);
OS_EnterCriticalSection();
. . .
}
}
~~UP~~
===== Сервисы для критических секций =====
^ Сервис ^ Аргументы ^ Описание ^ Свойства ^
| ''bool ''\\ ##[[osa:ref:allservices:OS_IsInCriticalSection|OS_IsInCriticalSection]]## | '''' | Возвращает 1, если одна из задач находится в критической секции. | |
| ##[[osa:ref:allservices:OS_EnterCriticalSection|OS_EnterCriticalSection]]## | '''' | Вход в критическую секцию | {{osa:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} |
| ##[[osa:ref:allservices:OS_LeaveCriticalSection|OS_LeaveCriticalSection]]## | '''' | Выход из критической секции | {{osa:ref:attr_call_task.png|Разрешен вызов только в контексте задачи}} |
~~UP~~