在Linux内核中读写原子操作实现

时间:2012-02-15 07:06:33

标签: c linux-kernel atomic volatile

最近我偷看了原子读写的Linux内核实现,并提出了一些问题。

首先是ia64架构的相关代码:

typedef struct {
    int counter;
} atomic_t;

#define atomic_read(v)      (*(volatile int *)&(v)->counter)
#define atomic64_read(v)    (*(volatile long *)&(v)->counter)

#define atomic_set(v,i)     (((v)->counter) = (i))
#define atomic64_set(v,i)   (((v)->counter) = (i))
  1. 对于读取和写入操作,似乎采用直接方法来读取或写入变量。除非在某处有另一个技巧,否则我不明白这个操作在汇编域中是否具有原子性的保证。我想一个明显的答案是,这样的操作转换为一个汇编操作码,但即便如此,在考虑不同的内存缓存级别(或其他优化)时如何保证?

  2. 在读取宏上,volatile类型用于转换技巧。任何人都知道这会如何影响这里的原子性? (注意,它不用于写操作)

3 个答案:

答案 0 :(得分:14)

我认为你误解了(非常含糊)使用“原子”和“易变”这个词。 Atomic只是意味着单词将以原子方式读取或写入(一步完成,并保证此存储器位置的内容始终为一个写入或另一个,而不是介于两者之间)。并且volatile关键字告诉编译器由于先前的读/写(从根本上说,从不优化掉读取),从不假设该位置的数据。

“原子”和“易变”这两个词在这里并不意味着存在任何形式的内存同步。既不暗示任何读/写障碍或围栏。关于内存和缓存一致性,没有任何保证。这些函数在软件级别基本上只是原子函数,硬件可以根据它认为合适来优化/说谎。

现在,为什么简单阅读就足够了:每个架构的内存模型都不同。许多体系结构可以保证对与某个字节偏移量对齐的数据进行原子读取或写入,或者长度为x个字等,并且因CPU而异。 Linux内核包含许多不同体系结构的定义,这些体系允许它在没有任何原子调用的情况下(基本上CMPXCHG)在保证的平台上(有时甚至仅在实践中,即使实际上他们的规范说实际上并不保证)原子读/写。

对于volatile,虽然除非你正在访问内存映射的IO,否则不需要一般,这一切都取决于{{何时/何地/为何正在调用1}}和atomic_read个宏。许多编译器(虽然它没有在C规范中设置)为volatile变量生成内存屏障/栅栏(GCC,在我的头顶,是一个.MSVC肯定会这样做。)。虽然这通常意味着所有对此变量的读/写现在正式免于任何编译器优化,在这种情况下只创建一个“虚拟”volatile变量这个特定的读/写实例是优化和重新排序的禁区。

答案 1 :(得分:3)

读取在大多数主要体系结构上都是原子的,只要它们与大小的倍数对齐(并且不大于给定类型的读取大小),请参阅英特尔体系结构手册。另一方面,写入许多不同,英特尔声称在x86下,单字节写入和对齐写入可能是原子的,在IPF(IA64)下,所有内容都使用获取和释放语义,这将使其保证原子性,请参阅{{3} }。

volatile阻止编译器在本地缓存该值,强制它在有权访问它的地方进行检索。

答案 2 :(得分:2)

如果您为特定架构编写,则可以对其进行特定的假设 我想IA-64确实将这些东西编译成一条指令。

缓存不应该是一个问题,除非计数器跨越缓存行边界。但如果需要4/8字节对齐,则不会发生这种情况。

当机器指令转换为两次存储器访问时,需要“真正的”原子指令。这是增量(读取,增量,写入)或比较和交换的情况。

volatile会影响编译器可以执行的优化 例如,它阻止编译器将多个读取转换为一个读取 但是在机器指令级别上,它什么也没做。