是否可以在不使用偏移量的情况下将指针存储在共享内存中?

Jos*_*vin 8 multithreading mmap atomic shared-memory race-condition

当使用共享存储器时,每个进程可以将共享区域映射到其相应地址空间的不同区域.这意味着在共享区域中存储指针时,需要将它们存储为共享区域起点的偏移量.不幸的是,这使得原子指令的使用变得复杂(例如,如果您正在尝试编写无锁算法).例如,假设您在共享内存中有一堆引用计数节点,由单个编写器创建.写入器周期性地原子地更新指针"p"以指向具有正引用计数的有效节点.读者希望原子地写入'p',因为它指向一个节点(结构)的开头,其第一个元素是引用计数.由于p始​​终指向有效节点,因此增加引用计数是安全的,并且可以安全地取消引用"p"并访问其他成员.但是,这一切只有在所有内容都在同一地址空间时才有效.如果节点和'p'指针存储在共享内存中,则客户端会遇到竞争条件:

  1. x =读p
  2. y = x +偏移量
  3. 在y处增加refcount

在步骤2期间,p可能会改变,x可能不再指向有效节点.我能想到的唯一解决方法是以某种方式迫使所有进程就共享内存映射的位置达成一致,以便在mmap'd区域中存储真实指针而不是偏移量.有没有办法做到这一点?我在mmap文档中看到了MAP_FIXED,但我不知道如何选择一个安全的地址.

编辑:使用内联汇编和x86上的'lock'前缀也许可以建立一个"增量ptr X,偏移量Y乘以值Z"?其他架构的等价选项?没有写过很多装配,不知道是否存在所需的指令.

GJ.*_*GJ. 3

在低级别上,x86 原子指令可以一次执行所有此树步骤:

  1. x = 读取 p
  2. y = x + 偏移增量
  3. y 处的重新计数
//
      mov  edi, Destination
      mov  edx, DataOffset
      mov  ecx, NewData
 @Repeat:
      mov  eax, [edi + edx]    //load OldData
//Here you can also increment eax and save to [edi + edx]          
      lock cmpxchg dword ptr [edi + edx], ecx
      jnz  @Repeat
//
Run Code Online (Sandbox Code Playgroud)