繁体   English   中英

如何在Linux上执行x86,arm,GCC和icc上的原子操作?

[英]How to perform atomic operations on Linux that work on x86, arm, GCC and icc?

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

  • Windows具有Interlocked* API
  • FreeBSD有<machine/atomic.h>
  • Solaris有<atomic.h>
  • Mac OS X有<libkern/OSAtomic.h>

对Linux有什么相似之处?

  • 我需要它在大多数Linux支持的平台上工作,包括:x86,x86_64和arm
  • 我需要它至少在GCC和英特尔编译器上工作。
  • 我不需要使用像glib或qt这样的第三方库。
  • 我需要它在C ++中工作(C不是必需的)

问题:

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

有什么建议?

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

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

编辑:

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

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

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

项目正在使用此:

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

如果你想要简单的操作,比如CAS,你不能只使用内核以外的特定于arch的实现,并使用autotools / cmake在用户空间中进行arch检查吗? 就许可而言,虽然内核是GPL,但我认为这些操作的内联汇编是由Intel / AMD提供的,而不是内核拥有许可证。 它们碰巧在内核源代码中易于访问。

C&C ++的最新标准(自2011年起)现在指定了原子操作:

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

该死。 我打算建议GCC原语,然后你说它们是不受限制的。 :-)

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

你将会有很多重复,并且可能很难验证是否正确,但这似乎是许多项目做到这一点的方式,而且我已经有了很好的结果。

过去有些问题让我感到困惑:当使用GCC时,不要忘记“ asm volatile ”和"memory""cc"等的咒语。

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

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

根据这个日期为2011-10-08的PHP bug__sync_*只会失败

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

因此,当GCC> 4.3(并且4.7是当前版本)时,您应该不会遇到ARMv6及更新版本的问题。 只要为Linux编译,你就不应该对ARMv5没有任何问题。

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

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

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

  • 尝试使用功能宏检测gcc内置
  • 如果没有可用的话,就用其他架构实现像cmpxch__asm__这样的东西(ARM比这复杂一点)。 只需为一个可能的大小做到这一点,例如sizeof(int)
  • 使用inline函数在该一个或两个基元之上实现所有其他功能

这里有一个GCC补丁来支持ARM原子操作。 对英特尔没有帮助,但是你可以检查一下代码 - 最近的内核支持旧的ARM体系结构,而新的内核支持内置指令,所以你应该能够构建一些有效的东西。

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

在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生产者 - 消费者线程上进行测试,在0.5秒内向环形缓冲区连接管道输入1000万英寸,而对于TBB则为1.2秒

并且易于使用,例如

挥发性AO_t头;

AO_fetch_and_add1(头);

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

kuser_cmpxchg

Location:       0xffff0fc0

Reference prototype:

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

Input:

  r0 = oldval
  r1 = newval
  r2 = ptr
  lr = return address

Output:

  r0 = success code (zero or non-zero)
  C flag = set if r0 == 0, clear if r0 != 0

Clobbered registers:

  r3, ip, flags

Definition:

  Atomically store newval in *ptr only if *ptr is equal to oldval.
  Return zero if *ptr was changed or non-zero if no exchange happened.
  The C flag is also set if *ptr was changed to allow for assembly
  optimization in the calling code.

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

例如, QtConcurrent使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM