结构TsMyStruct作为参数提供给某些函数:
typedef struct
{
uint16_t inc1;
uint16_t inc2;
}TsMyStruct;
void func1(TsMyStruct* myStruct)
{
myStruct->inc1 += 1;
}
void func2(TsMyStruct* myStruct)
{
myStruct->inc1 += 2;
myStruct->inc2 += 3;
}
在非中断上下文中调用func1,在中断上下文下调用func2。 func2的调用堆栈有一个中断向量作为原点。 C编译器不知道可以调用func2(但代码不被视为"未使用"代码作为链接器在中断向量表内存部分需要它),因此一些代码读取myStruct-> inc2外部可以优化func2,防止myStruct-> inc2从ram重新加载。 C基本类型确实如此,但对于inc2结构成员或某些数组是否正确?这是函数参数吗?
作为一般规则,我可以说"在中断上下文中修改并在其他地方读取的每个内存区域(基本类型?或不?)必须声明为volatile"?
答案 0 :(得分:2)
是的,在中断处理程序内部和外部使用的任何内存都应该是volatile
,包括结构和数组,以及作为函数参数传递的指针。假设您的目标是单核设备,则无需额外同步。
但是,您必须考虑func1
可能在任何地方中断,如果您不小心,可能会导致结果不一致。例如,考虑一下:
void func1(volatile TsMyStruct* myStruct)
{
myStruct->inc1 += 1;
if (myStruct->inc1 == 4)
{
print(myStruct->inc1); // assume "print" exists
}
}
void func2(volatile TsMyStruct* myStruct)
{
myStruct->inc1 += 2;
myStruct->inc2 += 3;
}
由于中断是异步的,因此可以打印不同于4的数字。例如,如果func1
在检查后但在print
调用之前被中断,则会发生这种情况。
答案 1 :(得分:0)
没有。 volatile
还不够。您必须为编译器(可以为volatile
)和处理器设置优化障碍。例如。当CPU核心写入数据时,这可以进入某个缓存,而对于另一个核心则不可见。
通常,您需要在代码中锁定一些(自旋锁或互斥锁)。此类函数通常包含优化屏障,因此您不需要volatile
。
你的代码很活泼,正确的锁定看起来像是
void func1(TsMyStruct* myStruct)
{
lock();
myStruct->inc1 += 1;
unlock();
}
void func2(TsMyStruct* myStruct)
{
lock();
myStruct->inc1 += 2;
unlock();
myStruct->inc1 += 3;
}
并且lock()
+ unlock()
函数包含优化障碍(例如__asm__ __volatile__("" ::: "memory")
或仅调用全局函数),这将导致编译器重新加载myStruct
。< / p>
对于挑剔:lock()
和unlock()
应该做正确的事情(例如禁用irqs)。现实世界的实施将是例如linux中的spin_lock_irqsave()
+ spin_lock_irqrestore()
。