Дано мікроконтролер, на якому працює наступний код:
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. Якщо це проблема, то чи робиться обробка в самому перериванні?