我正在使用gcc为单处理器32位微控制器编写代码.
我需要从链表中消耗带时间戳的对象.代码的另一部分可以是异步的(可能在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 ... } } }
Arch D. Robi.. 5
C++ 11有一个标准功能:std::atomic_signal_fence
.C11具有类似的功能,没有名称空间限定符.如果您的程序仅使用单个线程并且您只是试图阻止编译器跨越围栏移动加载/存储,那么它是合适的.使用std::atomic_signal_fence(memory_order_acquire)
临界区之前和std:atomic_signal_fence(memory_order_release)
临界区之后.
如果您不使用C++ 11或C11,但只使用gcc或了解gcc asms的编译器,则可以使用__asm__ __volatile__ ("": : :"memory")
编译器屏障.asm说它不能被删除并且有可能以神秘的方式修改内存,因此编译器将无法在其上移动加载/存储.