56

如今,每个现代操作系统都提供了一些原子操作:

  • Windows 有Interlocked*API
  • FreeBSD 有<machine/atomic.h>
  • 索拉里斯有<atomic.h>
  • Mac OS X 有<libkern/OSAtomic.h>

Linux有类似的东西吗?

  • 我需要它在大多数 Linux 支持的平台上工作,包括:x86、x86_64 和 arm
  • 我需要它至少在 GCC 和 Intel 编译器上工作。
  • 我不需要使用像 glib 或 qt 这样的 3rd par 库。
  • 我需要它在 C++ 中工作(不需要 C)

问题:

  • 并非所有平台 (ARM) 都支持GCC atomic builtins __sync_*,英特尔编译器也不支持。
  • AFAIK<asm/atomic.h>不应该在用户空间中使用,我根本没有成功使用它。另外,我不确定它是否适用于英特尔编译器。

有什么建议么?

我知道有很多相关的问题,但其中一些指向__sync*这对我 (ARM) 来说是不可行的,还有一些指向asm/atomic.h.

也许有一个内联汇编库可以为 GCC 执行此操作(ICC 支持 gcc 汇编)?

编辑:

仅针对添加操作有一个非常部分的解决方案(允许实现原子计数器,但不允许实现需要 CAS 的无锁结构):

如果您使用libstc++(英特尔编译器使用libstdc++),那么您可以使用在或__gnu_cxx::__exchange_and_add中定义的那个。取决于编译器版本。<ext/atomicity.h><bits/atomicity.h>

但是我仍然希望看到支持 CAS 的东西。

4

9 回答 9

19

项目正在使用这个:

http://packages.debian.org/source/sid/libatomic-ops

如果你想要简单的操作,比如 CAS,你不能只使用内核中特定于架构的实现,并使用 autotools/cmake 在用户空间进行架构检查吗?就许可而言,虽然内核是 GPL,但我认为这些操作的内联程序集是由 Intel/AMD 提供的,而不是内核对它们有许可,这是有争议的。它们恰好在内核源代码中以易于访问的形式出现。

于 2011-01-13T19:16:18.283 回答
14

C 和 C++ 的最新标准(从 2011 年开始)现在指定原子操作:

无论如何,您的平台或编译器可能不支持这些较新的标头和功能。

于 2013-07-31T06:47:25.887 回答
3

该死。我打算建议 GCC 原语,然后你说它们是禁区。:-)

在这种情况下,我会#ifdef为您关心的每个架构/编译器组合做一个并编写内联汇编。并且可能检查__GNUC__或一些类似的宏并使用 GCC 原语(如果它们可用),因为使用这些原语感觉更合适。:-)

你会有很多重复,可能很难验证正确性,但这似乎是很多项目这样做的方式,我已经取得了很好的结果。

过去让我感到困扰的一些问题:使用 GCC 时,不要忘记 " " 和 and 的clobbers等。asm volatile"memory""cc"

于 2010-02-18T09:36:04.270 回答
1

具有非侵入式许可证的 Boost 和其他框架已经提供了可移植的原子计数器——只要它们在目标平台上得到支持。

第三方图书馆对我们有好处。如果您的公司出于奇怪的原因禁止您使用它们,您仍然可以查看它们如何进行(只要许可证允许您使用)来实现您正在寻找的东西。

于 2010-02-18T12:09:50.623 回答
1

我最近做了一个这样的事情的实现,我遇到了和你一样的困难。我的解决方案基本上如下:

  • 尝试使用功能宏检测 gcc 内置函数
  • 如果不可用,只需为其他架构实现类似的东西cmpxch__asm__ARM 比这更复杂一些)。只需针对一种可能的尺寸进行操作,例如sizeof(int).
  • 在具有功能的一两个原语之上实现所有其他inline功能
于 2011-01-13T18:02:54.223 回答
1

这里有一个 GCC 补丁来支持 ARM 原子操作。在 Intel 上对您没有帮助,但您可以检查代码 - 最近内核支持较旧的 ARM 架构,而较新的内核支持内置指令,因此您应该能够构建一些有效的东西。

