Как выполнить атомарные операции в Linux, которые работают на x86, arm, GCC и icc? - PullRequest
55 голосов
/ 18 февраля 2010

Каждая современная ОС предоставляет сегодня несколько элементарных операций:

  • Windows имеет Interlocked* API
  • FreeBSD имеет <machine/atomic.h>
  • Солярис имеет <atomic.h>
  • Mac OS X имеет <libkern/OSAtomic.h>

Что-нибудь подобное для Linux?

  • Мне нужно, чтобы он работал на большинстве поддерживаемых Linux-платформ, включая: x86, x86_64 и arm .
  • Мне нужно, чтобы он работал хотя бы на GCC и Intel Compiler.
  • Мне не нужно использовать библиотеку 3-го числа, такую ​​как glib или qt.
  • Мне это нужно для работы в C ++ (C не требуется)

Вопросы:

  • Встроенные элементы GCC __sync_* не поддерживаются на всех платформах (ARM) и не поддерживаются компилятором Intel.
  • AFAIK <asm/atomic.h> не следует использовать в пользовательском пространстве, и я вообще не использовал его вообще. Кроме того, я не уверен, что он будет работать с компилятором Intel.

Есть предложения?

Я знаю, что есть много связанных вопросов, но некоторые из них указывают на __sync*, что для меня нецелесообразно (ARM), а некоторые указывают на asm/atomic.h.

Может быть, есть встроенная библиотека сборок, которая делает это для GCC (ICC поддерживает сборку gcc)?

Редактировать:

Существует очень частичное решение только для операций добавления (позволяет реализовать атомарный счетчик, но не блокировать свободные структуры, требующие CAS):

Если вы используете libstc++ (Intel Compiler использует libstdc++), тогда вы можете использовать __gnu_cxx::__exchange_and_add, который определен в <ext/atomicity.h> или <bits/atomicity.h>. Зависит от версии компилятора.

Однако я все еще хотел бы увидеть что-то, поддерживающее CAS.

Ответы [ 9 ]

19 голосов
/ 13 января 2011

Проекты используют это:

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

Если вам нужны простые операции, такие как CAS, разве вы не можете просто использовать специфичные для arch реализации из ядра и выполнять проверку arch в пространстве пользователя с помощью autotools / cmake? Что касается лицензирования, хотя ядро ​​GPL, я думаю, что можно утверждать, что встроенная сборка для этих операций обеспечивается Intel / AMD, а не то, что у ядра есть лицензия на них. Просто они находятся в легко доступной форме в исходном коде ядра.

13 голосов
/ 31 июля 2013

В последних стандартах C & C ++ (от 2011 г.) теперь определены атомарные операции:

В любом случае, ваша платформа или компилятор могут не поддерживать эти новые заголовки и функции.

3 голосов
/ 18 февраля 2010

штопать.Я собирался предложить примитивы GCC, тогда вы сказали, что они были запрещены.: -)

В этом случае я бы сделал #ifdef для каждой интересующей вас комбинации архитектура / компилятор и кодировал бы встроенный ассемблер.И, возможно, проверьте наличие &#95;&#95;GNUC__ или какого-либо подобного макроса и используйте примитивы GCC, если они доступны, потому что он кажется гораздо более правильным для их использования.: -)

У вас будет много дубликатов, и может быть трудно проверить правильность, но, похоже, именно так многие проекты делают это, и у меня были хорошие результаты с этим.

Некоторые ошибки, которые укусили меня в прошлом: при использовании GCC не забывайте "asm <b>volatile</b>" и всплывающие слова для "memory" и "cc" и т. Д.

1 голос
/ 12 апреля 2012

__sync* определенно поддерживается (и поддерживается) компилятором Intel, потому что GCC принял эти встроенные модули оттуда.Прочтите первый абзац на этой странице .Также см. « Компилятор Intel® C ++ для Linux * Справочник по встроенным функциям », стр. 198. Он взят из 2006 и описывает именно эти встроенные модули.

Что касается поддержки ARM,для более старых процессоров ARM: это не может быть сделано полностью в пользовательском пространстве, но это может быть сделано в пространстве ядра (путем отключения прерываний во время операции), и я думаю, что где-то читал, что это поддерживается довольно давно.

Согласно этой PHP-ошибке от 2011-10-08, __sync_* не будет работать только на

  • PA-RISC с чем-либо, кроме Linux
  • SPARCv7 и ниже
  • ARM с GCC <4.3 </li>
  • ARMv5 и ниже с чем-либо, кроме Linux
  • MIPS1

То есть с GCC> 4.3 (и 4.7 является текущей), у вас не должно быть проблем с ARMv6 и новее.У вас не должно быть никаких проблем с ARMv5, если вы компилируете для Linux.

1 голос
/ 16 марта 2012

Здесь есть патч для GCC для поддержки атомарных операций ARM. В Intel это не поможет, но вы можете изучить код - в последнее время в ядре поддерживается более старая архитектура ARM, а в более новые встроены инструкции, поэтому вы должны иметь возможность создать что-то, что работает.

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

1 голос
/ 13 января 2011

Недавно я реализовал такую ​​вещь и столкнулся с теми же трудностями, что и вы. Мое решение было в основном следующим:

  • попытаться обнаружить встроенные команды gcc с макрос объекта
  • если нет в наличии, просто внедрить что-то вроде cmpxch с __asm__ для других архитектур (ARM немного сложнее). Просто сделайте это для одного возможного размера, например sizeof(int).
  • реализовать все остальные функции на вершина этого одного или двух примитивов с inline функциями
1 голос
/ 18 февраля 2010

Boost, обладающий ненавязчивой лицензией, а другие платформы уже предлагают портативные атомные счетчики, если они поддерживаются на целевой платформе.

Сторонние библиотеки полезны для нас. И если по странным причинам ваша компания запрещает вам их использовать, вы все равно можете посмотреть, как они действуют (если лицензия позволяет это использовать) для реализации того, что вы ищете.

0 голосов
/ 13 июня 2013

См .: kernel_user_helpers.txt или entry-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).

Это для использования с Linux с ARMv3, использующим примитив swp. Вы должны иметь очень древний ARM, чтобы не поддерживать это. Только прерывание данных или прерывание может привести к сбою вращения, поэтому ядро ​​отслеживает этот адрес ~ 0xffff0fc0 и выполняет пространство пользователя PC исправление, когда данные прерывают или прерывание . Все библиотеки пользовательского пространства, которые поддерживают ARMv5 и ниже, будут использовать эту возможность.

Например, QtConcurrent использует это.

0 голосов
/ 22 мая 2012

В 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 более чем в два раза быстрее!(Компилятор Intel)

Тестирование на потоках производителя-потребителя Ubuntu i7, передавающих 10 миллионов дюймов по кольцевому буферному соединению за 0,5 секунды, в отличие от 1,2 секунды для TBB

И простой в использовании, например

изменчивая голова AO_t;

AO_fetch_and_add1 (& head);

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...