web-dev-qa-db-ger.com

Wie führt man atomare Operationen unter Linux durch, die auf x86, arm, GCC und icc funktionieren?

Jedes moderne Betriebssystem bietet heute einige atomare Operationen: 

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

So etwas für Linux?

  • Ich brauche es, um auf den meisten von Linux unterstützten Plattformen zu arbeiten, einschließlich: x86, x86_64 und arm .
  • Ich brauche es, um mindestens mit GCC und Intel Compiler zu arbeiten.
  • Ich brauche keine 3. Par-Bibliothek wie Glib oder Qt.
  • Ich brauche es, um in C++ zu arbeiten (C nicht erforderlich)

Probleme:

  • GCC Atomic Builtins __sync_* werden nicht auf allen Plattformen (ARM) und nicht vom Intel-Compiler unterstützt.
  • AFAIK <asm/atomic.h> sollte nicht im Benutzerbereich verwendet werden und ich habe es überhaupt nicht erfolgreich verwendet. Ich bin mir auch nicht sicher, ob es mit dem Intel-Compiler funktionieren würde.

Irgendwelche Vorschläge?

Ich weiß, dass es viele verwandte Fragen gibt, aber einige von ihnen weisen auf __sync* hin, was für mich (ARM) nicht machbar ist, und einige zeigen auf asm/atomic.h.

Vielleicht gibt es eine Inline-Assembly-Bibliothek, die dies für GCC tut (ICC unterstützt gcc Assembly)?

Bearbeiten:  

Es gibt eine sehr unvollständige Lösung nur für Add-Vorgänge (ermöglicht die Implementierung eines atomaren Zählers, aber keine freien Strukturen, die CAS erfordern).

Wenn Sie libstc++ verwenden (Intel Compiler verwendet libstdc++), können Sie __gnu_cxx::__exchange_and_add verwenden, das in <ext/atomicity.h> oder <bits/atomicity.h> definiert ist. Abhängig von der Version des Compilers.

Ich möchte jedoch immer noch etwas sehen, das CAS unterstützt.

55
Artyom

Projekte verwenden dies:

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

Wenn Sie einfache Operationen wie CAS wünschen, können Sie nicht einfach nur die Arch-spezifischen Implementierungen aus dem Kernel heraus verwenden und Arch im User-Space mit autotools/cmake prüfen? Was die Lizenzierung angeht, ist der Kernel zwar GPL, ich denke jedoch, dass die Inline-Assembly für diese Vorgänge von Intel/AMD bereitgestellt wird, nicht jedoch, dass der Kernel über eine Lizenz verfügt. Sie befinden sich zufällig in einer leicht zugänglichen Form in der Kernel-Quelle.

19
Noah Watkins

Aktuelle Standards (ab 2011) von C & C++ spezifizieren jetzt atomare Operationen:

Unabhängig davon unterstützt Ihre Plattform oder Ihr Compiler diese neueren Header und Features möglicherweise nicht.

13
kevinarpe

Verdammt. Ich wollte die GCC-Primitiven vorschlagen, dann sagten Sie, dass sie verboten sind. :-)

In diesem Fall würde ich einen #ifdef für jede Architektur/Compiler-Kombination, die Sie interessieren, erstellen und den Inline-asm codieren. Und vielleicht nach __GNUC__ oder einem ähnlichen Makro suchen und die GCC-Primitive verwenden, sofern sie verfügbar sind, weil es sich so viel mehr anfühlt, diese zu verwenden. :-)

Es wird eine Menge Duplizierungen geben, und es kann schwierig sein, die Korrektheit zu überprüfen. Dies scheint jedoch die Art zu sein, wie dies bei vielen Projekten der Fall ist, und ich habe gute Ergebnisse erzielt.

Einige gotchas, die mich in der Vergangenheit gebissen haben: Vergessen Sie bei der Verwendung von GCC nicht "asm volatile" und Clobbers für "memory" und "cc" usw.

3
asveikau

Es gibt einen Patch für GCC, der atomare Operationen ARM unterstützt. Ich werde Ihnen auf Intel nicht weiterhelfen, aber Sie könnten den Code untersuchen. Es gibt Unterstützung für Kernel für ältere ARM -Architekturen, und neuere enthalten die Anweisungen, so dass Sie etwas bauen können, das funktioniert.

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

1
Justin Cormack

Boost, das über eine nicht aufdringliche Lizenz verfügt, und andere Frameworks bieten bereits tragbare Atomzähler - sofern sie auf der Zielplattform unterstützt werden.

Drittanbieter-Bibliotheken sind gut für uns. Und wenn Ihr Unternehmen Ihnen aus seltsamen Gründen die Verwendung untersagt, können Sie sich trotzdem ansehen, wie sie vorgehen (sofern die Lizenz es Ihnen erlaubt), um zu implementieren, was Sie suchen.

