Я пишу код для одного процессора 32-битного микроконтроллера с использованием gcc.Нужно ли использовать ключевое слово volatile для доступа к памяти в критическом разделе?
Мне нужно использовать объекты с меткой времени из связанного списка. Другая часть кода, которая может быть асинхронной (возможно, в ISR), добавляет их в список.
Критическая секция выполнена путем отключения прерываний и использования функции barrier()
.
Я смущен, когда gcc-оптимизация может сломать мой код путем кэширования указателей на элементы списка (следующий последний элемент для удаления, список заголовков или бесплатный список). Я не хочу, чтобы внутри цикла while было кэшировано предыдущее время цикла. Будет ли барьер памяти защищать меня от компилятора, решив загрузить указатель один раз в начале функции и не перезагружать его снова? Все эти указатели списка могут быть изменены в критическом разделе кода производителя (не показано). Я пытаюсь понять, если, например, pqueue_first
должен быть изменчивым указателем.
Предположительно, если не было цикла (что подходит для добавления в список), я в порядке, если весь код в функции находится в критическом разделе?
Пожалуйста, не просто указывайте мне на какую-то общую статью о нестабильных или критических разделах, потому что я прочитал их кучу, но у меня возникли проблемы с тем, как применить его к этому конкретному коду. Я понимаю, что volatile гарантирует, что компилятор будет перезагружать переменную каждый раз, когда она ссылается. Но я не понимаю вероятного объема оптимизации и ее взаимодействия с барьерами памяти.
typedef struct {
EV_EventQueueEntry_t *pqueue_alloc; // allocation (never changes)
EV_EventQueueEntry_t *pqueue_head; // head of active queue (ISR can change it)
EV_EventQueueEntry_t *pqueue_free; // head of free list (ISR can change it)
EV_EventQueueEntry_t *pqueue_first; // soonest item in queue (ISR can change it)
EV_EventQueueEntry_t *pqueue_first_prev; // back pointer from soonest item (ISR can change it)
EV_UInt_t max_event_count;
} EV_EventQueue_t;
void RunLoop(EV_EventQueue_t *pev)
{
while(not timeout)
{
// Enter critical section
disable_interrupts();
barrier();
// item with most recent timestamp
// this can be changed by ISR add to queue operation
EV_EventQueueEntry_t *pfirst = pev->pqueue_first;
if(pfirst!=NULL && EV_PortIsFutureTime(pfirst->event.timestamp, EV_PortGetTime()))
{
// Copy out message
EV_Event_t e = pfirst->event;
// Remove event from queue
if(pev->pqueue_first_prev != NULL)
pev->pqueue_first_prev->pnext = pfirst->pnext;
else
pev->pqueue_head = pfirst->pnext;
// Put event back on free list
pfirst->pnext = pev->pqueue_free;
pev->pqueue_free = pfirst;
pfirst->event.message.type = EV_MESSAGE_NULL;
// Find next soonest message to process after this one
pev->pqueue_first = ...;
pev->pqueue_first_prev = ...; // back pointer
// Exit critical section
barrier();
enable_interrupts();
// Dispatch message
...
}
else
{
// Exit critical section
barrier();
enable_interrupts();
// waste some time
...
}
}
}
Первый вопрос, который я задал бы, заключается в том, определена ли функция «барьер»/стандартизована для всех процессоров и операционных систем. Если нет, то второй вопрос: где находится официальная документация для конкретного процессора и операционной системы, которые вы используете. – user3386109
Функция барьера выводит '__asm__ __volatile__ (" ":::" memory ")', как в ответе ниже. Это AVR32. – Robotbugs
Примечание: долгое время выглядит в пределах барьера. – chux