Comment garantir que les écritures 64 bits sont atomiques?
Quand peut-64-bits écritures être garanti d'être atomique, lors de la programmation en C sur un processeur Intel x86 plate-forme (en particulier, un Mac à processeur Intel fonctionnant MacOSX 10.4 en utilisant le compilateur Intel)? Par exemple:
unsigned long long int y;
y = 0xfedcba87654321ULL;
/* ... a bunch of other time-consuming stuff happens... */
y = 0x12345678abcdefULL;
Si un autre thread est en examinant la valeur de y après la première cession à y a fini de s'exécuter, je voudrais vous assurer qu'il voit soit la valeur 0xfedcba87654321 ou la valeur 0x12345678abcdef, et non pas un mélange d'entre eux. Je voudrais le faire sans aucun blocage, et si possible sans code supplémentaire. Mon espoir est que, lors de l'utilisation d'un compilateur 64 bits (64 bits Intel compilateur), sur un système d'exploitation capable de supporter de code 64 bits (MacOSX 10.4), que ces 64 bits écrit sera atomique. Est-ce toujours vrai?
source d'informationauteur
Vous devez vous connecter pour publier un commentaire.
Votre meilleur pari est d'éviter d'essayer de construire votre propre système de primitives, et au lieu d'utiliser le verrouillage à moins qu'il vraiment apparaît comme un point chaud lorsque le profilage. (Si vous pensez que vous pouvez être intelligent et d'éviter des écluses, n'en ont pas. Vous n'êtes pas. C'est le général "vous" qui me comprend et tout le monde.) Vous devriez au minimum l'utilisation d'un verrou de rotation, voir spinlock(3). Et quoi que vous fassiez, ne pas essayer de mettre en œuvre "votre propre" verrous. Vous l'aurez tort.
En fin de compte, vous devez utiliser quel que soit le verrouillage ou d'opérations atomiques votre système d'exploitation. L'obtention de ces sortes de choses exactement dans tous les cas est extrêmement difficile. Souvent, il peut impliquer la connaissance des choses comme les errata pour des versions spécifiques de processeur spécifique. ("Oh, la version 2.0 de ce processeur ne fait pas le cache-cohérence de l'espionnage au bon moment, c'est corrigé dans la version 2.0.1 mais sur la 2.0, vous devez insérer un
NOP
.") Juste gifler unvolatile
mot-clé sur une variable en C est presque toujours insuffisante.Sur Mac OS X, cela signifie que vous devez utiliser les fonctions énumérées dans atomique(3) pour effectuer vraiment atomique-dans-tous-les Processeurs opérations sur 32-bit, 64-bit, et le pointeur de la taille d'quantités. (Utilisez ce dernier pour toutes les opérations atomiques sur les pointeurs si vous êtes 32/64-bit compatible automatiquement.) Qui va si vous voulez faire les choses comme atomique compare-and-swap, incrémenter/décrémenter, de la rotation de verrouillage, ou la pile/gestion de file d'attente. Heureusement, le spinlock(3)atomique(3)et barrière(3) fonctions doivent tous fonctionner correctement sur tous les Processeurs qui sont pris en charge par Mac OS X.
Sur x86_64, à la fois le processeur Intel et d'un compilateur gcc en charge certains intrinsèque atomique-fonctions d'exploitation. Voici la documentation de gcc: http://gcc.gnu.org/onlinedocs/gcc-4.1.0/gcc/Atomic-Builtins.html
Le compilateur Intel docs aussi en parler ici: http://softwarecommunity.intel.com/isn/downloads/softwareproducts/pdfs/347603.pdf (page 164 ou environs).
Selon le Chapitre 7 de Partie 3A - Guide de Programmation Système de l'Intel processeur manuelsquadword accède sera effectuée automatiquement s'ils sont alignés sur une limite de 64 bits, sur un Pentium ou plus récent, et non alignés (si elle est encore à l'intérieur d'une ligne de cache) sur un P6 ou plus récent. Vous devez utiliser
volatile
pour s'assurer que le compilateur ne cherche pas le cache de l'écrire dans une variable, et vous pouvez avoir besoin d'utiliser un mémoire en clôture de routine pour s'assurer que l'écriture qui se passe dans le bon ordre.Si vous avez besoin de la base de la valeur écrite sur une valeur existante, vous devez utiliser votre système d'exploitation Contrefil caractéristiques (par exemple, Windows a InterlockedIncrement64).
Sur MacOSX Intel, vous pouvez utiliser le système intégré des opérations atomiques. Il n'y a pas fourni atomique d'obtenir ou de définir, soit 32 ou 64 bits entiers, mais vous pouvez le construire hors de la condition CompareAndSwap. Vous pouvez effectuer des recherches XCode documentation des diverses OSAtomic fonctions. J'ai écrit la version 64 bits ci-dessous. La version 32 bits qui peut être fait de la même façon avec les fonctions nommées.
Noter que Apple OSAtomicCompareAndSwap fonctions atomiquement effectuer l'opération:
Nous l'utiliser dans l'exemple ci-dessus pour créer une méthode de Jeu par le premier saisissant l'ancienne valeur, puis tentant de swap de la mémoire cible de la valeur. Si le swap, qui réussit, qui indique que la mémoire de valeur est toujours l'ancienne valeur au moment de l'échange, et il est donné la nouvelle valeur au cours de l'échange (qui lui-même est atomique), de sorte que nous avons fini. S'il ne réussit pas, puis un autre thread a entravé par la modification de la valeur dans l'entre-deux quand on a attrapé et quand nous avons essayé de le réinitialiser. Si cela se produit, nous ne pouvons tout simplement de la boucle et essayez de nouveau avec un minimum de peine.
L'idée derrière la méthode Get est que nous pouvons tout d'abord extraire la valeur (qui peut ou peut ne pas être la valeur réelle, si un autre thread est interférents). Nous pouvons alors essayer de swap de la valeur par lui-même, il suffit de vérifier que la prise initiale est égale à la valeur atomique.
Je n'ai pas vérifié cette contre mon compilateur, s'il vous plaît excuser les fautes de frappe.
Vous avez mentionné OSX en particulier, mais dans le cas où vous avez besoin de travailler sur d'autres plateformes, Windows a un certain nombre de Contrefil* fonctions, et vous pouvez rechercher dans la documentation MSDN pour eux. Certains d'entre eux travaillent sur Windows 2000 Pro et plus tard, et certains (en particulier certains des 64 bits fonctions) sont de nouveaux avec Vista. Sur d'autres plates-formes, les versions de GCC 4.1 et plus tard avoir une variété d' __sync* fonctions, telles que l' __synchronisation_fetch_et_add(). Pour les autres systèmes, vous pouvez avoir besoin d'utiliser de l'assemblée, et vous pouvez trouver des implémentations dans le SVN de navigateur pour la HaikuOS projet, à l'intérieur de la src/system/libroot/os/arch.
Sur X86, le moyen le plus rapide pour atomiquement écrire un alignées valeur de 64 bits est d'utiliser FISTP. Pour les non alignés valeurs, vous devez utiliser un CAS2 (_InterlockedExchange64). Le CAS2 opération est assez lent en raison de BUSLOCK bien souvent, il peut être plus rapide pour vérifier l'alignement et de faire le FISTP version pour les adresses alignées. En effet, c'est la façon dont le Intel Filetée Blocs de construction implémente Atomique 64 bits écrit.
GCC a intrinsèques pour les opérations atomiques; je soupçonne que vous pouvez faire la même avec d'autres compilateurs, trop. Ne jamais compter sur le compilateur pour les opérations atomiques; optimisation sera presque certainement courir le risque de faire de même, évidemment, des opérations atomiques dans le non-atomique chers, sauf si vous indiquez explicitement au compilateur de ne pas le faire.
Si vous voulez faire quelque chose comme ça pour interthread ou interprocess communication, alors vous avez besoin d'avoir plus que juste un atomique de lecture/écriture de garantie. Dans votre exemple, il semble que vous voulez que les valeurs écrites pour indiquer qu'une partie du travail est en cours et/ou a été terminé. Vous aurez besoin de faire plusieurs choses, qui ne sont pas tous portable, pour s'assurer que le compilateur a fait les choses dans l'ordre que vous voulez faire (le mot clé volatile peut aider dans une certaine mesure) et que la mémoire est cohérent. Les processeurs modernes et les caches peuvent effectuer des travaux de l'ordre à l'insu du compilateur, si vous avez vraiment besoin d'un certain support de plate-forme (c'est à dire., serrures ou de plate-forme spécifique contrefil Api) pour faire ce qu'il apparaît que vous voulez faire.
Mémoire"barrière" ou "barrière de mémoire" sont des termes que vous souhaitez.
La dernière version de la norme ISO C (C11) définit un ensemble d'opérations atomiques, y compris
atomic_store(_explicit)
. Voir, par exemple, cette page pour plus d'informations.La deuxième plus portable de la mise en œuvre de atomics sont la GCC intrinsèques, qui ont déjà été mentionnés. Je trouve qu'ils sont entièrement pris en charge par GCC, Clang, Intel et IBM, des compilateurs, et depuis la dernière fois que j'ai vérifié - partiellement pris en charge par le Cray compilateurs.
Un avantage évident de C11 atomics - en plus de l'ensemble de la norme ISO chose - c'est qu'ils prennent en charge une plus précise de la consistance de la mémoire de la prescription. La GCC atomics implique une barrière de mémoire complète autant que je sache.