Spinlock versus semáforo

119

¿Cuáles son las diferencias básicas entre un semáforo y un bloqueo de giro?

¿Cuándo usaríamos un semáforo en lugar de un bloqueo de giro?

iankits
fuente

Respuestas:

134

Spinlock y semáforo se diferencian principalmente en cuatro cosas:

1. ¿Qué son
un spinlock es una posible implementación de una cerradura, es decir, uno que es implementada por la espera activa ( "girar"). Un semáforo es una generalización de un candado (o, al revés, un candado es un caso especial de un semáforo). Por lo general, pero no necesariamente , los bloqueos giratorios solo son válidos dentro de un proceso, mientras que los semáforos también se pueden usar para sincronizar entre diferentes procesos.

Un bloqueo funciona para la exclusión mutua, es decir, un hilo a la vez puede adquirir el bloqueo y proceder con una "sección crítica" de código. Por lo general, esto significa código que modifica algunos datos compartidos por varios subprocesos.
Un semáforo tiene un contador y permitirá ser adquirido por uno o varios hilos, dependiendo del valor que le publiques y (en algunas implementaciones) dependiendo de cuál sea su valor máximo permitido.

En la medida en que se puede considerar una cerradura como un caso especial de un semáforo con un valor máximo de 1.

2. Qué hacen
Como se indicó anteriormente, un spinlock es un bloqueo y, por lo tanto, un mecanismo de exclusión mutua (estrictamente 1 a 1). Funciona consultando y / o modificando repetidamente una ubicación de memoria, generalmente de manera atómica. Esto significa que adquirir un spinlock es una operación "ocupada" que posiblemente quema los ciclos de la CPU durante mucho tiempo (¡quizás para siempre!) Mientras que efectivamente no logra "nada".
El principal incentivo para este enfoque es el hecho de que un cambio de contexto tiene una sobrecarga equivalente a girar unos cientos (o tal vez miles) de veces, por lo que si se puede adquirir una cerradura quemando algunos ciclos girando, esto en general puede muy bien ser más eficiente. Además, para las aplicaciones en tiempo real, puede que no sea aceptable bloquear y esperar a que el programador vuelva a ellas en un momento lejano en el futuro.

Un semáforo, por el contrario, no gira en absoluto, o solo gira durante un tiempo muy corto (como una optimización para evitar la sobrecarga de syscall). Si no se puede adquirir un semáforo, se bloquea, dando tiempo de CPU a un subproceso diferente que está listo para ejecutarse. Por supuesto, esto puede significar que pasan unos milisegundos antes de que su hilo se programe nuevamente, pero si esto no es un problema (generalmente no lo es), entonces puede ser un enfoque muy eficiente y conservador de CPU.

3. Cómo se comportan en presencia de congestión
Es un error común pensar que los spinlocks o los algoritmos sin bloqueo son "generalmente más rápidos", o que solo son útiles para "tareas muy breves" (idealmente, ningún objeto de sincronización debe mantenerse por más tiempo de lo absolutamente necesario, nunca).
La única diferencia importante es cómo se comportan los diferentes enfoques en presencia de congestión .

Un sistema bien diseñado normalmente tiene poca o ninguna congestión (esto significa que no todos los subprocesos intentan adquirir el bloqueo al mismo tiempo). Por ejemplo, normalmente uno no escribiría código que adquiere un bloqueo, luego carga medio megabyte de datos comprimidos en zip de la red, decodifica y analiza los datos y finalmente modifica una referencia compartida (agregar datos a un contenedor, etc.) antes de soltar el bloqueo. En cambio, uno adquiriría el bloqueo solo con el propósito de acceder al recurso compartido .
Dado que esto significa que hay mucho más trabajo fuera de la sección crítica que dentro de ella, naturalmente la probabilidad de que un hilo esté dentro de la sección crítica es relativamente baja y, por lo tanto, pocos hilos compiten por el bloqueo al mismo tiempo. Por supuesto, de vez en cuando, dos subprocesos intentarán adquirir el bloqueo al mismo tiempo (si esto no pudiera suceder, ¡no necesitaría un bloqueo!), Pero esta es más la excepción que la regla en un sistema "saludable". .

