web-dev-qa-db-fra.com

Comment effectuer des opérations atomiques sur Linux fonctionnant sous x86, arm, GCC et icc?

Chaque système d'exploitation moderne fournit aujourd'hui certaines opérations atomiques: 

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

Quelque chose comme ça pour Linux?

  • J'en ai besoin pour fonctionner sur la plupart des plates-formes supportées par Linux, notamment: x86, x86_64 et arm .
  • J'en ai besoin pour travailler au moins sur GCC et Intel Compiler.
  • Je n'ai pas besoin d'utiliser la 3ème bibliothèque par pair comme glib ou qt.
  • J'en ai besoin pour travailler en C++ (C non requis)

Problèmes:

  • Les entités intégrées atomiques GCC __sync_* ne sont pas prises en charge sur toutes les plateformes (ARM) et ne sont pas prises en charge par le compilateur Intel.
  • Autant que je sache, <asm/atomic.h> ne doit pas être utilisé dans l'espace utilisateur et je ne l'ai pas utilisé avec succès du tout. De plus, je ne suis pas sûr que cela fonctionne avec le compilateur Intel.

Aucune suggestion?

Je sais qu'il y a beaucoup de questions connexes mais certaines d'entre elles portent sur __sync*, ce qui n'est pas faisable pour moi (ARM) et d'autres sur asm/atomic.h.

Peut-être y a-t-il une bibliothèque d'assemblage intégrée qui fait cela pour GCC (ICC supporte l'assemblage de gcc)?

Modifier:  

Il existe une solution très partielle pour les opérations d'ajout uniquement (permet d'implémenter un compteur atomique mais pas de verrouiller les structures libres qui requièrent un CAS):

Si vous utilisez libstc++ (Intel Compiler utilise libstdc++), vous pouvez utiliser __gnu_cxx::__exchange_and_add celui défini dans <ext/atomicity.h> ou <bits/atomicity.h>. Dépend de la version du compilateur.

Cependant, j'aimerais quand même voir quelque chose qui supporte CAS.

55
Artyom

Les projets utilisent ceci:

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

Si vous voulez des opérations simples telles que CAS, ne pouvez-vous pas simplement utiliser les implémentations spécifiques d'Arch en dehors du noyau, et vérifier Arch dans l'espace utilisateur avec autotools/cmake? En ce qui concerne les licences, bien que le noyau soit en GPL, je pense qu’il est discutable que l’assemblage en ligne de ces opérations soit fourni par Intel/AMD et non que le noyau dispose d’une licence. Ils se trouvent simplement sous une forme facilement accessible dans la source du noyau.

19
Noah Watkins

Les normes récentes (à partir de 2011) de C & C++ spécifient maintenant les opérations atomiques:

Quoi qu'il en soit, votre plate-forme ou votre compilateur peut ne pas prendre en charge ces nouveaux en-têtes et fonctionnalités.

13
kevinarpe

Zut. J'allais suggérer les primitives GCC, puis vous avez dit qu'elles étaient interdites. :-)

Dans ce cas, je ferais un #ifdef pour chaque combinaison architecture/compilateur qui vous tient à coeur et coderais le fichier asm en ligne. Et peut-être vérifier __GNUC__ ou une macro similaire et utiliser les primitives GCC si elles sont disponibles, car il semble beaucoup plus approprié de les utiliser. :-)

Vous allez avoir beaucoup de doublons et il sera peut-être difficile de vérifier l'exactitude, mais cela semble être la façon dont beaucoup de projets procèdent, et j'ai obtenu de bons résultats.

Quelques pièges qui m'ont mordu dans le passé: lors de l'utilisation de GCC, n'oubliez pas "asm volatile" et clobbers pour "memory" et "cc", etc.

3
asveikau

Il y a un correctif pour GCC ici pour supporter les opérations atomiques ARM. Je ne vous aiderai pas sur Intel, mais vous pourrez examiner le code - le noyau actuel prend en charge les architectures plus anciennes ARM, et les nouvelles architectures ont les instructions intégrées.

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

1
Justin Cormack

Boost, qui possède une licence non intrusive, et d'autres frameworks proposent déjà des compteurs atomiques portables, à condition qu'ils soient pris en charge sur la plate-forme cible.

Les bibliothèques tierces sont bonnes pour nous. Et si, pour des raisons étranges, votre société vous interdit de les utiliser, vous pouvez quand même regarder comment elles procèdent (tant que la licence le permet pour votre usage) et mettre en œuvre ce que vous recherchez.

