用户空间程序中的 "volatile" 是否表示存在错误?
Is it true that "volatile" in a userspace program tends to indicate a bug?
当我用谷歌搜索 "volatile" 及其用户 space 的用法时,我发现了 Theodore Tso 和 Linus Torvalds 之间的邮件。根据这些大师的说法,在用户space中使用"volatile"可能是一个错误??Check discussion here
虽然他们有一些解释,但我实在是看不懂。谁能用一些简单的语言解释他们为什么这么说?我们不应该在用户 space??
中使用 volatile
volatile
告诉编译器每次读写都有一个可观察到的副作用;因此,编译器无法假设连续两次读取或两次写入具有相同的效果。
例如,通常情况下,如下代码:
int a = *x;
int b = *x;
if (a == b)
printf("Hi!\n");
可以优化为:
printf("Hi!\n");
volatile
所做的是告诉编译器这些值可能来自程序控制之外的某个地方,因此它必须实际读取这些值并执行比较。
很多人都误以为可以用volatile
构建lock-free数据结构,让多线程共享值,可以观察效果其他线程中的那些值。
但是,volatile
没有说明不同线程如何交互,并且可以应用于可以在不同内核上缓存不同值的值,或者可以应用于不能以原子方式写入的值在单个操作中,因此如果您尝试使用 volatile
编写 multi-threaded 或 multi-core 代码,您可能 运行 会遇到很多问题。
相反,您需要使用锁或其他一些标准并发机制在线程之间进行通信,或者使用 memory barriers, or use C11/C++11 atomic types and atomic operations。锁确保整个代码区域对变量具有独占访问权限,如果您的值太大、太小或未对齐而无法在单个操作中以原子方式写入,则锁可以起作用,而内存屏障和原子类型和操作提供关于它们如何与 CPU 一起工作的保证,以确保缓存同步或读写以特定顺序发生。
基本上,当您与单个硬件寄存器交互时,volatile
最有用,它可以在程序控制之外变化,但可能不需要任何特殊的原子操作来访问。或者它可以在 signal handlers 中使用,因为线程可能会被中断,并且处理程序 运行,然后控制在同一线程内返回,您需要使用 volatile
值,如果你想向中断的代码传递一个标志。
但是如果您在线程之间进行任何类型的同步,您应该使用锁或标准库提供的其他一些并发原语,或者真正了解您在内存排序和使用内存方面所做的事情障碍或原子操作。
当我用谷歌搜索 "volatile" 及其用户 space 的用法时,我发现了 Theodore Tso 和 Linus Torvalds 之间的邮件。根据这些大师的说法,在用户space中使用"volatile"可能是一个错误??Check discussion here
虽然他们有一些解释,但我实在是看不懂。谁能用一些简单的语言解释他们为什么这么说?我们不应该在用户 space??
中使用 volatilevolatile
告诉编译器每次读写都有一个可观察到的副作用;因此,编译器无法假设连续两次读取或两次写入具有相同的效果。
例如,通常情况下,如下代码:
int a = *x;
int b = *x;
if (a == b)
printf("Hi!\n");
可以优化为:
printf("Hi!\n");
volatile
所做的是告诉编译器这些值可能来自程序控制之外的某个地方,因此它必须实际读取这些值并执行比较。
很多人都误以为可以用volatile
构建lock-free数据结构,让多线程共享值,可以观察效果其他线程中的那些值。
但是,volatile
没有说明不同线程如何交互,并且可以应用于可以在不同内核上缓存不同值的值,或者可以应用于不能以原子方式写入的值在单个操作中,因此如果您尝试使用 volatile
编写 multi-threaded 或 multi-core 代码,您可能 运行 会遇到很多问题。
相反,您需要使用锁或其他一些标准并发机制在线程之间进行通信,或者使用 memory barriers, or use C11/C++11 atomic types and atomic operations。锁确保整个代码区域对变量具有独占访问权限,如果您的值太大、太小或未对齐而无法在单个操作中以原子方式写入,则锁可以起作用,而内存屏障和原子类型和操作提供关于它们如何与 CPU 一起工作的保证,以确保缓存同步或读写以特定顺序发生。
基本上,当您与单个硬件寄存器交互时,volatile
最有用,它可以在程序控制之外变化,但可能不需要任何特殊的原子操作来访问。或者它可以在 signal handlers 中使用,因为线程可能会被中断,并且处理程序 运行,然后控制在同一线程内返回,您需要使用 volatile
值,如果你想向中断的代码传递一个标志。
但是如果您在线程之间进行任何类型的同步,您应该使用锁或标准库提供的其他一些并发原语,或者真正了解您在内存排序和使用内存方面所做的事情障碍或原子操作。