En tal caso, un bloqueo de giro supera con creces a un semáforo porque si no hay congestión de bloqueo, la sobrecarga de adquirir el bloqueo de giro es de una mera docena de ciclos en comparación con cientos / miles de ciclos para un cambio de contexto o 10-20 millones de ciclos para perder el resto de un intervalo de tiempo.

Por otro lado, dada la alta congestión, o si el bloqueo se mantiene durante períodos prolongados (¡a veces simplemente no puede evitarlo!), Un bloqueo de giro quemará cantidades increíbles de ciclos de CPU para no lograr nada.
Un semáforo (o mutex) es una opción mucho mejor en este caso, ya que permite que un hilo diferente ejecute tareas útiles durante ese tiempo. O, si ningún otro hilo tiene algo útil que hacer, permite que el sistema operativo desacelere la CPU y reduzca el calor / conserve energía.

Además, en un sistema de un solo núcleo, un bloqueo de giro será bastante ineficiente en presencia de congestión de bloqueo, ya que un hilo giratorio perderá su tiempo completo esperando un cambio de estado que no puede suceder (no hasta que se programe el hilo de liberación, que no es ¡No sucede mientras se ejecuta el hilo de espera!). Por lo tanto, dada cualquier cantidad de contención, adquirir el bloqueo toma alrededor de 1 1/2 porciones de tiempo en el mejor de los casos (asumiendo que el hilo de liberación es el siguiente que se está programando), lo que no es un comportamiento muy bueno.

4. Cómo se implementan
En la actualidad, un semáforo normalmente se ajusta sys_futexbajo Linux (opcionalmente con un spinlock que sale después de algunos intentos).
Un spinlock se implementa normalmente mediante operaciones atómicas y sin utilizar nada proporcionado por el sistema operativo. En el pasado, esto significaba usar intrínsecos del compilador o instrucciones de ensamblador no portátiles. Mientras tanto, tanto C ++ 11 como C11 tienen operaciones atómicas como parte del lenguaje, por lo que, aparte de la dificultad general de escribir código libre de bloqueo comprobablemente correcto, ahora es posible implementar código libre de bloqueo en un dispositivo completamente portátil y (casi) manera indolora.

Damon
fuente
"Además, en un sistema de un solo núcleo, un spinlock será bastante ineficiente en presencia de congestión de bloqueo, ya que un hilo giratorio perderá su tiempo completo esperando un cambio de estado que no puede suceder": también hay (al menos en Linux ) el spin_trylock, que regresa inmediatamente con un código de error, si el bloqueo no se pudo adquirir. Un bloqueo de giro no siempre es tan duro. Pero el uso spin_trylockrequiere, para que una aplicación, esté correctamente diseñada de esa manera (probablemente una cola de operaciones pendientes, y aquí, seleccionando la siguiente, dejando la real en la cola).
Hibou57
El bloqueo de mutex y semáforos no solo es útil en entornos de un solo subproceso, sino también si hay exceso de suscripción, es decir, el número de subprocesos que crea un programa (o varios programas que comparten el sistema) es mayor que el número de recursos de hardware. En estos casos, bloquear su hilo permite que los demás puedan usar el tiempo de la CPU de una manera útil. Además, si el hardware admite hyperthreading, el otro subproceso podría hacer uso de las unidades de ejecución que se están utilizando para realizar el bucle inactivo.
Jorge Bellon
76

De manera muy simple, un semáforo es un objeto de sincronización que "cede", un spinlock es un "ocupado". (Hay un poco más de semáforos en el sentido de que sincronizan varios subprocesos, a diferencia de un mutex o guardia o monitor o sección crítica que protege una región de código de un solo subproceso)

