¿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.
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:
- C11:
stdatomic.h
- C++11:
std::atomic
De todos modos, es posible que su plataforma o compilador no admita estas nuevas cabeceras y características.
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.
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.
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 ejemplosizeof(int)
. - implementar todas las demás funcionalidades en
la parte superior de que uno o dos primitivos
con
inline
funciones
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.
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.
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);
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.
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