2011-01-19 10 views
6

Después de haber estado leyendo a través Understanding the Linux kernel (Bovet & Cesati), el capítulo sobre el kernel de sincronización indica que el código de adquisición de bloqueo de giro se reduce a:¿Son los spinlocks de Linux/SMP innecesariamente lentos?

1: lock: 
    btsl $0, slp 
    jnc  3 
2: testb $1, slp 
    jne  2 
    jmp  1 
3: 

Ahora principio pensé que parecía un desperdicio tener bucles anidados y se podía poner en práctica algo así como :

1: lock: 
    btsl $0, slp 
    jc  1 

que sería mucho más simple. Sin embargo, veo por qué lo hicieron, ya que el lock afecta a las otras CPU y los tiempos para el btsl son más grandes que los de un simple testb.

Lo único que I no tengo he podido entender es la posterior publicación del bloqueo de giro. El libro afirma que se obtiene la siguiente:

lock: 
    btrl $0, slp 

Mi pregunta es básicamente por qué? Me parece que un combo lock/mov-immediate es más rápido.

No necesita obtener el estado antiguo en la bandera de acarreo ya que, siguiendo la regla de que el núcleo está libre de errores (se asume en muchos otros lugares dentro de dicho kernel), el estado anterior será 1 (usted no intentaría liberarlo si aún no lo hubiera adquirido).

Y un mov es mucho más rápido que un btrl, al menos en el 386.

Entonces, ¿qué me falta?

¿Han cambiado los tiempos para esas instrucciones en chips posteriores?

¿Se ha actualizado el kernel desde que se imprimió el libro?

¿Es el libro simplemente incorrecto (o muestra instrucciones simplificadas)?

¿He omitido algún otro aspecto relacionado con la sincronización entre CPU que las instrucciones más rápidas no satisfacen?

Respuesta

10

Bueno, Understanding the Linux Kernel es viejo. Desde que se escribió, el kernel de Linux se actualizó para usar los denominados tickets spinlocks. El bloqueo es básicamente una cantidad de 16 bits dividida en dos bytes: llamemos a uno Next (como el siguiente ticket en un dispensador) y el otro Owner (como el número 'Ahora sirviendo' en un contador). Un spinlock se inicializa con ambas partes configuradas a cero. Locking toma nota del valor del spinlock e incrementa Next, atómicamente. Si el valor de Siguiente antes de incrementar es igual a Propietario, se ha obtenido el bloqueo. De lo contrario, gira hasta que el Propietario se incrementa al valor correcto, y así sucesivamente.

El código correspondiente está en asm/spinlock.h (para x86). La operación de desbloqueo es de hecho mucho más rápido y sencillo que el libro dice:

static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock) 
{ 
    asm volatile(UNLOCK_LOCK_PREFIX "incb %0" 
     : "+m" (lock->slock) 
     : 
     : "memory", "cc"); 
} 

desde inc es de unos 8 o 9 veces más rápido que btr.

Espero que esto ayude; si no, estaré feliz de cavar más profundo.

Cuestiones relacionadas