当前位置: 首页 > 面试题库 >

内存屏障和Linux上的atomic_t

壤驷阳波
2023-03-14
问题内容

最近,我正在阅读一些Linux内核空间代码,我看到了

uint64_t used;
uint64_t blocked;

used = atomic64_read(&g_variable->used);       //#1
barrier();                                     //#2
blocked = atomic64_read(&g_variable->blocked); //#3

该代码段的语义是什么?是否确保#1在#3之前由#2执行。但是我有点乱,因为

#A 在64位平台上,atomic64_read宏扩展为

used = (&g_variable->used)->counter           // where counter is volatile.

在32位平台中,将其转换为使用锁 cmpxchg8b 。我认为这两个具有相同的语义,对于64位版本,我认为这意味着:

  1. all-or-nothing ,我们可以排除地址未对齐且字长大于CPU本机字长的情况。
  2. 没有优化 ,强制CPU从内存位置读取。

atomic64_read没有保留读取顺序的语义!!! 看到这个

#B屏障定义

/* Optimization barrier */
/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

在Wiki中,这只是防止
gcc编译器 重新排列读写顺序。

我很困惑的是它如何禁用CPU的重新排序优化?另外,我可以认为屏障宏是完整的栅栏吗?


问题答案:

32位x86处理器不能为64位类型提供简单的原子读取操作。在此类处理“普通”寄存器的CPU上,对64位类型唯一的原子操作是LOCK CMPXCHG8B,这就是为什么在此使用它的原因。另一种选择是使用MOVQ和MMX /
XMM寄存器,但是这需要了解FPU状态/寄存器,并且需要使用MMX / XMM指令完成对该值的所有操作。

在64位x86_64处理器上,对64位类型的对齐读取是原子的,并且可以通过一条MOV指令完成,因此仅需要进行普通读取—
的使用volatile只是为了确保编译器实际进行读取,并且不缓存先前的值。

至于读取顺序,您引用的内联汇编程序可确保编译器以正确的顺序发出指令,这是x86 / x86_64
CPU所需要的,只要写入顺序正确即可。LOCKed在x86上的写入具有总顺序;普通MOV写提供了“因果一致性”,因此,如果线程A做到了,x=1那么y=2如果线程B进行了读取,y==2则随后的读取x将看到x==1

在IA-64中,的PowerPC,SPARC,和具有更宽松的存储器模型其它处理器很可能有更多的atomic64_read()barrier()



 类似资料:
  • 问题内容: 假设我有一个静态的复杂对象,该对象由线程池定期更新,并在长时间运行的线程中或多或少地连续读取。对象本身始终是不可变的,并反映事物的最新状态。 我至少不在乎读者是看到旧版本还是新版本的Foo,但是我需要看到一个完全初始化的对象。IIUC,Java规范说,这里没有内存障碍,我可能会看到一个对象,其中fb已初始化,但fa尚未提交给内存。我的程序是一个现实世界的程序,迟早会将内容提交到内存中,

  • 问题内容: 我最近在一次演讲中听说,对volatile的写操作会为线程已写入的每个变量触发内存屏障。真的对吗?从JLS看来,似乎只有相关的变量被清除了,而其他变量则没有。有人知道什么是正确的吗?能否给我指出JLS中的具体位置? 问题答案: 对挥发性变量和其他变量的引用是正确的。我没有意识到,before- before的可传递性是VM必须实现的,而不是从定义中得出的。我仍然感到困惑,为什么并没有明

  • 我发现x86 CPU具有以下内存屏障指令:

  • 问题内容: 在linux代码中,我记得听说过mutex_lock()周围有完整的内存屏障。我想确定它是否也在sem_xxx附近。 问题答案: 是的,在无竞争的情况下,它使用原子递增/递减,这当然有一个小数。对于有争议的情况,有一个对futex的系统调用,它也有一个成员。

  • 问题内容: 在过去的问题中,我询问了如何在不破坏种族的情况下实现pthread屏障: 从迈克尔·伯尔(Michael Burr)那里获得了针对过程本地障碍的完美解决方案,但是对于过程共享的障碍却没有。我们后来研究了一些想法,但从未得出令人满意的结论,甚至没有开始涉及资源故障案例。 在Linux上能否制造出满足以下条件的障碍: 进程共享的(可以在任何共享内存中创建)。 在屏障等待函数返回后,可以安全

  • 我们都知道,直接从内存读写数据要比从硬盘读写数据快得多,因此更希望所有数据的读取和写入都在内存中完成,然而内存是有限的,这样就引出了物理内存与虚拟内存的概念。 物理内存就是系统硬件提供的内存大小,是真正的内存。相对于物理内存,在 Linux 下还有一个虚拟内存的概念,虚拟内存是为了满足物理内存的不足而提出的策略,它是利用磁盘空间虚拟出的一块逻辑内存。用作虚拟内存的磁盘空间被称为 交换空间(又称