Usaría un semáforo en más circunstancias, pero use un candado giratorio en el que se bloqueará por un tiempo muy corto; el bloqueo tiene un costo, especialmente si bloquea mucho. En tales casos, puede ser más eficiente hacer spinlock durante un rato mientras se espera a que se desbloquee el recurso protegido. Obviamente, hay un impacto en el rendimiento si gira durante demasiado tiempo.

Por lo general, si gira durante más tiempo que un cuanto de hilo, debe usar un semáforo.

gbjbaanb
fuente
27

Más allá de lo que dijeron Yoav Aviram y gbjbaanb, el otro punto clave solía ser que nunca usarías un bloqueo de giro en una máquina con una sola CPU, mientras que un semáforo tendría sentido en una máquina así. Hoy en día, con frecuencia tiene dificultades para encontrar una máquina sin múltiples núcleos, o hyperthreading, o equivalente, pero en las circunstancias en que solo tiene una CPU, debe usar semáforos. (Confío en que la razón es obvia. Si la única CPU está ocupada esperando que otra cosa libere el bloqueo de giro, pero se está ejecutando en la única CPU, es poco probable que se libere el bloqueo hasta que el proceso o subproceso actual sea reemplazado por el O / S, que puede llevar un tiempo y no sucede nada útil hasta que se produce la preferencia).

Jonathan Leffler
fuente
7
Me gustaría segundo lo importante que es no usar spinlocks en sistemas de un solo hilo. Están marcados por problemas de inversión prioritarios. Y créame: no desea depurar este tipo de errores.
Nils Pipenbrinck
2
Los spinlocks están por todas partes en el kernel de Linux, independientemente de si tiene una o más CPU. ¿Qué quieres decir exactamente?
Prof. Falken
@Amigable: por definición, un spinlock significa que el hilo actual en la CPU está esperando que algo más libere el objeto bloqueado. Si lo único activo que puede cambiar el bloqueo es la CPU actual, el bloqueo no se liberará girando. Si algo más, una transferencia DMA u otro controlador de E / S puede liberar el bloqueo, todo está bien. Pero girar cuando nada más puede liberar el bloqueo no es muy sensato; también podría ceder la CPU a otro proceso ahora como esperar a que lo sustituyan.
Jonathan Leffler
1
Puede que me equivoque, pero tenía la impresión de que un kernel de Linux reentrante (CPU única) puede interrumpir un bloqueo de giro en ejecución.
Prof. Falken
2
@Amigable: existe la posibilidad de que yo también me equivoque, pero creo que estoy cerca de la definición clásica de spinlock. Con la programación preventiva, un proceso puede girar en un bloqueo hasta el final de su intervalo de tiempo, o hasta que una interrupción haga que ceda, pero si otro proceso debe proporcionar la condición que permite que el bloqueo de giro se bloquee, un bloqueo de giro no es un problema. buena idea en una sola máquina con CPU. El sistema en el que trabajo tiene bloqueos de giro y tiene un límite superior configurable en el número de giros antes de pasar al modo de espera no ocupado. Este es un bloqueo de giro a nivel de usuario; puede haber una diferencia en el kernel.
Jonathan Leffler
19

Desde controladores de dispositivos Linux por Rubinni

A diferencia de los semáforos, los spinlocks se pueden usar en código que no puede dormir, como los manejadores de interrupciones.

Zbyszek
fuente
8

No soy un experto en kernel, pero aquí hay algunos puntos:

Incluso la máquina monoprocesador puede usar bloqueos de giro si la preferencia del kernel está habilitada mientras se compila el kernel. Si la preferencia del kernel está deshabilitada, el bloqueo de giro (quizás) se expande a una declaración vacía .

Además, cuando intentamos comparar Semaphore vs Spin-lock, creo que semaphore se refiere al que se usa en el kernel, NO al que se usa para IPC (área de usuario).

