如何在 Linux 上执行适用于 x86、arm、GCC 和 icc 的原子操作?

     2023-02-16     110

关键词:

【中文标题】如何在 Linux 上执行适用于 x86、arm、GCC 和 icc 的原子操作?【英文标题】:How to perform atomic operations on Linux that work on x86, arm, GCC and icc? 【发布时间】:2011-01-18 06:00:08 【问题描述】:

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

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

对于 Linux 有什么类似的吗?

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

问题:

GCC atomic builtins __sync_* 并非在所有平台 (ARM) 上均受支持,英特尔编译器也不支持。 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 的东西。

【问题讨论】:

@KennyTM 好问题。 AFAIK ll/sc 仅适用于 arm6 及更高版本?所以它可能只适用于 arm6 及更高版本 所以让我们澄清一下 - ARM 不支持 GCC 内在函数是因为 GCC 工具链的缺点还是因为 ARM 的缺点?您能否确认您不能将它们与支持这些原子操作的 GCC for ARM 一起使用? ARM 的缺点。一些相关支持说明: ARM v5: SWP(atomic swap word size and byte size) ARM v6: LDREX/STREX(32 bit chained possible atomic load/store)+SWP ARM v7: LDREX/STREX(与v6相同,但有大小8/16 位的选项)V5 不能在不禁用中断的情况下进行原子增量,用户空间也不能这样做。但是,它可以做的就是调用操作系统,比如一个软件中断,然后让操作系统来处理它。 与 C 类似,但不需要太多可移植性:***.com/questions/2353371/atomic-increment-fetch 【参考方案1】:

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

C11:stdatomic.h C++11:std::atomic

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

【讨论】:

IIRC,对于 GCC,这可能需要使用 libatomic。 @Hasturkun:哪个版本的 GCC?最近的 GCC 版本仍然是这种情况吗? @Isaac:根据GCC Wiki on the C++11 memory model,从 GCC 4.7 开始,当无锁指令不可用时,可能需要 libatomic。 GCC 4.8 在其源代码树中包含 libatomic。【参考方案2】:

请参阅:kernel_user_helpers.txt 或 entry-arm.c 并查找 __kuser_cmpxchg。在其他 ARM Linux 版本的 cmets 中可以看到,

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 不支持这个。只有 data abortinterrupt 会导致旋转失败,因此内核监视此地址 ~0xffff0fc0 并执行 user空间 PC 在发生数据中止中断 时进行修复。所有支持 ARMv5 及更低版本的用户空间库都将使用此工具。

例如,QtConcurrent 使用这个。

【讨论】:

【参考方案3】:

在 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,libatomic-ops-dev 的速度是两倍多! (英特尔编译器)

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

并且易于使用,例如

易失性AO_t头;

AO_fetch_and_add1(&head);

【讨论】:

【参考方案4】:

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

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

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

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

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

【讨论】:

【参考方案5】:

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

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

【讨论】:

【参考方案6】:

项目正在使用这个:

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

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

【讨论】:

【参考方案7】:

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

尝试使用以下命令检测 gcc 内置函数 功能宏 如果不可用,只需执行 对于其他架构,类似于cmpxch__asm__(ARM 比这更复杂一些)。只需为一种可能的大小执行此操作,例如 sizeof(int)。 实现所有其他功能 在那一两个基元之上 带有inline 函数

【讨论】:

【参考方案8】:

Boost 具有非侵入式许可证,而其他框架已经提供便携式原子计数器——只要它们在目标平台上受支持。

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

【讨论】:

哪个 Boost 库包含这些? 这是常见细节的一部分:boost.cvs.sourceforge.net/viewvc/boost/boost/boost/detail/… (a) Boost 经常回退到 pthreads,事实上对于 ARM,它会使用 pthreads。 (b) atomic_counters 仅提供对引用计数很好的 inc/dec 操作,但它们太弱了。如果你需要像原子链表这样的东西,你需要像 CAS 这样的操作。 “如果你的公司出于奇怪的原因禁止你使用它们”我不禁止我只是不想使用第三部分库来减少构建某些项目的依赖关系。【参考方案9】:

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

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

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

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

【讨论】:

我最终发现 asm volatile 不是易变的,尽管我怀疑这种现象不仅限于内联汇编。 gcc 原语的问题在于,在某些版本中它们实际上是可用的,但是没有功能测试宏来测试它们。

如何使用 PYTHON 的“tkinter”模块在画布上显示图像,特别适用于 linux?

】如何使用PYTHON的“tkinter”模块在画布上显示图像,特别适用于linux?【英文标题】:HowtodisplaydisplayanIMAGEonaCANVASusingPYTHON\'s"tkinter"module,Specificallyforlinux?【发布时间】:2021-01-1202:02:15【问题描述】:我使用了以下代码:#AP... 查看详情

如何在 Linux 上强制执行向量下标超出范围调试断言

】如何在Linux上强制执行向量下标超出范围调试断言【英文标题】:howtoenforcevectorsubscriptoutofrangedebugassertiononlinux【发布时间】:2017-07-1413:32:57【问题描述】:此代码适用于linux。#include<vector>#include<iostream>usingnamespacestd;int... 查看详情

gcc和arm-linux-gcc是啥关系?区别是啥?

