Короткая предистория. Я портировал 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());
}
}
{
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();
}
}
{
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());
}
}
{
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();
}
}
{
byte NextPrty = GetHighPriority(ReadyProcessMap);
if(NextPrty != SchedProcPriority)
{
SchedProcPriority = NextPrty;
RaiseContextSwitch();
}
}
конечно в этом случае теряется смысл в названии SchedProcPriority но проблема решена. Прерывание переключающее задачи как и прежде исполняется но там переключения не происходит так как текущая и следующая задачи - это одно и то же.
Я не знаю как в других процессорах эта ситуация обыгрывается? В предыдущем проекте где мы использовали Xtensa был специальный обработчик прерывания для переключателя задач где другие прерывания не разрешались и такого шанса не возникало. Это довольно редкое стечение обстоятельств и трудно уловимый ведь следующий сигнал обработается без проблем.
Спасибо и удачи.