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