Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Переключение задач по прерыванию.
Форум разработчиков электроники ELECTRONIX.ru > Cистемный уровень проектирования > Операционные системы > scmRTOS
dsp4us
Недвно мы натолкнулись на проблему которую хотелось бы обсудить и услышать мнения других о нашем пути ее решения.
Короткая предистория. Я портировал scmRTOS на ARM926 у которого есть векторный контроллер прерываний с несколькими уровнями приорететов. Для переключения задач выбрал прерывание с самым низким приоритетом (полностью поддерживаю Сергея Б в его аргументации об этом) для того чтобы избежать излишних переключений задач. Все замечательно работало до тех пор пока мы не начали стресс тест, продувая через систему много и быстро. Мы заметели что иногда одна высоко приорететная задача "как бы" теряла сигнал и пропускала критичное по времени действие. Это икание случалось пару раз за час интенсивной работы. После ползания на пузе с микроскопом оказалось что происходит следующее:
Эта высоко приорететная задача в какой то момент начинала уходить в ожидание этого сигнала и вызывала scheduler. Процесс уже в критической секции входил в функцию и далее в if(NextPrty != CurProcPriority)

CODE
void TKernel::Sched()
{
byte NextPrty = GetHighPriority(ReadyProcessMap);
if(NextPrty != CurProcPriority)
{
SchedProcPriority = NextPrty;

RaiseContextSwitch();
do
{
EnableContextSwitch();
DUMMY_INSTR();
DisableContextSwitch();
}
while(!IsContextSwitchDone());
}
}

EnableContextSwitch() в моем случае реализован как разрешение всех прерываний. В тот же момент происходило прерывание которое и посылало этот ожидаемый сигнал. Процессор уходил в обработчик прерываний где это высоко приоритетное прерывание вызывало перепланировщик тоже

CODE
void OS::TKernel::SchedISR()
{
byte NextPrty = GetHighPriority(ReadyProcessMap);
if(NextPrty != CurProcPriority)
{
SchedProcPriority = NextPrty;
RaiseContextSwitch();
}
}


но поскольку CurProcPriority еще оставался прежним то в if(NextPrty != CurProcPriority) ничего не происходило. А завершив это прерывание процессор попадал в прерывание переключения задач (мы его подняли только что сами) и благополучно уходил как правило в спячку игнорируя факт что мы должы бы были остаться в текущей задаче.

Проблему решили просто заменив CurProcPriority на SchedProcPriority и добавив начальную инициализацию
CODE
void TKernel::Sched()
{
byte NextPrty = GetHighPriority(ReadyProcessMap);
if(NextPrty != SchedProcPriority)
{
SchedProcPriority = NextPrty;

RaiseContextSwitch();
do
{
EnableContextSwitch();
__nop();
DisableContextSwitch();
}
while(!IsContextSwitchDone());
}
}


и

CODE
void OS::TKernel::SchedISR()
{
byte NextPrty = GetHighPriority(ReadyProcessMap);
if(NextPrty != SchedProcPriority)
{
SchedProcPriority = NextPrty;
RaiseContextSwitch();
}
}


конечно в этом случае теряется смысл в названии SchedProcPriority но проблема решена. Прерывание переключающее задачи как и прежде исполняется но там переключения не происходит так как текущая и следующая задачи - это одно и то же.

Я не знаю как в других процессорах эта ситуация обыгрывается? В предыдущем проекте где мы использовали Xtensa был специальный обработчик прерывания для переключателя задач где другие прерывания не разрешались и такого шанса не возникало. Это довольно редкое стечение обстоятельств и трудно уловимый ведь следующий сигнал обработается без проблем.

Спасибо и удачи.
dxp
Разрешать прерывания во время переключения контекстов - идеологически неверно и чревато неочевидными ошибками. Разрешение переключения контекста по прерыванию должно касаться исключительно специализированного прерывания, где производится переключение контекстов.
Цитата(dsp4us @ Jun 11 2010, 14:02) *
конечно в этом случае теряется смысл в названии SchedProcPriority но проблема решена.

Вы уверены, что данный ход решает проблему окончательно, а не является "заплаткой"?

Цитата(dsp4us @ Jun 11 2010, 14:02) *
Прерывание переключающее задачи как и прежде исполняется но там переключения не происходит так как текущая и следующая задачи - это одно и то же.

А если там будет активно другое прерывание, которое будет активировать другой процесс? Что будет в этом случае?

Цитата(dsp4us @ Jun 11 2010, 14:02) *
Я не знаю как в других процессорах эта ситуация обыгрывается? В предыдущем проекте где мы использовали Xtensa был специальный обработчик прерывания для переключателя задач где другие прерывания не разрешались и такого шанса не возникало.

