2010-04-21 17 views
7

Estoy tratando de crear un mutex de bloqueo de giro realmente simple en C y por alguna razón estoy obteniendo casos en los que dos hilos obtienen el bloqueo al mismo tiempo, lo que no debería ser posible. Se ejecuta en un sistema multiprocesador, que puede ser el motivo por el cual hay un problema. ¿Alguna idea de por qué no está funcionando?Implementando un bloqueo Mutex en C

void mutexLock(mutex_t *mutexlock, pid_t owner) 
{ 
int failure = 1; 
while(mutexlock->mx_state == 0 || failure || mutexlock->mx_owner != owner) 
{ 
    failure = 1; 
    if (mutexlock->mx_state == 0) 
    { 
     asm(
     "movl $0x01,%%eax\n\t"  // move 1 to eax 
     "xchg %%eax,%0\n\t"   // try to set the lock bit 
     "mov  %%eax,%1\n\t"   // export our result to a test var 
     :"=r"(mutexlock->mx_state),"=r"(failure) 
     :"r"(mutexlock->mx_state) 
     :"%eax" 
     ); 
    } 
    if (failure == 0) 
    { 
     mutexlock->mx_owner = owner; //test to see if we got the lock bit 
    } 
    } 
} 
+0

El objetivo de un mutex es que pueda ejecutarse en varios procesadores. El objetivo es mantener segura la sección crítica y debería ser así sin importar cuántos procesadores haya en el sistema. – Kaili

+0

Oooh pero me gusta el código, muy bien. Nunca he hecho nada con asm de c. – Kaili

+0

¿Su O/s no ofrece mutex? ¿No podría considerar uno que lo haga? Lo siento, pero esto suena a reinventar la rueda, aunque supongo que tienes una buena razón para hacerlo. – Mawg

Respuesta

7

Bueno para empezar que está probando una variable sin inicializar (failure) la primera vez que se ejecuta la condición while().

Su problema real es que le está diciendo a gcc que use un registro para mx_state - que claramente no funcionará para un spinlock. Proveedores:

asm volatile (
    "movl $0x01,%%eax\n\t"  // move 1 to eax 
    "xchg %%eax,%0\n\t"   // try to set the lock bit 
    "mov  %%eax,%1\n\t"   // export our result to a test var 
    :"=m"(mutexlock->mx_state),"=r"(failure) 
    :"m"(mutexlock->mx_state) 
    :"%eax" 
    ); 

Tenga en cuenta que asm volatile es también importante en este caso, para asegurarse de que no quede izada fuera de su bucle while.

+0

Tienes razón, pero ese no era el problema ... – Adam

+0

@Adam: mira la actualización. – caf

+0

Heh ... gracias, Gcc Extended Inline Assembly es bastante nuevo para mí. Eso parece que resuelve el problema muy bien :). – Adam

3

El problema es que carga mx_state en un registro (la restricción 'r') y luego realiza el intercambio con los registros, solo escribe el resultado en mx_state al final del código de asm. Lo que queremos es algo más parecido a

asm(
    "movl $0x01,%%eax\n\t"  // move 1 to eax 
    "xchg %%eax,%1\n\t"   // try to set the lock bit 
    "mov  %%eax,%0\n\t"   // export our result to a test var 
    :"=r"(failure) 
    :"m" (mutexlock->mx_state) 
    :"%eax" 
    ); 

Incluso esto es algo peligroso, ya que, en teoría, el compilador podría cargar el mx_state, verterlo en una ranura pila temporal local, y hacer lo xchg allí. También es algo ineficiente, ya que tiene movs extra codificados que pueden no ser necesarios pero que no pueden ser eliminados por el optimizador. Es mejor que el uso de un simple asm que se expande a una sola instrucción, tales como

failure = 1; 
asm("xchg %0,0(%1)" : "=r" (failure) : "r" (&mutex->mx_state), "0" (failure)); 

Nota cómo forzar el uso de mx_state en su lugar, utilizando su dirección en lugar de su valor.