Básicamente, el bloqueo de giro se utilizará si la sección crítica es pequeña (más pequeña que la sobrecarga de dormir / despertar) y la sección crítica no llama a nada que pueda dormir. Se utilizará un semáforo si la sección crítica es más grande y puede dormir.

Raman Chalotra.

Raman Chalotra
fuente
7

Spinlock se refiere a una implementación de bloqueo entre subprocesos utilizando instrucciones de ensamblaje dependientes de la máquina (como probar y configurar). Se denomina bloqueo de giro porque el hilo simplemente espera en un bucle ("gira") comprobando repetidamente hasta que el bloqueo esté disponible (espera ocupada). Los spinlocks se utilizan como sustituto de los mutex, que son una función proporcionada por los sistemas operativos (no la CPU), porque los spinlocks funcionan mejor si se bloquean durante un período corto de tiempo.

Un Semaphor es una instalación suministrada por sistemas operativos para IPC, por lo que su propósito principal es la comunicación entre procesos. Al ser una función proporcionada por el sistema operativo, su rendimiento no será tan bueno como el de un bloqueo giratorio para el bloqueo entre ejes (aunque es posible). Los semáforos son mejores para bloquear durante períodos de tiempo más largos.

Dicho esto, implementar splinlocks en el ensamblaje es complicado y no portátil.

yoav.aviram
fuente
4
Todas las CPU de subprocesos múltiples necesitan una instrucción de bloqueo de giro ("probar y configurar") y siempre se implementa como una única instrucción en el hardware porque, de lo contrario, siempre habría una condición de carrera en la que más de un subproceso pensó que era "propietario" del recurso protegido.
Richard T
No estoy seguro de que entienda los semáforos ... vea lo que dijo Dijkstra: cs.cf.ac.uk/Dave/C/node26.html
gbjbaanb
POSIX hace una distinción entre un semáforo compartido por hilos y un semáforo compartido por procesos.
Greg Rogers
2
Los semáforos son para la sincronización entre procesos, no para la comunicación.
Johan Bezem
6

Me gustaría agregar mis observaciones, más generales y no muy específicas de Linux.

Dependiendo de la arquitectura de la memoria y las capacidades del procesador, es posible que necesite un bloqueo de giro para implementar un semáforo en un sistema de varios núcleos o procesadores, porque en tales sistemas puede ocurrir una condición de carrera cuando dos o más subprocesos / procesos lo desean adquirir un semáforo.

Sí, si su arquitectura de memoria ofrece el bloqueo de una sección de memoria por un núcleo / procesador retrasando todos los demás accesos, y si sus procesadores ofrecen una prueba y configuración, puede implementar un semáforo sin bloqueo de giro (¡pero con mucho cuidado! ).

Sin embargo, como se diseñan sistemas multinúcleo simples / baratos (estoy trabajando en sistemas integrados), no todas las arquitecturas de memoria admiten estas características de múltiples núcleos / multiprocesador, solo prueba y configuración o equivalente. Entonces, una implementación podría ser la siguiente:

  • adquirir el bloqueo de giro (ocupado esperando)
  • tratar de adquirir el semáforo
  • suelte el bloqueo de giro
  • si el semáforo no se adquirió correctamente, suspenda el hilo actual hasta que se libere el semáforo; de lo contrario continúe con la sección crítica

La liberación del semáforo debería implementarse de la siguiente manera:

  • adquirir el bloqueo de giro
  • suelta el semáforo
  • suelte el bloqueo de giro

Sí, y para semáforos binarios simples en un nivel de sistema operativo, sería posible usar solo un bloqueo de giro como reemplazo. Pero solo si las secciones de código a proteger son realmente muy pequeñas.

Como se dijo antes, si implementa su propio sistema operativo, asegúrese de tener cuidado. Depurar tales errores es divertido (mi opinión, no es compartida por muchos), pero sobre todo muy tedioso y difícil.