Это было правильно.
dsp4us
Поясню, вероятно я не ясно изложил.
В ядре ARM9 как в прочем и в ARM7 нет штатного контроллера прерывания. Там по спецификации сделано до безобразия просто - как только линия прерывания (IRQ например) дергается ядро перескакивает на адрес (0x18) глобально (в самом ядре) запретив при этом (IRQ) прерывания. А дальше все зависит от конкретного изготовителя процессора, какой контроллер прерываний он подцепил. В моем случае это векторный с 3 уровнями приорететов - чем то похожий на Atmel-ий.

Наиболее распространненый вариант следующего шага - программа переходит в обработчик прерываний высокого уровня - что то на подобие диспечера прерываний/обертки - (как вариант IRQHandler в прортах). Там уже вычисляется конкреный адресс конкретного прерывания и передается управление (IRQ_SWITCH макро). В scm портах для ARM на данный момент прерывание в этом диспечере не разрешается (MSR CPSR_c, #(NIRQ | MODE_ххх). Я написал свой IRQHandler где в определенный момент разрешаю глобально прерывания при этом уже контроллер маскирует тот уровень приоретета который в данный момент обрабатывается. Таким образом если произойдет прерывание с большим приорететом то он дернет линию IRQ и все повториться (вложение прирываний).

Цитата(dxp @ Jun 11 2010, 05:04) *
Разрешать прерывания во время переключения контекстов - идеологически неверно и чревато неочевидными ошибками.


Полностью согласен. В моем варианте ContextSwitcher_ISR первым же делом я запрещаю глобольно все прерывания. Проблема которую я описал выше как раз происходит с момента когда в общем диспечере прерываний разрешаются прерывания высого уровня (переключатель задач имеет самый низкий приоретет) и до момента когда уже в ContextSwitcher_ISR происходит этот запрет (от силы 10-20 ассемблерных комманд).

Для Xtensa диспечер проверял в самом начале если это прерывание переключателя и далее ответвлялся если да. Если нет то продолжал как обычно - разрешал прерывания и т.д. По окончании обработки прерывания глобально запрещались, опять проверялось если прерывание для переключения ждет.... Короче все было довольно коряво. Но это по большей части из-за "уникальности" процессора Xtensa (чтоб они побыстрее обанкротились уже и не морочили людям головы).
Таким образом для выполнение КАЖДОГО прерывания это повторялось. Для ARM все наоборот - очень компактно и нет лишних комманд. А это очень важно для высоко скоростных систем где каждый цикл на счету и кстати иметь вложенные прерывания очень важно так как это уменьшает джиттер в обработке.

К сожалению у меня нет под рукой ни одной платы с обычными распространнеными ARM9/ARM7 (есть только с AT91CAP9) для того чтобы доработать порт в scmRTOS для вложенных прерываний. Если кому интересно могу запостить код диспечера и переключателя задач.
sergeeff
На мой взгляд есть две разные вещи:

1. Вложенные прерывания.
2. Переключатель задач.

Во всех реализациях, которые я пока встречал в обработчике прерываний сделана защита от перепланировки задач во внутренних прерываниях (хотя я не видел ни одного вложенного обработчика прерываний в RTOS на ARM) за счет введения переменной ISR_NestCount.

В моих приложениях мне нужны вложенные прерывания и я их в scmRTOS реализовал именно используя такой метод защиты от перепланировки для at91rm9200. Если кому интересно, могу выложить. Сейчас активно все это тестирую.
dsp4us
Обработчик прерывания для перключения задач является обычной С-callable функцией только написанной на ассембли и вызывается той же самой командой в диспечере прерываний как и обработчики других прерываний.

В моем варианте (когда переключатель задач работает на прерывании самого низкого приоритета ) наличие переменной ISR_NestCount вобщем то не нужно. ISR_NestCount предохраняет от того чтобы во вложенных прерываниях не поднималось прерывание переключателя каждый раз когда заканчивается очередная функция обработчика. Но на самом деле контроллер прерываний не имеет памяти и одно и тоже прерывание произошедшее несколько раз до момента начала его обработки (когда контроллер сделал это прерывание текущим) зарегестрируется только один раз а остатьные просто теряются. По крайней мере так работают все контроллеры прерываний которыя я встречал. А реально "регистрация" прерывания переключателя произойдет только когда произойдет разрешение (уберется маска) для самого низкого уровня приоритева, то есть в самом конце всех вложений.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.