1
Luc Hermitte

__sync* wird (und wurde) sicherlich vom Intel-Compiler unterstützt, da GCC diese Build-Ins von dort übernommen hat. Lesen Sie den ersten Absatz auf dieser Seite . Siehe auch " Intel® C++ - Compiler für Linux * Intrinsics Reference ", Seite 211. Es stammt aus 2006 und beschreibt genau diese integrierten Komponenten.

In Bezug auf die Unterstützung von ARM gilt für ältere ARM - CPUs: Dies kann nicht vollständig im Benutzerbereich durchgeführt werden, aber im Kernelspace (durch Deaktivieren von Interrupts während der Operation) seit geraumer Zeit unterstützt.

Gemäß dieser PHP - Fehler vom 2011-10-08 wird __sync_* nur fehlschlagen

  • PA-RISC mit etwas anderem als Linux
  • SPARCv7 und niedriger
  • ARM mit GCC <4.3
  • ARMv5 und niedriger mit etwas anderem als Linux
  • MIPS1

Mit GCC> 4.3 (und 4.7 ist das aktuelle), sollten Sie also kein Problem mit ARMv6 und neuer haben. Sie sollten auch kein Problem mit ARMv5 haben, solange Sie für Linux kompilieren.

1
Mecki

Ich habe vor kurzem eine solche Umsetzung durchgeführt und war mit den gleichen Schwierigkeiten konfrontiert wie Sie. Meine Lösung war im Wesentlichen folgende:

  • versuchen Sie, die GCC-Builds mit dem Feature-Makro zu erkennen
  • wenn nicht verfügbar, implementieren Sie einfach etwas wie cmpxch mit __asm__ für die anderen Architekturen (ARM ist etwas komplizierter als das). Tun Sie dies einfach für eine mögliche Größe, z. B. sizeof(int).
  • implementieren Sie alle anderen Funktionen auftop dieser einen oder zwei Grundelemente mit inline-Funktionen
1
Jens Gustedt

Auf Debian/Ubuntu empfehlen ...

Sudo apt-get install libatomic-ops-dev

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

GCC & ICC kompatibel.

im Vergleich zu Intel Thread Building Blocks (TBB) mit atomarem <T> ist libatomic-ops-dev doppelt so schnell! (Intel-Compiler)

Tests mit Ubuntu i7-Producer-Consumer-Threads, die 10 Millionen Ints über eine Ringpufferverbindung in 0,5 Sekunden leiten, im Gegensatz zu 1,2 Sekunden für TBB

Und einfach zu verwenden, z.

flüchtiger AO_t-Kopf;

AO_fetch_and_add1 (& head);

0
user1408985

Siehe: kernel_user_helpers.txt oder entry-arm.c und suche nach __kuser_cmpxchg. Wie in Kommentaren anderer ARM Linux-Versionen zu sehen ist, 

kuser_cmpxchg

 Speicherort: 0xffff0fc0 

 Referenzprototyp: 

 int __kuser_cmpxchg (int32_t oldval, int32_t newval, flüchtig int32_t * ptr); 

 Eingabe: 

 r0 = oldval 
 r1 = newval 
 r2 = ptr 
 lr = Rücksprungadresse 

 Ausgabe: 

 r0 = Erfolgscode (Null oder Nicht-Null) 
 C-Flag = gesetzt, wenn r0 == 0, wird gelöscht, wenn r0! = 0 

 Verstümmelte Register: 

 r3, ip, Flags 

 Definition: 

 Speichern Sie newval in * ptr nur dann systematisch, wenn * ptr gleich oldval ..__ ist. Gibt Null zurück, wenn * ptr geändert wurde, oder nicht Null, wenn kein Austausch stattgefunden hat .
 Das C-Flag wird auch gesetzt, wenn * ptr geändert wurde, um Assembly __ zuzulassen. Optimierung im aufrufenden Code .

 Verwendungsbeispiel: 
 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;
}

Anmerkungen:

  • Diese Routine enthält bei Bedarf bereits Speicherbarrieren.
  • Nur gültig, wenn __kuser_helper_version> = 2 (ab Kernel-Version 2.6.12).

Dies ist für die Verwendung mit Linux mit ARMv3 unter Verwendung des Grundelements swp vorgesehen. Sie müssen einen sehr alten ARM haben, um dies nicht zu unterstützen. Nur ein Datenabbruch oder Interrupt kann dazu führen, dass das Drehen fehlschlägt. Der Kernel überwacht also diese Adresse ~ 0xffff0fc0 und führt ein BenutzerbereichPC fix-up, wenn entweder ein Datenabbruch oder ein Interrupt auftritt. Alle User-Space-Bibliotheken, die ARMv5 und niedriger unterstützen, verwenden diese Funktion.

Beispielsweise verwendet QtConcurrent dies.

0
artless noise