【问题标题】:Read and Write atomic operation implementation in the Linux KernelLinux内核中的读写原子操作实现
【发布时间】:2012-03-06 13:12:27
【问题描述】:

最近我窥探了原子读写的 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 类型用于强制转换技巧。任何人都知道这如何影响这里的原子性? (注意在写操作中不使用)

【问题讨论】:

    标签: c linux-kernel atomic volatile


    【解决方案1】:

    如果您为特定架构编写代码,则可以针对它做出特定假设。
    我猜 IA-64 确实会将这些东西编译成一条指令。

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

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

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

    【讨论】:

      【解决方案2】:

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

      volatile 阻止编译器在本地缓存该值,从而强制在可以访问它的地方检索它。

      【讨论】:

      • 在现代内核中,这里的volatile 通过一个名为READ_ONCE() / WRITE_ONCE 的宏来使用。我脑海中的解释是编译器在技术上被允许读取/写入值 multiple 次。例如。如果代码将读取的值复制到局部变量,然后在不同的地方使用。因此,我们必须将其描述为不仅仅是防止值在本地缓存。完整描述:lwn.net/Articles/508991
      【解决方案3】:

      我认为您在这里误解了“原子”和“易失性”一词的(非常模糊的)用法。原子只是真正意味着单词将被原子地读取或写入(在一个步骤中,并保证此内存位置的内容将始终是一个写入或另一个,而不是介于两者之间)。而volatile 关键字告诉编译器永远不要假设由于较早的读/写而位于该位置的数据(基本上,永远不要优化读取)。

      “原子”和“易失性”这两个词在这里并不意味着存在任何形式的内存同步。两者都不意味着任何读/写障碍或栅栏。关于内存和缓存的一致性,没有任何保证。这些功能基本上只是在软件层面是原子的,硬件可以优化/撒谎,但它认为合适。

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

      至于volatile,虽然没有必要一般除非你访问内存映射的IO,这完全取决于atomic_readatomic_write 正在调用宏。许多编译器(尽管它没有在 C 规范中设置)为 volatile 变量生成内存屏障/栅栏(GCC 就是其中之一。MSVC 肯定会这样做。)。虽然这通常意味着所有对该变量的读取/写入现在正式免除几乎任何编译器优化,在这种情况下,仅通过创建“虚拟”易失性变量这个特定的读/写实例是优化和重新排序的禁区。

      【讨论】:

      • 几乎(全部?)CPU 保证对自然对齐的普通内存(用于普通对象的内存)的任何字长内存主要操作(加载或存储)进行原子访问(在许多情况下也用于适合缓存行的大多数未对齐访问)。
      猜你喜欢
      • 1970-01-01
      • 2012-09-24
      • 2016-05-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-07-26
      • 2015-02-26
      • 1970-01-01
      相关资源
      最近更新 更多