Johan Bezem
fuente
1

Un "mutex" (o "bloqueo de exclusión mutua") es una señal que pueden utilizar dos o más procesos asincrónicos para reservar un recurso compartido para uso exclusivo. El primer proceso que obtiene la propiedad del "mutex" también obtiene la propiedad del recurso compartido. Otros procesos deben esperar a que el primer proceso libere su propiedad del "mutex" antes de que puedan intentar obtenerlo.

La primitiva de bloqueo más común en el kernel es el spinlock. El spinlock es un candado de un solo soporte muy simple. Si un proceso intenta adquirir un bloqueo de giro y no está disponible, el proceso seguirá intentándolo (girando) hasta que pueda adquirir el bloqueo. Esta simplicidad crea una cerradura pequeña y rápida.


fuente
1

Spinlock se usa si y solo si está bastante seguro de que el resultado esperado ocurrirá muy pronto, antes de que expire el tiempo de segmento de ejecución de su hilo.

Ejemplo: En el módulo de controlador de dispositivo, el controlador escribe "0" en el registro de hardware R0 y ahora necesita esperar a que ese registro R0 se convierta en 1. El H / W lee el R0 y hace algún trabajo y escribe "1" en R0. Esto es generalmente rápido (en microsegundos). Ahora girar es mucho mejor que irse a dormir e interrumpido por el H / W. Por supuesto, durante el centrifugado, se debe tener cuidado con la condición de falla de H / W.

No hay absolutamente ninguna razón para que una aplicación de usuario gire. No tiene sentido. Vas a girar para que suceda algún evento y ese evento debe ser completado por otra aplicación de nivel de usuario que nunca se garantiza que suceda dentro de un período de tiempo rápido. Entonces, no giraré en absoluto en el modo de usuario. Es mejor dormir () o mutexlock () o semáforo lock () en modo de usuario.

sukumarst
fuente
1

A partir de lo que es la diferencia entre bloqueos de giro y semáforos? por Maciej Piechotka :

Ambos gestionan un recurso limitado. Primero describiré la diferencia entre semáforo binario (mutex) y bloqueo de giro.

Los bloqueos de giro realizan una espera ocupada, es decir, sigue ejecutando un ciclo:

while (try_acquire_resource ()); 
 ...  
lanzamiento();

Realiza un bloqueo / desbloqueo muy ligero, pero si el subproceso de bloqueo será reemplazado por otro que intentará acceder al mismo recurso, el segundo simplemente intentará adquirir el recurso hasta que se agote la cantidad de CPU.
Por otro lado, el mutex se comporta más como:

if (! try_lock ()) {
    add_to_waiting_queue ();
    Espere();
}
...
proceso * p = get_next_process_from_waiting_queue ();
p-> wakeUp ();

Por lo tanto, si el hilo intenta adquirir un recurso bloqueado, se suspenderá hasta que esté disponible para él. Bloquear / desbloquear es mucho más pesado, pero la espera es 'gratuita' y 'justa'.

El semáforo es un candado que puede usarse varias veces (conocido por la inicialización); por ejemplo, se permite que 3 subprocesos retengan simultáneamente el recurso, pero no más. Se utiliza por ejemplo en problemas productores / consumidores o en general en colas:

P (resources_sem)
recurso = recursos.pop ()
...
resources.push (recursos)
V (resources_sem)

¿Diferencia entre semáforo, mutex y spinlock?

Bloqueo en Linux

vinayak jadi
fuente
1
Parece ser una copia / pegado de esto ;-): ¿cuál es la diferencia entre los bloqueos de giro y los semáforos?
Hibou57
0

el bloqueo de giro se puede mantener mediante un solo proceso, mientras que el semáforo se puede mantener mediante uno o más procesos. Spin lock espere hasta que el proceso libere un bloqueo y luego adquiera un bloqueo. El semáforo está durmiendo, es decir, espera y se va a dormir.

sanket31
fuente