1
Luc Hermitte

__sync* est certainement (et a été) pris en charge par le compilateur Intel, car GCC a adopté ces versions à partir de là. Lire le premier paragraphe sur cette page . Voir aussi " Compilateur Intel® C++ pour Linux * Référence Intrinsics ", page 198. Il s’agit de 2006 et décrit exactement ces éléments intégrés.

En ce qui concerne la prise en charge de ARM, pour les anciens processeurs ARM: cela ne peut pas être fait entièrement en espace utilisateur, mais cela peut être fait en espace noyau (en désactivant les interruptions pendant l'opération), et je pense avoir lu quelque part que c'est pris en charge depuis un certain temps maintenant.

Selon ce bogue PHP , daté du 2011-10-08, __sync_* n'échouera que le

  • PA-RISC avec autre chose que Linux
  • SPARCv7 et inférieur
  • ARM avec GCC <4.3
  • ARMv5 et inférieur avec autre chose que Linux
  • MIPS1

Donc, avec GCC> 4,3 (et 4,7 est l'actuel), vous ne devriez pas avoir de problème avec ARMv6 ou plus récent. Vous ne devriez pas avoir de problème avec ARMv5 aussi longtemps que la compilation pour Linux.

1
Mecki

J'ai récemment mis en œuvre une telle chose et j'ai été confronté aux mêmes difficultés que vous. Ma solution était fondamentalement la suivante:

  • essayez de détecter les gins intégrés avec la macro de fonctionnalité
  • si non disponible, implémentez quelque chose comme cmpxch avec __asm__ pour les autres architectures (ARM est un peu plus complexe que cela) Il suffit de faire cela pour une taille possible, par exemple sizeof(int).
  • implémenter toutes les autres fonctionnalités surtop de cette une ou deux primitives avec des fonctions inline
1
Jens Gustedt

Sur Debian/Ubuntu, recommandez ...

Sudo apt-get installez libatomic-ops-dev

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

Compatible GCC et ICC.

comparé aux blocs de construction Intel Thread (TBB), utilisant atomic <T>, libatomic-ops-dev est deux fois plus rapide! (Compilateur Intel)

Test sur les threads producteur-consommateur Ubuntu i7, en connectant 10 millions de pouce dans une connexion de mémoire tampon en 0,5 seconde au lieu de 1,2 seconde pour TBB

Et facile à utiliser, par exemple.

tête AO_t volatile;

AO_fetch_and_add1 (& head);

0
user1408985

Voir: kernel_user_helpers.txt ou entry-arm.c et cherchez __kuser_cmpxchg. Comme indiqué dans les commentaires de other ARM versions Linux, 

kuser_cmpxchg

 Emplacement: 0xffff0fc0 

 Prototype de référence: 

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

 Entrée: 

 r0 = oldval 
 r1 = newval 
 r2 = ptr 
 lr = adresse de retour 

 Sortie: 

 r0 = code de succès (zéro ou différent de zéro) 
 Indicateur C = défini si r0 == 0, efface si r0! = 0 

 Registres obstrués: 

 r3, ip, flags 

 Définition: 

 Stockez atomiquement newval dans * ptr uniquement si * ptr est égal à oldval .
 Retourne zéro si * ptr a été changé ou différent de zéro si aucun échange n'a eu lieu .
 Le drapeau C est également défini si * ptr a été modifié pour permettre l’assemblage 
 optimisation dans le code d'appel .

 Exemple d'utilisation: 
 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;
}

Remarques:

  • Cette routine inclut déjà des barrières de mémoire en fonction des besoins.
  • Valide uniquement si __kuser_helper_version> = 2 (à partir de la version 2.6.12 du noyau).

Ceci est destiné à être utilisé avec Linux avec ARMv3 en utilisant la primitive swp. Vous devez avoir un très ancien ARM = pour ne pas supporter cela. Seul un {annulation des données} ou interruption peut entraîner l'échec de la rotation. Le noyau surveille donc cette adresse ~ 0xffff0fc0 et effectue un espace utilisateur. _ PC corrige quand un abandon de données ou un interruption se produit. Toutes les bibliothèques d’espace utilisateur prenant en charge ARMv5 et versions antérieures utiliseront cette fonctionnalité.

Par exemple, QtConcurrent l'utilise.

0
artless noise