获取信号量必须是原子的。平托斯的sema_down安全吗?



这段代码来自Pintos源码:https://www.cs.usfca.edu/本森/cs326/平托一家都/平托一家都/src/线程/synch.c

void
sema_down (struct semaphore *sema) 
{
  enum intr_level old_level;
  ASSERT (sema != NULL);
  ASSERT (!intr_context ());
  old_level = intr_disable ();
  while (sema->value == 0) 
    {
      list_push_back (&sema->waiters, &thread_current ()->elem);
      thread_block ();
    }
  sema->value--;
  intr_set_level (old_level);
}

获取信号量的事实是sema->value--;。如果它工作,它一定是一个原子操作。我们怎么知道它实际上是原子操作呢?我知道现代CPU保证对齐内存操作(对于字/双字/四字-这取决于)是原子的。但是,在这里,我不相信为什么它是原子的。

TL:DR:如果您在UP系统上禁用中断,只要您不计算使用DMA观察内存的系统设备,那么任何事情都是原子的。

注意操作周围的intr_disable ();/intr_set_level (old_level);


现代CPU保证对齐内存操作是原子的

对于多线程观察者,这只适用于单独的加载存储,而不是读-修改-写操作。


对于原子的东西,我们必须考虑我们关心的潜在观察者。重要的是,没有任何可以观察到操作已经部分发生。实现这一目标的最直接的方法是使操作在物理/电气上是瞬时的,并同时影响所有比特(例如,并行总线上的负载或存储从未开始到完成在时钟周期的边界,所以它是原子的"免费的";直到平行总线的宽度)。这对于读-修改-写操作是不可能的,我们能做的最好的事情就是阻止观察者在load和store之间寻找。

我对x86上原子性的回答以不同的方式解释了同样的事情,关于原子的含义。


在单处理器(UP)系统中,唯一的异步观察者是其他系统设备(例如DMA)和中断处理程序。如果我们可以排除非cpu观察者对信号量的写入,那么我们关心的只是中断的原子性。

这段代码采用了简单的方法并禁用了中断。这是没有必要的(或者至少如果我们用asm写的话,这是没有必要的)。

中断是在两个指令之间处理的,而不是在指令中间。机器的体系结构状态要么包含内存减量,要么不包含,因为dec [mem]要么运行,要么不运行。我们实际上不需要lock dec [mem]

顺便说一句,这是没有lock前缀的cmpxchg的用例。我过去总是想知道为什么他们不直接把lock隐式地放在cmpxchg中,原因是UP系统通常不需要lock前缀。

此规则的例外是可以记录部分进度的可中断指令,如rep movsbvpgather/vpscatter 参见执行中间中断指令。这些指令不会是原子wrt。即使唯一的观察者是同一核心上的其他代码,也会中断。只有一次rep whatever的迭代,或者一个元素的聚集或分散,将发生或不发生。

大多数SIMD指令不能记录部分进度,所以例如vmovdqu ymm0, [rdi]要么完全发生,要么根本不发生在它运行的核心的PoV上。(当然不能保证原子强度。系统中的其他观察者,如DMA或MMIO,或其他核心。这时,正常的load/store原子性保证就起作用了。


没有可靠的方法来确保编译器发出dec [value]而不是像这样:

mov   eax, [value]
                           ;; interrupt here = bad
dec   eax
                           ;; interrupt here = bad
mov   [value], eax

ISO C11/c++ 11没有提供关于信号处理程序/中断的请求原子性的方法,但没有提供其他线程。他们确实提供了atomic_signal_fence作为编译器屏障(而不是thread_fence作为屏障wrt)。其他线程/内核),但是屏障不能创建原子性,只能控制顺序wrt。其他操作。

C11/c++ 11 volatile sig_atomic_t确实考虑到了这个想法,但它只提供了独立加载/存储的原子性,而不是RMW。它是x86 Linux上int的类型定义。

在特定的实现中,gcc -Wa,-momit-lock-prefix=yes将省略所有锁前缀。这对于单线程代码或单处理器机器是安全的,如果您的代码不包含需要在MMIO位置上执行原子RMW的设备驱动程序硬件访问,或者使用虚拟lock add作为更快的mfence

但是这在需要在SMP机器上运行的多线程程序中是不可用的,如果您在线程之间以及线程和信号处理程序之间有一些原子rmw。

相关内容

  • 没有找到相关文章

最新更新