Red de conocimiento informático - Problemas con los teléfonos móviles - La diferencia entre las operaciones atómicas de arquitectura x86 y arm

La diferencia entre las operaciones atómicas de arquitectura x86 y arm

Primero, compare el núcleo único:

Dado que x86 pertenece al conjunto de instrucciones CISC y permite dos operaciones de memoria en una instrucción, en condiciones de un solo núcleo, estas operaciones en i e i__ son, por supuesto, operaciones atómicas. , se debe utilizar el modo de visualización de addl r, 1 para completar las operaciones de lectura y escritura en una instrucción.

El brazo pertenece al conjunto de instrucciones RISC y solo puede haber una operación de memoria durante la ejecución de una instrucción. Por lo tanto, operaciones como i e i, que requieren leer el valor de la memoria primero y luego. asignándolo, en la arquitectura arm No se puede completar en una instrucción, por lo que no satisface las operaciones atómicas. Entonces, cómo implementar operaciones atómicas:

Echemos un vistazo al código

<. p> Para atomic_add

La implementación de x86 es muy simple:

static __inline__ void atomic_add(int i, atomic_t *v)

{

__asm__ __volatile__(

BLOQUEO "addl 1,0"

: "=m" (v-gt; contador)

: "ir" (i), "m" (v-gt ;counter));

}

LOCK está vacío en casos de un solo núcleo.

Echemos un vistazo a atomic_add_and_test:

static __inline__ int hal_atomic_add_and_test(int i, emcos_atomic_t *v)

{

unsigned char c;

__asm__ __volatile__(

LOCK "addl 2, 0; sete 1"

: "=m" (v-gt; val), " =qm" (c)

: "ir" (i), "m" (v-gt; val): "memoria");

Devuelve c;

}

Tal vez estés pensando, ahora que hay dos instrucciones, ¿puede ser una instrucción atómica? Definitivamente, ¿por qué es eso?

Debemos tener en cuenta que el set 1 es una instrucción condicional, y esta condición (cflags) está relacionada con el proceso. Incluso si el proceso se ejecuta después de add1 2,0, se produce una interrupción en este momento y cambia. a otro proceso. , cuando regresa a cflags sigue siendo el mismo proceso y no ocurre la misma situación de cambio, por lo que es atómico.

Para arm, esto requiere más trabajo:

#define atomic_add(i, v) (void) atomic_add_return(i, v)

static inline int atomic_add_return (int i, atomic_t *v)

estática en línea int atomic_add_return(int i, atomic_t t *v)

{

banderas largas sin firmar

int val;

local_irq_save(flags);

val = v-gt;

v-gt; ;

local_irq_restore(flags);

return val

}

Como se puede ver en lo anterior, está cerrado por Se implementa mediante interrupciones. ¿Por qué necesitamos desactivar las interrupciones para operaciones atómicas?

Análisis:

arm for i produce el siguiente código:

1 ldr r0, =i

2 mov [r0], r1 //esta operación de lectura de memoria

3 inc r1 //si ocurre una interrupción en este momento, y luego en el controlador de interrupciones también realiza la operación i, no es una operación atómica

4 mov r1,[r0] //esta operación de escritura en memoria

Asumiendo I = 0.p> Asumiendo I=0. Si el proceso 1 ejecuta i , se interrumpe en 3 , y luego también ejecuta i en la interrupción, cuando se ejecutan ambos i, i=1 en lugar de 2 como queremos, que es el resultado de una operación no atómica.

Cómo solucionarlo, es decir, el código 2-4 no se ejecuta o garantiza la atomicidad después de la ejecución. Esto se puede lograr en un solo núcleo desactivando las interrupciones. Por qué las interrupciones están desactivadas arriba.

2. Situación multinúcleo;

Arquitectura x86:

Una sola instrucción no es una operación atómica, como addl r, 1. La operación de dos memorias tampoco es una operación atómica, es posible que al realizar la siguiente operación de memoria, otro núcleo también lea la memoria, lo que también provocará que dos resultados incorrectos de las operaciones i sean 1.

La solución es utilizar el flag LOCK para bloquear el bus mientras se ejecuta la instrucción para que otros núcleos no puedan leerla, obteniendo así operaciones atómicas.

arquitectura arm:

arm solo tiene múltiples núcleos después de la serie v6, pero también tiene un mecanismo de operación atómica de memoria dedicada, a saber, instrucciones ldrex y strex.

El código fuente es el siguiente:

static inline int atomic_add_return(int i, atomic_t *v)

{

unsigned long tmp;

int resultado;

__asm ​​​​__ __volatile__("@ atomic_add_return\n"

"1: ldrex 0, [2]\n"

" agregar 0, 0, 3\n"

" strex 1, 0, [2]\n"

" teq 1, #0\ n"<

" bne 1b"

: "=amp;r" (resultado), "=amp;r" (tmp)

: " r" (amp; v-gt; contador), "Ir" (i)

: "cc");

devolver resultado;

}