Дан микроконтроллер, на котором выполняется следующий код:
volatile bool has_flag = false;
void interrupt(void) //called when an interrupt is received
{
clear_interrupt_flag(); //clear interrupt flag
has_flag = true; //signal that we have an interrupt to process
}
int main()
{
while(1)
{
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
Предположим, что if(has_flag)
условие оценивается как ложное, и мы собираемся выполнить инструкцию сна. Непосредственно перед тем, как выполнить инструкцию сна, мы получаем прерывание. После выхода из прерывания мы выполняем инструкцию сна.
Эта последовательность выполнения нежелательна, потому что:
- Микроконтроллер пошел спать вместо того, чтобы просыпаться и звонить
process()
. - Микроконтроллер может никогда не проснуться, если после этого не получено никакого прерывания.
- Звонок
process()
откладывается до следующего прерывания.
Как можно написать код, чтобы предотвратить возникновение этого состояния гонки?
редактировать
Некоторые микроконтроллеры, такие как ATMega, имеют бит включения режима сна, который предотвращает возникновение этого состояния (спасибо Kvegaoro за указание на это). JRoberts предлагает пример реализации, которая иллюстрирует это поведение.
Другие микросхемы, такие как PIC18, не имеют этого бита, и проблема все еще возникает. Однако эти микросхемы спроектированы таким образом, что прерывания могут по-прежнему пробуждать ядро независимо от того, установлен ли бит разрешения глобальных прерываний (спасибо суперкату за указание на это). Для таких архитектур решение заключается в отключении глобальных прерываний непосредственно перед сном. Если прерывание сработает прямо перед выполнением команды ожидания, обработчик прерываний не будет выполнен, ядро проснется, и как только глобальные прерывания будут повторно включены, обработчик прерываний будет выполнен. В псевдокоде реализация будет выглядеть так:
int main()
{
while(1)
{
//clear global interrupt enable bit.
//if the flag tested below is not set, then we enter
//sleep with the global interrupt bit cleared, which is
//the intended behavior.
disable_global_interrupts();
if(has_flag) //if we had an interrupt
{
has_flag = false; //clear the interrupt flag
enable_global_interrupts(); //set global interrupt enable bit.
process(); //process the interrupt
}
else
sleep(); //place the micro to sleep
}
}
interrupt_flag
как int
, и увеличивать его каждый раз, когда есть прерывание. Затем измените if(has_flag)
к while (interrupts_count)
и затем спать. Тем не менее, прерывание может произойти после того, как вы вышли из цикла while. Если это проблема, значит ли обработка в самом прерывании?