Перейти к содержанию
    

Переключение задач по прерыванию.

Недвно мы натолкнулись на проблему которую хотелось бы обсудить и услышать мнения других о нашем пути ее решения.

Короткая предистория. Я портировал scmRTOS на ARM926 у которого есть векторный контроллер прерываний с несколькими уровнями приорететов. Для переключения задач выбрал прерывание с самым низким приоритетом (полностью поддерживаю Сергея Б в его аргументации об этом) для того чтобы избежать излишних переключений задач. Все замечательно работало до тех пор пока мы не начали стресс тест, продувая через систему много и быстро. Мы заметели что иногда одна высоко приорететная задача "как бы" теряла сигнал и пропускала критичное по времени действие. Это икание случалось пару раз за час интенсивной работы. После ползания на пузе с микроскопом оказалось что происходит следующее:

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

 

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

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

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

 

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

 

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

 

Проблему решили просто заменив CurProcPriority на SchedProcPriority и добавив начальную инициализацию

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

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

 

и

 

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

 

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

 

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

 

Спасибо и удачи.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Разрешать прерывания во время переключения контекстов - идеологически неверно и чревато неочевидными ошибками. Разрешение переключения контекста по прерыванию должно касаться исключительно специализированного прерывания, где производится переключение контекстов.

конечно в этом случае теряется смысл в названии SchedProcPriority но проблема решена.

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

 

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

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

 

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

Это было правильно.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Поясню, вероятно я не ясно изложил.

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

 

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

 

Разрешать прерывания во время переключения контекстов - идеологически неверно и чревато неочевидными ошибками.

 

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

 

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

Таким образом для выполнение КАЖДОГО прерывания это повторялось. Для ARM все наоборот - очень компактно и нет лишних комманд. А это очень важно для высоко скоростных систем где каждый цикл на счету и кстати иметь вложенные прерывания очень важно так как это уменьшает джиттер в обработке.

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

На мой взгляд есть две разные вещи:

 

1. Вложенные прерывания.

2. Переключатель задач.

 

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...