识别干扰的可能性
Identifying possibility of interference
在多任务环境中。如果任务具有表达式 y = x + x;
是否有可能在 x 的两次读取之间发生中断(任务切换)。
这里是针对类似 x86 架构的特定答案,与传统调度程序 (https://en.wikipedia.org/wiki/Completely_Fair_Scheduler) 一起使用。看看这个 asm 代码,由 gcc 为 x86 生成,优化设置为 -OS0。
int main(void) {
int y;
int x = 5;
y = x + x;
}
转为
main:
pushq %rbp
movq %rsp, %rbp
movl , -4(%rbp)
movl -4(%rbp), %eax #read x
addl %eax, %eax #execute x + x
movl %eax, -8(%rbp)
movl [=11=], %eax
popq %rbp
ret
如您所见,x 仅被读取一次,这意味着即使有任务切换,也不会对 x 进行第二次读取。
编辑:
EOL 在评论中指出 x
确实可以读取两次,修改上面的代码会产生以下结果:
volatile x = 5;
强制编译器插入两个 movl 操作(而不是一个):
movl -8(%rbp), %edx
movl -8(%rbp), %eax
虽然这并没有使我的第一个猜测错误。编译器将优化变量的读取访问(如果它是简单类型)并删除第二次读取。 volatile 关键字强制编译器访问寄存器中的最新值。第一个代码不会强制读取 - 因此在线程切换和修改 x
.
的情况下给出其他结果
在大多数情况下,编译器生成的代码只会读取 x 一次,因为优化是微不足道的。事实上,它甚至可能将运算转换为右移,相当于 x << 1
。但是你不能保证,如果 x
被声明为 volatile
它必然会进行两次读取,然后在读取低阶和高阶单词之间是不间断的(反之亦然),并且在这种情况下,y
的分配也是可中断的。
另一个问题是,如果 x
不是原子数据类型(即无法在单个指令中读取),例如 16 位目标上的 32 位类型,那么即使它如果是单次读取,它可能是可中断的。
在任何一种情况下,如果 x
本身(或第二种情况下的 y
)在上下文之间共享(在这种情况下也应声明为 volatile
所以需要两次读取),或者如果由于某种原因,分配的时间在某种程度上是关键的并且需要完全确定(不太可能)。
如果 x
是共享的,因此 volatile
是原子类型,这个例子 中的简单解决方案是写表达式 y = x << 1
或 y = x * 2
以确保 x
只读一次。 x
不是原子的 - 您可能需要锁定调度程序或禁用中断(即使用 临界区 ),或者更有选择性地使用互斥锁保护访问。对于无法将表达式简化为对共享变量的单个引用的更复杂的表达式,只需将变量分配给非共享局部临时变量即可确保它只被读取一次。原子性问题依然存在
在多任务环境中。如果任务具有表达式 y = x + x;
是否有可能在 x 的两次读取之间发生中断(任务切换)。
这里是针对类似 x86 架构的特定答案,与传统调度程序 (https://en.wikipedia.org/wiki/Completely_Fair_Scheduler) 一起使用。看看这个 asm 代码,由 gcc 为 x86 生成,优化设置为 -OS0。
int main(void) {
int y;
int x = 5;
y = x + x;
}
转为
main:
pushq %rbp
movq %rsp, %rbp
movl , -4(%rbp)
movl -4(%rbp), %eax #read x
addl %eax, %eax #execute x + x
movl %eax, -8(%rbp)
movl [=11=], %eax
popq %rbp
ret
如您所见,x 仅被读取一次,这意味着即使有任务切换,也不会对 x 进行第二次读取。
编辑:
EOL 在评论中指出 x
确实可以读取两次,修改上面的代码会产生以下结果:
volatile x = 5;
强制编译器插入两个 movl 操作(而不是一个):
movl -8(%rbp), %edx
movl -8(%rbp), %eax
虽然这并没有使我的第一个猜测错误。编译器将优化变量的读取访问(如果它是简单类型)并删除第二次读取。 volatile 关键字强制编译器访问寄存器中的最新值。第一个代码不会强制读取 - 因此在线程切换和修改 x
.
在大多数情况下,编译器生成的代码只会读取 x 一次,因为优化是微不足道的。事实上,它甚至可能将运算转换为右移,相当于 x << 1
。但是你不能保证,如果 x
被声明为 volatile
它必然会进行两次读取,然后在读取低阶和高阶单词之间是不间断的(反之亦然),并且在这种情况下,y
的分配也是可中断的。
另一个问题是,如果 x
不是原子数据类型(即无法在单个指令中读取),例如 16 位目标上的 32 位类型,那么即使它如果是单次读取,它可能是可中断的。
在任何一种情况下,如果 x
本身(或第二种情况下的 y
)在上下文之间共享(在这种情况下也应声明为 volatile
所以需要两次读取),或者如果由于某种原因,分配的时间在某种程度上是关键的并且需要完全确定(不太可能)。
如果 x
是共享的,因此 volatile
是原子类型,这个例子 中的简单解决方案是写表达式 y = x << 1
或 y = x * 2
以确保 x
只读一次。 x
不是原子的 - 您可能需要锁定调度程序或禁用中断(即使用 临界区 ),或者更有选择性地使用互斥锁保护访问。对于无法将表达式简化为对共享变量的单个引用的更复杂的表达式,只需将变量分配给非共享局部临时变量即可确保它只被读取一次。原子性问题依然存在