http://gcc.gnu.org/ml/gcc-patches/2011-07/msg00050.html

于 2012-03-16T13:35:22.560 回答
1

__sync*英特尔编译器肯定(并且已经)支持,因为 GCC 从那里采用了这些内置组件。阅读本页的第一段。另请参阅第 198 页的“英特尔® C++ Compiler for Linux* Intrinsics Reference ”。它来自2006 年,准确描述了这些内置函数。

关于 ARM 支持,对于较旧的 ARM CPU:它不能完全在用户空间中完成,但可以在内核空间中完成(通过在操作期间禁用中断),我想我在某处读到它现在支持很长一段时间了。

根据this PHP bug,日期为2011-10-08,__sync_*只会失败

  • PA-RISC 与 Linux 以外的任何东西
  • SPARCv7 及更低版本
  • GCC < 4.3 的 ARM
  • ARMv5 及更低版本与 Linux 以外的任何东西
  • MIPS1

因此,对于 GCC > 4.3(并且 4.7 是当前版本),您不应该对 ARMv6 和更新版本有问题。只要为 Linux 编译,你对 ARMv5 也不应该有任何问题。

于 2012-04-12T02:17:06.320 回答
0

在 Debian/Ubuntu 上推荐...

sudo apt-get install libatomic-ops-dev

示例: http: //www.hpl.hp.com/research/linux/atomic_ops/example.php4

GCC 和 ICC 兼容。

与 Intel Thread Building Blocks (TBB) 相比,使用 atomic<T>,libatomic-ops-dev 的速度要快两倍多!(英特尔编译器)

在 Ubuntu i7 生产者-消费者线程上测试 1000 万个整数在 0.5 秒内通过环形缓冲区连接,而 TBB 需要 1.2 秒

并且易于使用,例如

不稳定的AO_t头;

AO_fetch_and_add1(&head);

于 2012-05-21T23:33:52.780 回答
0

请参阅:kernel_user_helpers.txtentry-arm.c并查找__kuser_cmpxchg. 正如其他 ARM Linux 版本的评论中所见,

kuser_cmpxchg

位置:0xffff0fc0

参考原型:

  int __kuser_cmpxchg(int32_t oldval, int32_t newval, volatile int32_t *ptr);

输入:

  r0 = 旧值
  r1 = 新值
  r2 = 指针
  lr = 返回地址

输出:

  r0 = 成功代码(零或非零)
  C 标志 = 如果 r0 == 0 则设置,如果 r0 != 0 则清除

破坏寄存器:

  r3,IP,标志

定义:

  仅当 *ptr 等于 oldval 时,以原子方式将 newval 存储在 *ptr 中。
  如果 *ptr 已更改,则返回零,如果没有发生交换,则返回非零。
  如果 *ptr 已更改为允许汇编,则也会设置 C 标志
  调用代码中的优化。

使用示例:
 typedef int (__kuser_cmpxchg_t)(int oldval, int newval, volatile int *ptr);
 #define __kuser_cmpxchg (*(__kuser_cmpxchg_t *)0xffff0fc0)

 int atomic_add(volatile int *ptr, int val)
 {
        int old, new;

        do {
                old = *ptr;
                new = old + val;
        } while(__kuser_cmpxchg(old, new, ptr));

        return new;
}

笔记:

  • 此例程已根据需要包含内存屏障。
  • 仅当 __kuser_helper_version >= 2(来自内核版本 2.6.12)时才有效。

这适用于使用swp原语的带有 ARMv3 的 Linux。你必须有一个非常古老的 ARM 不支持这个。只有数据中止中断会导致旋转失败,因此内核监视此地址~0xffff0fc0并在发生数据中止中断时执行用户空间 PC修复。所有支持 ARMv5 及更低版本的用户空间库都将使用此工具。

例如,QtConcurrent使用它。

于 2013-06-13T15:49:53.337 回答