[英]How to perform atomic operations on Linux that work on x86, arm, GCC and icc?
每个现代操作系统都提供一些原子操作:
Interlocked*
API <machine/atomic.h>
<atomic.h>
<libkern/OSAtomic.h>
对Linux有什么相似之处?
问题:
__sync_*
,并且英特尔编译器不支持。 <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提供的,而不是内核拥有许可证。 它们碰巧在内核源代码中易于访问。
该死。 我打算建议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_*
只会失败
因此,当GCC> 4.3(并且4.7是当前版本)时,您应该不会遇到ARMv6及更新版本的问题。 只要为Linux编译,你就不应该对ARMv5没有任何问题。
具有非侵入式许可证的Boost和其他框架已经提供了可移植的原子计数器 - 只要它们在目标平台上受支持。
第三方图书馆对我们有好处。 如果由于奇怪的原因,您的公司禁止您使用它们,您仍然可以查看它们如何继续(只要许可证允许您使用)来实现您正在寻找的内容。
我最近做了这样的事情,我遇到了和你一样的困难。 我的解决方案基本上如下:
cmpxch
和__asm__
这样的东西(ARM比这复杂一点)。 只需为一个可能的大小做到这一点,例如sizeof(int)
。 inline
函数在该一个或两个基元之上实现所有其他功能 这里有一个GCC补丁来支持ARM原子操作。 对英特尔没有帮助,但是你可以检查一下代码 - 最近的内核支持旧的ARM体系结构,而新的内核支持内置指令,所以你应该能够构建一些有效的东西。
在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.txt或entry-arm.c并查找__kuser_cmpxchg
。 如其他ARM Linux版本的评论所示,
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;
}
笔记:
这适用于使用swp
原语的Linux和ARMv3。 你必须有一个非常古老的ARM不支持这个。 只有数据中止或中断才会导致旋转失败,因此内核会监视此地址~0xffff0fc0,并在发生数据中止或中断时执行用户空间 PC
修复。 支持ARMv5及更低版本的所有用户空间库都将使用此工具。
例如, QtConcurrent使用它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.