¿Cómo realizar operaciones atómicas en Linux que funcionan en x86, arm, GCC y icc?


Cada sistema operativo moderno proporciona hoy en día algunas operaciones atómicas:

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

¿Algo así para Linux?

  • Lo necesito para trabajar en la mayoría de las plataformas soportadas por Linux, incluyendo: x86, x86_64 y arm.
  • Lo necesito para trabajar al menos en el compilador GCC e Intel.
  • No necesito usar la biblioteca 3rd par como glib o qt.
  • Necesito que funcione en C++ (C no es necesario)

Cuestiones:

  • GCC atomic builtins __sync_* no son compatibles con todas las plataformas (ARM) y no son compatibles con el compilador Intel.
  • AFAIK <asm/atomic.h> no debe usarse en el espacio de usuario y no lo he utilizado con éxito en absoluto. Además, no estoy seguro de si funcionaría con el compilador Intel.

Alguna sugerencia?

Sé que hay muchas preguntas relacionadas, pero algunas de ellas apunta a __sync* que no es factible para mí (ARM) y algunos apuntan a asm/atomic.h.

Tal vez hay una biblioteca de ensamblado en línea que hace esto para GCC (ICC soporta ensamblado gcc)?

Editar:

Hay una solución muy parcial para agregar operaciones solamente (permite implementar contador atómico pero no bloquear estructuras libres que requieren CAS):

Si usas libstc++ (el compilador de Intel usa libstdc++) entonces puedes usar __gnu_cxx::__exchange_and_add que se define en <ext/atomicity.h> o <bits/atomicity.h>. Depende de versión del compilador.

Sin embargo, todavía me gustaría ver algo que soporte CAS.

9 answers

Los proyectos están usando esto:

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

Si desea operaciones simples como CAS, ¿no puede simplemente usar las implementaciones específicas de arch desde el núcleo, y hacer comprobaciones de arch en el espacio de usuario con autotools/cmake? En cuanto a las licencias, aunque el núcleo es GPL, creo que es discutible que el ensamblado en línea para estas operaciones es proporcionado por Intel/AMD, no que el núcleo tenga una licencia sobre ellas. Simplemente pasan estar en una forma fácilmente accesible en el código fuente del núcleo.

 19
Author: Noah Watkins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-14 17:43:13

Los estándares recientes (de 2011) de C & C++ ahora especifican operaciones atómicas:

De todos modos, es posible que su plataforma o compilador no admita estas nuevas cabeceras y características.

 12
Author: kevinarpe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-16 10:08:46

Maldición. Iba a sugerir a los primitivos del CCG, luego dijiste que estaban fuera de los límites. :-)

En ese caso, haría un #ifdef para cada combinación de arquitectura/compilador que te importe y codificaría el asm inline. Y tal vez busque __GNUC__ o alguna macro similar y use las primitivas de GCC si están disponibles, porque se siente mucho más correcto usarlas. :-)

Va a tener mucha duplicación y puede ser difícil verificar la corrección, pero esto parece ser la forma en que muchos proyectos hacen esto, y he tenido buenos resultados con él.

Algunas trampas que me han mordido en el pasado: cuando use GCC, no olvide "asm volatile" y clobbers para "memory" y "cc", etc.

 3
Author: asveikau,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-18 09:41:27

Boost, que tiene una licencia no intrusiva, y otros frameworks ya ofrecen contadores atómicos portátiles as siempre y cuando sean compatibles con la plataforma de destino.

Las bibliotecas de terceros son buenas para nosotros. Y si por razones extrañas su empresa le prohíbe usarlos, todavía puede echar un vistazo a cómo proceden (siempre y cuando la licencia lo permita para su uso) para implementar lo que está buscando.

 1
Author: Luc Hermitte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-02-18 12:09:50

Recientemente hice una implementación de tal cosa y me enfrenté a las mismas dificultades que ustedes. Mi solución fue básicamente la siguiente:

  • intenta detectar los builtins de gcc con la macro de características
  • si no está disponible, solo implemente algo como cmpxch con __asm__ para las otras arquitecturas (ARM es un poco más complicado que eso). Solo haz eso para un tamaño posible, por ejemplo sizeof(int).
  • implementar todas las demás funcionalidades en la parte superior de que uno o dos primitivos con inline funciones
 1
Author: Jens Gustedt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-01-13 18:02:54

Aquí hay un parche para GCC para apoyar las operaciones atómicas de ARM. No le ayudará en Intel, pero podría examinar el código - hay soporte de kernel reciente para arquitecturas ARM más antiguas, y las más nuevas tienen las instrucciones incorporadas, por lo que debería ser capaz de construir algo que funcione.

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

 1
Author: Justin Cormack,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-03-16 13:35:22

__sync* ciertamente es (y ha sido) soportado por el compilador Intel, porque GCC adoptó estos build-ins desde allí. Lea el primer párrafo en esta página. Consulte también" Intel® C++ Compiler for Linux* Intrinsics Reference", página 198. Es de 2006 y describe exactamente los incorporados.

Con respecto al soporte ARM, para CPU ARM más antiguas: no se puede hacer completamente en el espacio de usuario, pero se puede hacer en el espacio de núcleo (deshabilitando las interrupciones durante la operación), y creo que leí en alguna parte que se apoya desde hace bastante tiempo.

De acuerdo con este error de PHP , con fecha 2011-10-08, __sync_* solo fallará en

  • PA-RISC con cualquier otra cosa que no sea Linux
  • SPARCv7 e inferior
  • BRAZO con CCG
  • ARMv5 y versiones inferiores con cualquier otra cosa que no sea Linux
  • MIPS1

Así que con GCC > 4.3 (y 4.7 es el actual), no debería tener un problema con ARMv6 y versiones posteriores. No deberías tener problema con ARMv5 ya sea mientras compile para Linux.

 1
Author: Mecki,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-04-12 02:17:06

En Debian/Ubuntu recomendar...

Sudo apt-get install libatomic-ops-dev

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

Compatible con GCC e ICC.

En comparación con Intel Thread Building Blocks (TBB), utilizando atomic, libatomic-ops-dev es más del doble de rápido! (Compilador Intel)

Testing on Ubuntu i7 producer-consumer threads piping 10 million ints down a ring buffer connection in 0.5 secs as opposed to 1.2 secs for TBB

Y fácil de usar, por ejemplo,

Cabeza volátil AO_t;

AO_fetch_and_add1 (&head);

 0
Author: ,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-05-21 23:33:52

Ver: kernel_user_helpers.txt or entry-arm.c y busque __kuser_cmpxchg. Como se ve en los comentarios de otras versiones de 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;
}

Notas:

  • Esta rutina ya incluye barreras de memoria según sea necesario.
  • Válido solo si __kuser_helper_version >= 2 (desde la versión del kernel 2.6.12).

Esto es para usar con Linux con ARMv3 usando la primitiva swp. Debes tener un BRAZO muy antiguo para no apoya esto. Solo un data abort o interrupt puede causar que el giro falle, por lo que el núcleo monitorea esta dirección ~0xffff0fc0 y realiza un espacio de usuario PC se soluciona cuando se produce un aborto de datos o una interrupción . Todas las bibliotecas de espacio de usuario que admiten ARMv5 y versiones inferiores utilizarán esta función.

Por ejemplo, QtConcurrent usa esto.

 0
Author: artless noise,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-06-14 14:32:29