如题gcc是linux系统下面用来将代码编译成一个可执行程序的手段。编译出来的是适用于linux系统的可执行二进制文件。可执行程序其实就是一堆的0101二进制机器码。这些机器码代表什么含义只有机器本身能理解。所以你用gcc编译... 查看详情

gcc和arm-linux-gcc是啥关系?区别是啥?

...好呢?谢谢!gcc是linux系统下面用来将代码编译成一个可执行程序的手段。编译出来的是适用于linux系统的可执行二进制文件。可执行程序其实就是一堆的0101二进制机器码。这些机器码代表什么含义只有机器本身能理解。所以你... 查看详情

qt-4.8.4安装和环境变量配置

...在linux中全安装qt。用它编译好程序后,直接用“./”就可执行。嵌入式X86平台:带有qvfb功能,在PC机上能够模拟应用程序在arm开发板上的执行情况。用此编译好的程序,仅仅用“./”命令 查看详情

【香港】windows8后续小事纪

...完美应用于平板电脑上,但相较于目前的热门产品iPad2又如何呢?有外国网站就拿来了Windows8的平板电脑,与已安装了iOS5的iPad2比对使用 查看详情

我们可以在 linux 上运行 tensorflow lite 吗?或者它仅适用于 android 和 ios

...有可能在linux平台上运行tensorflowlite?如果是,那么我们如何在java/C++/python中编写代码以在linux平台上加载和 查看详情

如何在 Linux x86 上使用 C 对蓝牙 LE 设备进行编程?

】如何在Linuxx86上使用C对蓝牙LE设备进行编程?【英文标题】:HowtoprogramaBluetoothLEdeviceusingConLinuxx86?【发布时间】:2015-06-0107:17:06【问题描述】:我有一个蓝牙设备,我可以在Linux上使用gatttool来控制它。我想开发自己的可以向其... 查看详情

x86和arm架构对比

...文件,这样可以保证更高的速度。  CPU有着处理指令、执行操作、控制时间、处理数据四大作用。移动设备很复杂,CPU需要执行数以百万计的指示,才能使它向期待的方向运行,而CPU的速度和功率效率是至关重要的。速度影响... 查看详情

在适用于 Linux 的 Windows 子系统上安装 Apache

】在适用于Linux的Windows子系统上安装Apache【英文标题】:InstallingApacheonWindowsSubsystemforLinux【发布时间】:2016-07-2722:36:36【问题描述】:刚刚更新到最新的Windows10版本(内部版本14316),我立即开始使用WSL,这是适用于Linux的Windows... 查看详情

如何在 c++ 中创建一个适用于 Windows 和 linux 的文件夹(目录)[重复]

】如何在c++中创建一个适用于Windows和linux的文件夹(目录)[重复]【英文标题】:howcanIcreateafolder(directory)inc++thatworksonwindwosandlinux[duplicate]【发布时间】:2018-04-2909:52:29【问题描述】:我必须用c++编写一个程序来创建一个目录,但... 查看详情

PHP TesseractOCR 仅适用于命令行

...当我从Web浏览器运行相同的脚本时,我得到空白页。知道如何在网络浏览器上也能做到这一点吗?【问题讨论】:检查您的网络服务器日志。可能 查看详情

x86的库和arm的.o文件能链接吗

...此无法直接链接。你需要先将x86的库编译成ARM架构下的可执行文件,然后再和.o文件相链接,这样就可以执行ARM架构下的可执行文件了。参考技术Aarm64-v8a是可以向下兼容的,但前提是你的项目里面没有arm64-v8a的文件夹,如果你有... 查看详情

x86机器上运行arm64docker(代码片段)

...像,但是在x86上直接运行会报错:panic:standard_init_linux.go:175:execuserprocesscaused“execformaterror”[recovered]在x86_64平台上缺少运行arm32v7的Python解释器所需要的“环境依赖”,值得庆幸的是在Linu 查看详情

Eclipse makefile 仅适用于 C(windows 版本)

】Eclipsemakefile仅适用于C(windows版本)【英文标题】:EclipsemakefileonlyforC(windowsversion)【发布时间】:2011-02-2809:58:43【问题描述】:这个Eclipse(Helios)在WindowsPC上运行,针对ARM9目标板进行交叉编译。我可以使用托管makefile选项为目标... 查看详情

休眠和 HSQLDB - 适用于 PC - Linux 上的错误

...个测试应用程序来熟悉HSQLDB上的Hibernate。我制作了一个可执行的JAR并将应用程序移到LinuxVM上。当我在设置后运行相同的程序时,当我尝试运行测试应用程序时,我的HSQLDB会抛出以下消息: 查看详情

如何实现对arm汇编指令的调试?

学习ARM汇编语言时,少不了对ARM汇编指令的调试。作为支持多语言的调试器,gdb自然是较好的选择。调试器工作时,一般通过修改代码段的内容构造trap软中断指令,实现程序的暂停和程序执行状态的监控。为了在x86平台上执行A... 查看详情

c++代码和可执行程序在x86和arm上的区别(代码片段)

从使用上来看,可执行程序肯定是不通用的。armx86生成的可执行程序大小都有差异呢。 但是,如果源码编译,如果环境类似,相同的源码可以直接移植。例如:如下程序👇donut.cpp#include<stdio.h>#include&l... 查看详情