Diferencia entre revisiones de «Cerrojos»

De Wiki de Sistemas Operativos
Saltar a: navegación, buscar
(Cerrojos mediante instrucciones atómicas)
 
(No se muestran 14 ediciones intermedias de 9 usuarios)
Línea 4: Línea 4:
  
 
<source lang="c">
 
<source lang="c">
int cerrojo = 1;      /* Inicializamos la variable cerrojo. Esto sería sólo válido para
+
int cerrojo = 0;      /* Inicializamos la variable cerrojo. Esto sería sólo válido para
                         hilos, ya que los procesos no comparten espacio de memoria */
+
                         hilos, ya que los procesos no comparten espacio de memoria. La variable se inicializa FUERA de los hilos.
 +
                        Del while en adelante, ya es código perteneciente a los hilos en ejecución. */
  
while(cerrojo == 0);  // Protocolo de
+
while(cerrojo == 1);  // Protocolo de              
cerrojo = 0;          // entrada
+
cerrojo = 1;          // entrada
  
 
...                  // Sección crítica
 
...                  // Sección crítica
  
cerrojo = 1;          // Protocolo de salida
+
cerrojo = 0;          // Protocolo de salida
 
</source>
 
</source>
  
Sin embargo, esto es problemático: Supongamos que tenemos únicamente un proceso con dos hilos (Hx y Hy) y el planificador retire al proceso Hx justo antes de ejecutarse la instrucción ''cerrojo = 0;''. El proceso Hy ejecuta su código, entra en la sección crítica (Esto puede ocurrir ya que cerrojo sigue a 1) y el planificador le retira el uso del procesador dentro de ésta. A continuación, se le vuelve a asignar el procesador a Hx.
+
Sin embargo, esto es problemático: Supongamos que tenemos únicamente un proceso con dos hilos (Hx y Hy) y el planificador retira el hilo Hx justo antes de ejecutarse la instrucción ''cerrojo = 1;''. El hilo Hy ejecuta su código, entra en la sección crítica (Esto puede ocurrir ya que cerrojo sigue a 0) y el planificador le retira el uso del procesador dentro de ésta. A continuación, se le vuelve a asignar el procesador a Hx.
  
 
De esta forma, tenemos a dos hilos ejecutando código de la sección crítica. Para evitar este problema, debemos implementar los protocolos de entrada y salida de forma que se ejecuten en una sola instrucción, con lo cual o el planificador retira el uso del procesador antes de esta instrucción, o sino lo retira después de haberla ejecutado.
 
De esta forma, tenemos a dos hilos ejecutando código de la sección crítica. Para evitar este problema, debemos implementar los protocolos de entrada y salida de forma que se ejecuten en una sola instrucción, con lo cual o el planificador retira el uso del procesador antes de esta instrucción, o sino lo retira después de haberla ejecutado.
 
  
 
= Cerrojos mediante instrucciones atómicas =
 
= Cerrojos mediante instrucciones atómicas =
Línea 36: Línea 36:
 
</source>
 
</source>
  
El método lock comprueba mediante __sync_locktest_and_set(&cerrojo,1) el valor del cerrojo, si es 0, cambia el valor del cerrojo a 1 y devuelve 0, por tanto, se sale del while, si es 1, devuelve 1 y continúa comprobando el valor, por tanto, no entra en situación crítica.
+
El método lock comprueba mediante __sync_locktest_and_set(&cerrojo,1) el valor del cerrojo: si es 0, cambia el valor del cerrojo a 1 y devuelve 0, por tanto, se sale del while y entra en sección crítica; si es 1, devuelve 1 y continúa comprobando el valor, por tanto, no entra en la sección crítica hasta que cambie el valor del cerrojo.
  
El método unlock simplemente, mediante _sync_lock_release(&cerrojo) cambia el valor del cerrojo a 0.
+
El método unlock, mediante _sync_lock_release(&cerrojo), cambia el valor del cerrojo a 0.
  
 
* http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
 
* http://gcc.gnu.org/onlinedocs/gcc-4.1.2/gcc/Atomic-Builtins.html
 
* http://stackoverflow.com/questions/1383363/is-my-spin-lock-implementation-correct-and-optimal
 
* http://stackoverflow.com/questions/1383363/is-my-spin-lock-implementation-correct-and-optimal
 +
 +
 +
 +
5.4[[Ejercicio_de_concurrencia_de_procesos | Ejercicio de concurrencia de procesos]]
 +
 +
5.5[[Semáforos | Semáforos]]

Revisión actual del 17:30 2 abr 2020

Ejemplo de implementación

El siguiente código sería un posible ejemplo del uso de control de concurrencia mediante cerrojos:

int cerrojo = 0;      /* Inicializamos la variable cerrojo. Esto sería sólo válido para
                         hilos, ya que los procesos no comparten espacio de memoria. La variable se inicializa FUERA de los hilos.
                         Del while en adelante, ya es código perteneciente a los hilos en ejecución. */

while(cerrojo == 1);  // Protocolo de               
cerrojo = 1;          // entrada

...                   // Sección crítica

cerrojo = 0;          // Protocolo de salida

Sin embargo, esto es problemático: Supongamos que tenemos únicamente un proceso con dos hilos (Hx y Hy) y el planificador retira el hilo Hx justo antes de ejecutarse la instrucción cerrojo = 1;. El hilo Hy ejecuta su código, entra en la sección crítica (Esto puede ocurrir ya que cerrojo sigue a 0) y el planificador le retira el uso del procesador dentro de ésta. A continuación, se le vuelve a asignar el procesador a Hx.

De esta forma, tenemos a dos hilos ejecutando código de la sección crítica. Para evitar este problema, debemos implementar los protocolos de entrada y salida de forma que se ejecuten en una sola instrucción, con lo cual o el planificador retira el uso del procesador antes de esta instrucción, o sino lo retira después de haberla ejecutado.

Cerrojos mediante instrucciones atómicas

Podemos implementar cerrojos mediante instrucciones especiales de comprobación y puesta a 0 y 1.

int cerrojo = 0;

void lock() {
    while (__sync_lock_test_and_set(&cerrojo, 1));
}

void unlock() {
    __sync_lock_release(&cerrojo);
}

El método lock comprueba mediante __sync_locktest_and_set(&cerrojo,1) el valor del cerrojo: si es 0, cambia el valor del cerrojo a 1 y devuelve 0, por tanto, se sale del while y entra en sección crítica; si es 1, devuelve 1 y continúa comprobando el valor, por tanto, no entra en la sección crítica hasta que cambie el valor del cerrojo.

El método unlock, mediante _sync_lock_release(&cerrojo), cambia el valor del cerrojo a 0.


5.4 Ejercicio de concurrencia de procesos

5.5 Semáforos