2013-08-06 1 views
3

Предположим, у меня есть function 1 и isr routine, обе делятся и обновляют один и тот же флаг без какой-либо блокировки между ними. система однопоточная.Как защитить глобальную переменную, разделяемую isr и регулярной функцией?

в то время как будет инструкция по сборке на 3 руки, что означает, что это не атомная операция, нормально ли использовать глобальную переменную между функциями non isr и isr ​​без какой-либо блокировки или защиты?

функция 1:

while (flag == false); 
flag = false; 

ISR рутина:

do something 
flag=true 

Я не помню, есть механизм ядра Linux для блокировки между sleepable и, не sleepable контексте например irq и kernel thread.


Спасибо @artless за его ответ здесь некоторые вопросы, я не уверен:

  1. Есть ли способ, я не упущу прерывать вообще?

  2. Как барьеры памяти решают проблему, имеет ли это эффект, когда код работает на одном процессоре?

  3. Каково ожидаемое поведение при использовании барьеров между различными контекстами?

  4. Может спать в то время как loop может решить проблемы с синхронизацией?

+4

Убедитесь, что 'flag' объявлен' volatile', иначе компилятор может оптимизировать 'while (флаг == false);' as 'while (true);' поскольку он, скорее всего, обнаружит, что 'flag' не может быть изменено изнутри функции 1. Пометка «volatile» сообщает компилятору, что переменная может быть изменена в любое время. (Так как система однопоточная, не требуется обязательная защита памяти.) – cdhowie

ответ

7

Использование 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 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.

+1

[Ограничения памяти Linux] (https://www.kernel.org/doc/Documentation/memory-barriers.txt) дает хороший пример различных типы проблем, с которыми вы можете столкнуться при синхронизации. Например, ваша текущая структура всегда может пропустить прерывание. Т.е. между опросом и сбросом флага на «false» может произойти другое прерывание. –

+0

Обновлен мой вопрос и нашел хорошую ссылку на руку и память: http://engenuics.com/mpg/notes/notes_mpgl1_chapter6.pdf – 0x90

+0

еще раз спасибо за ваши замечательные ответы. Так что в принципе нет 100% метода, который я могу использовать и не пропущу никакого прерывания? источником прерывания может быть UART/USB, который является внешним, можете ли вы проконсультироваться с каким механизмом я могу это рассмотреть? – 0x90

-1

Да. Если это однопоточная модель, нет необходимости в блокировке.

0

Это будет лучше, если вы можете объявить флаг как: volatile int flag; или volatile bool flag;

Смежные вопросы