Использование volatile
часто цитируется в качестве решения, но это не совсем верно. Он часто маскирует проблему, так как volatile
всегда будет делать код медленнее. Если ваше единственное использование показано так, то, возможно, будет работать volatile
.
Это, вероятно, лучше с одного читателя и одной записи использовать memory barriers.Это будет ваш код тогда,
Магистраль:
volatile int *p = &flag;
while (*p == false); /* You must use volatile if you poll */
flag = false;
asm volatile ("" : : : "memory"); /* gcc barrier */
ISR:
/* do something */
flag=true
asm volatile ("" : : : "memory"); /* gcc barrier */
Здесь барьер просто заставляет компилятор, чтобы сделать ARMstr
инструкции в этот момент. Оптимизатор не будет перемещать код до или после. Вы также можете использовать swp
или ldrex
и strex
в зависимости от вашего ARM CPU. Кроме того, кольцевые буферы часто используются с ISR и mainlines, поскольку они не нуждаются в какой-либо специальной поддержке процессора; только компилятор защита памяти.
См. lock-free и в частности поиск lock-free and arm.
Edit: Для добавления,
Есть ли способ, я не упущу прерывать вообще?
Это зависит от источника прерывания. Если это таймер, а вы знаете, источник таймера никогда не может быть быстрее, чем XX инструкции и никакие другие прерывания не активны в системе, тогда ваш текущий код будет работать. Однако, если прерывание происходит от внешнего источника, такого как контроллер Ethernet, неуязвимая клавиатура и т. Д. Возможно многократное прерывание. Несколько раз новые прерывания происходят даже во время обработчика прерываний. В зависимости от источника ISR существуют разные решения. A кольцевой буфер обычно используется для обработки рабочих элементов из ISR для основной линии. Для UART кольцо может содержать фактические символьные данные. Это может быть список указателей и т. Д. Трудно синхронизировать ISR от основной линии , когда сообщение становится более сложным; Поэтому я считаю, что ответ зависит от источника прерывания. Вот почему каждый OS имеет так много примитивов и инфраструктуры для этой проблемы.
Как барьеры памяти решают проблему, имеет ли это эффект, когда код работает на одном процессоре?
Недостатки памяти не полностью устраняют проблему пропущенного прерывания; как volatile
нет. Они просто делают окно намного меньше. Они вынуждают компилятор расписанию загрузить или сохранить ранее.Например, основной цикл линии,
1: ldr r0, [r1]
cmp r0, #0 ; xxx
bne 1b ; xxx
mov r0,#1 ; xxx
str r0, [r1]
Если второе прерывание происходит во время ххх линии, то ваш flag
должен быть установлен в два раза, и вы пропустили одно прерывание. Замки просто убедитесь, что компилятор размещает ldr
и str
рядом.
Какое ожидаемое поведение при использовании барьеров между различными контекстами?
Компилятор барьер памяти я показываю только делает компилятор делать вещи рано. Это не влияет на контекст. Существуют разные барьеры; но в основном они предназначены для многопроцессорных проектов.
Может ли спать в цикле while решить проблемы с синхронизацией?
Не совсем, это просто более эффективное использование. Инструкция ARM WFI
может временно остановить CPU, и это сэкономит электроэнергию. Обычно это sleep() делает на ARM. Я думаю, вам нужно изменить связь между ISR и магистраль , если это проблема. Это зависит от источника ISR.
Убедитесь, что 'flag' объявлен' volatile', иначе компилятор может оптимизировать 'while (флаг == false);' as 'while (true);' поскольку он, скорее всего, обнаружит, что 'flag' не может быть изменено изнутри функции 1. Пометка «volatile» сообщает компилятору, что переменная может быть изменена в любое время. (Так как система однопоточная, не требуется обязательная защита памяти.) – cdhowie