Volver al temario
Tema 04 · 70 páginas

Comunicación y sincronización

Tema completo Modo estudio
TEXTO ORIGINAL · Páginas 913 Ver PDF

2.1.1. ¿Por qué es necesaria la sincronización?

Texto íntegro de la conversión. Las figuras y la disposición de las notas se pueden consultar en el PDF.

2.1.1. ¿Por qué es necesaria la sincronización?

Veamos por qué es necesaria la sincronización de los procesos con un caso concreto. Un ejemplo claro de variable compartida2 es el caso de una variable

contador, a la que denominaremos trabajo_pendiente, que se modificada por diferentes procesos (usuario y gestor); tenemos que:

a) Los diferentes procesos usuario que se están ejecutando de modo concurrente en el sistema la actualizan (la incrementan) cada vez que se envía un trabajo3 a la impresora. Así, esta variable compartida indica cuántos trabajos pendientes de impresión hay en el sistema.

b) El proceso gestor de la impresora envía los trabajos a imprimir y actualiza el valor del contador decrementándolo cada vez que finaliza la impresión de un trabajo.

Si la actualización de esta variable contador se efectúa sin ningún tipo de restricción, se pueden generar inconsistencias, es decir, resultados erróneos. En el caso de nuestro ejemplo, estos resultados erróneos podrían repercutir en el proceso de impresión, de manera que se podría dejar de imprimir algún trabajo o intentar imprimir algún trabajo que no existe.

Veamos cómo se pueden generar estas inconsistencias analizando nuestro ejemplo. Como ya hemos indicado antes, un proceso usuario a la hora de enviar un trabajo a imprimir incrementa la variable trabajo_pendiente (figura 1).

Notas y pies de figura de la fuente

(2)Suponemos que los procesos pueden compartir memoria. (3)Los trabajos que los procesos usuario envían a la impresora son ficheros.

Página 10

El proceso gestor de la impresora cada vez que se acaba de imprimir un trabajo decrementa la variable trabajo_pendiente (figura 2).

... trabajo_pendiente = trabajo_pendiente + 1

...

Figura 1. Fragmento de código del proceso de usuario

...

trabajo_pendiente = trabajo_pendiente - 1 ...

Figura 2. Fragmento de código del proceso gestor

Así pues, la variable compartida trabajo_pendiente indica cuántos trabajos están pendientes de ser enviados a la impresora.

El principal problema que se plantea con el código que acabamos de presentar, y en general con el código programado en cualquier lenguaje de alto nivel, es que durante la compilación del programa una instrucción de alto nivel se traduce en una o más instrucciones de lenguaje máquina.

Por ejemplo, en una máquina RISC (reduced instruction set computer) el código generado una vez se ha compilado el código de nuestro ejemplo podría ser el que indicamos a continuación: en el caso del código del proceso usuario (figura 3) y en el caso del código del proceso gestor de la impresora (figura 4).

LOAD R0,trabajo_pendiente ;Copiar el contenido de trabajo_pendiente en el registro R0

INC R0 STORE trabajo_pendiente,R0 ;Almacenar R0 en trabajo_pendiente

Figura 3. Código ensamblador correspondiente al fragmento de código del proceso usuario

LOAD R1,trabajo_pendiente ;Copiar el contenido de trabajo_pendiente en el registro R1 DEC R1 STORE trabajo_pendiente,R1 ;Almacenar R1 en trabajo_pendiente

Figura 4. Código ensamblador correspondiente al fragmento de código del proceso gestor Los detalles de la codificación no son importantes, lo que nos interesa advertir aquí es que durante la ejecución de la instrucción de alto nivel para incrementar/decrementar la variable trabajo_pendiente se realizan copias temporales de su contenido en registros del sistema4. Estas copias locales, como las que se llevan a cabo en los registros R0 y R1, pueden ser inconsistentes con el valor de la variable de la memoria en un determinado instante de la ejecución del proceso. Esta inconsistencia es la que puede llevar a las dos situaciones erróneas mencionadas:

Notas y pies de figura de la fuente

;Incrementar R0 ;Decrementar R1 (4)Los registros del sistema son registros de hardware y no han de ser necesariamente diferentes para cada copia temporal.

Página 11

  1. No impresión de un trabajo

Consideremos, por ejemplo, que la variable trabajo_pendiente tiene un valor inicial igual a 3 cuando el gestor de la impresora ejecuta las instrucciones en lenguaje máquina que se han especificado anteriormente de la manera siguiente (figura 5):

a) En primer lugar, hace una copia del valor de la variable en el registro R1. En este momento, el valor de R1 coincide con el valor de la variable trabajo_pendiente, 3.

b) A continuación, decrementa el valor del registro, que pasa a valer 2. Ahora el valor de la copia es diferente del valor de la variable trabajo_pendiente en la memoria, 3, y, por lo tanto, hay una inconsistencia.

Figura 5. El proceso gestor empieza a decrementar la variable trabajo_pendiente.

Si en este punto de la ejecución del código máquina pasa alguna cosa en el sistema5 que provoca que el proceso gestor deje de ejecutarse, el sistema queda inconsistente.

En este estado de inconsistencia, lo peor que puede suceder es que un proceso usuario intente enviar un trabajo a imprimir. Entonces, cuando el proceso quiere incrementar el valor de la variable trabajo_pendiente, sucede lo siguiente (figura 6):

a) Se hace una copia de la variable en el registro R0, y R0 vale 3, porque la variable trabajo_pendiente continúa teniendo el valor de 3, ya que el gestor no lo había actualizado.

b) Se incrementa el valor del registro R0, que pasa a valer 4.

c) Se actualiza la variable trabajo_pendiente, que pasa a valer 4.

Notas y pies de figura de la fuente

(5)Un cambio de contexto porque expira el quantum del proceso gestor, una interrupción de un dispositivo, etc.

Página 12

Figura 6. El proceso usuario incrementa la variable trabajo_pendiente.

Al cabo de un cierto tiempo, cuando el proceso gestor se vuelve a ejecutar (figura 7), como se había interrumpido justo cuando debía ejecutar la instrucción STORE, lo primero que ejecuta es STORE trabajo_pendiente, R1. El valor de R1 en el proceso gestor era 2, por lo tanto el valor de la variable trabajo_pendiente en la memoria se actualiza y pasa a ser 2.

Figura 7. El proceso gestor finaliza el decremento de la variable trabajo_pendiente.

Después de esta secuencia de operaciones, el valor de trabajo_pendiente no es correcto. Habíamos partido de un valor de 3, el proceso gestor lo ha decrementado una vez y un proceso usuario lo ha incrementado otra vez. Por lo tanto, el valor final de la variable debería ser 3 y, en cambio, lo que hemos conseguido es asignarle un valor igual a 2.

A efectos prácticos, en el sistema hay un trabajo que no se imprimirá.

  1. Intento de impresión de un trabajo no existente

El caso simétrico al anterior es aquél en el que el usuario inicia la operación de actualización de la variable y no la finaliza porque es interrumpido (figura 8 y figura 9). Este proceso implica que el valor final de la variable considerada, trabajo_pendiente, sea erróneo e igual a 4.

Página 13

Figura 8. El proceso usuario es interrumpido mientras incrementa la variable trabajo_pendiente y el proceso gestor decrementa la misma variable.

Figura 9. El proceso usuario finaliza el incremento de la variable trabajo_pendiente.

En este caso, el gestor de la impresora intentaría enviar a imprimir un trabajo que no existe.

Todos los procesos se habrían ejecutado correctamente si las operaciones de incremento y decremento que el programador escribe en el lenguaje de alto nivel se hubieran ejecutado de manera indivisible o atómica. La indivisibilidad a la hora de ejecutar estas operaciones nos asegura a efectos prácticos que cuando un proceso incrementa/decrementa la variable no deja el procesador hasta que no ha finalizado la actualización o, dicho de otra manera, una vez iniciada una operación de actualización de una variable compartida, ningún proceso puede iniciar otra operación de actualización de aquella variable hasta que no ha finalizado la primera.

Con este ejemplo se demuestra que la ejecución concurrente no sincronizada de procesos que utilizan variables compartidas puede dar lugar a errores irrecuperables. Este tipo de situaciones recibe el nombre de race conditions (condición de carrera) porque pueden generar un resultado erróneo en función de cómo hayan sido planificados los procesos y hay que erradicarlas de los programas.

Ver fragmento extraído sin normalizar
2.1.1.  ¿Por qué es necesaria la sincronización?

Veamos por qué es necesaria la sincronización de los procesos con un caso                              (2)Suponemos que los procesos
concreto. Un ejemplo claro de variable compartida2 es el caso de una variable                          pueden compartir memoria.

contador, a la que denominaremos trabajo_pendiente, que se modificada
por diferentes procesos (usuario y gestor); tenemos que:

a) Los diferentes procesos usuario que se están ejecutando de modo concu-                              (3)Los trabajos que los procesos
rrente en el sistema la actualizan (la incrementan) cada vez que se envía un                           usuario envían a la impresora son
                                                                                                       ficheros.
trabajo3 a la impresora. Así, esta variable compartida indica cuántos trabajos
pendientes de impresión hay en el sistema.


b) El proceso gestor de la impresora envía los trabajos a imprimir y actualiza
el valor del contador decrementándolo cada vez que finaliza la impresión de
un trabajo.


Si la actualización de esta variable contador se efectúa sin ningún tipo de res-
tricción, se pueden generar inconsistencias, es decir, resultados erróneos. En
el caso de nuestro ejemplo, estos resultados erróneos podrían repercutir en el
proceso de impresión, de manera que se podría dejar de imprimir algún tra-
bajo o intentar imprimir algún trabajo que no existe.


Veamos cómo se pueden generar estas inconsistencias analizando nuestro
ejemplo. Como ya hemos indicado antes, un proceso￿usuario a la hora de
enviar un trabajo a imprimir incrementa la variable trabajo_pendiente (fi-
gura 1).

## Página 10

<!-- source-page: 10 -->

GNUFDL • PID_00214803                                                                                  10                                                                                         Comunicación y sincronización

El proceso￿gestor￿de￿la￿impresora cada vez que se acaba de imprimir un tra-
bajo decrementa la variable trabajo_pendiente (figura 2).


    ...
    trabajo_pendiente = trabajo_pendiente + 1

    ...


Figura 1. Fragmento de código del proceso de usuario


    ...

    trabajo_pendiente = trabajo_pendiente - 1
    ...


Figura 2. Fragmento de código del proceso gestor

Así pues, la variable compartida trabajo_pendiente indica cuántos trabajos
están pendientes de ser enviados a la impresora.


El principal problema que se plantea con el código que acabamos de presentar,
y en general con el código programado en cualquier lenguaje de alto nivel,
es que durante la compilación del programa una instrucción de alto nivel se
traduce en una o más instrucciones de lenguaje máquina.


Por ejemplo, en una máquina RISC (reduced instruction set computer) el código
generado una vez se ha compilado el código de nuestro ejemplo podría ser
el que indicamos a continuación: en el caso del código￿del￿proceso￿usuario
(figura 3) y en el caso del código￿del￿proceso￿gestor￿de￿la￿impresora (figura 4).


    LOAD R0,trabajo_pendiente  ;Copiar el contenido de trabajo_pendiente en el registro R0

    INC R0                     ;Incrementar R0
    STORE trabajo_pendiente,R0 ;Almacenar R0 en trabajo_pendiente


Figura 3. Código ensamblador correspondiente al fragmento de código del proceso usuario


    LOAD R1,trabajo_pendiente  ;Copiar el contenido de trabajo_pendiente en el registro R1
    DEC R1                     ;Decrementar R1
    STORE trabajo_pendiente,R1 ;Almacenar R1 en trabajo_pendiente


Figura 4. Código ensamblador correspondiente al fragmento de código del proceso gestor
Los detalles de la codificación no son importantes, lo que nos interesa adver-                           (4)Los registros del sistema son re-
tir aquí es que durante la ejecución de la instrucción de alto nivel para incre-                         gistros de hardware y no han de
                                                                                                         ser necesariamente diferentes para
mentar/decrementar la variable trabajo_pendiente se realizan copias tem-                                 cada copia temporal.
porales de su contenido en registros del sistema4. Estas copias locales, como las
que se llevan a cabo en los registros R0 y R1, pueden ser inconsistentes con el
valor de la variable de la memoria en un determinado instante de la ejecución
del proceso. Esta inconsistencia es la que puede llevar a las dos situaciones
erróneas mencionadas:

## Página 11

<!-- source-page: 11 -->

GNUFDL • PID_00214803                                                                                  11                                                                                         Comunicación y sincronización

1)￿No￿impresión￿de￿un￿trabajo


Consideremos, por ejemplo, que la variable trabajo_pendiente tiene un
valor inicial igual a 3 cuando el gestor de la impresora ejecuta las instruccio-
nes en lenguaje máquina que se han especificado anteriormente de la manera
siguiente (figura 5):


a) En primer lugar, hace una copia del valor de la variable en el registro
R1. En este momento, el valor de R1 coincide con el valor de la variable
trabajo_pendiente, 3.


b) A continuación, decrementa el valor del registro, que pasa a valer 2. Ahora
el valor de la copia es diferente del valor de la variable trabajo_pendiente
en la memoria, 3, y, por lo tanto, hay una inconsistencia.




















Figura 5. El proceso gestor empieza a decrementar la variable trabajo_pendiente.

Si en este punto de la ejecución del código máquina pasa alguna cosa en el                                   (5)Un cambio de contexto porque
sistema5 que provoca que el proceso gestor deje de ejecutarse, el sistema queda                              expira el quantum del proceso ges-
                                                                                                             tor, una interrupción de un dispo-
inconsistente.                                                                                               sitivo, etc.


En este estado de inconsistencia, lo peor que puede suceder es que un proce-
so usuario intente enviar un trabajo a imprimir. Entonces, cuando el proceso
quiere incrementar el valor de la variable trabajo_pendiente, sucede lo si-
guiente (figura 6):


a) Se hace una copia de la variable en el registro R0, y R0 vale 3, porque la va-
riable trabajo_pendiente continúa teniendo el valor de 3, ya que el gestor
no lo había actualizado.


b) Se incrementa el valor del registro R0, que pasa a valer 4.


c) Se actualiza la variable trabajo_pendiente, que pasa a valer 4.

## Página 12

<!-- source-page: 12 -->

GNUFDL • PID_00214803                                                                                  12                                                                                         Comunicación y sincronización



















Figura 6. El proceso usuario incrementa la variable trabajo_pendiente.

Al cabo de un cierto tiempo, cuando el proceso gestor se vuelve a ejecutar (fi-
gura 7), como se había interrumpido justo cuando debía ejecutar la instruc-
ción STORE, lo primero que ejecuta es STORE trabajo_pendiente, R1. El
valor de R1 en el proceso gestor era 2, por lo tanto el valor de la variable
trabajo_pendiente en la memoria se actualiza y pasa a ser 2.















Figura 7. El proceso gestor finaliza el decremento de la variable trabajo_pendiente.

Después de esta secuencia de operaciones, el valor de trabajo_pendiente
no es correcto. Habíamos partido de un valor de 3, el proceso gestor lo ha
decrementado una vez y un proceso usuario lo ha incrementado otra vez. Por
lo tanto, el valor final de la variable debería ser 3 y, en cambio, lo que hemos
conseguido es asignarle un valor igual a 2.


A efectos prácticos, en el sistema hay un trabajo que no se imprimirá.


2)￿Intento￿de￿impresión￿de￿un￿trabajo￿no￿existente


El caso simétrico al anterior es aquél en el que el usuario inicia la operación
de actualización de la variable y no la finaliza porque es interrumpido (figura
8 y figura 9). Este proceso implica que el valor final de la variable considerada,
trabajo_pendiente, sea erróneo e igual a 4.

## Página 13

<!-- source-page: 13 -->

GNUFDL • PID_00214803                                                                                  13                                                                                         Comunicación y sincronización




































Figura 8. El proceso usuario es interrumpido mientras incrementa la variable trabajo_pendiente y el proceso gestor decrementa
la misma variable.














Figura 9. El proceso usuario finaliza el incremento de la variable trabajo_pendiente.

En este caso, el gestor de la impresora intentaría enviar a imprimir un trabajo
que no existe.


Todos los procesos se habrían ejecutado correctamente si las operaciones de
incremento y decremento que el programador escribe en el lenguaje de alto
nivel se hubieran ejecutado de manera indivisible o atómica. La indivisibili-
dad a la hora de ejecutar estas operaciones nos asegura a efectos prácticos que
cuando un proceso incrementa/decrementa la variable no deja el procesador
hasta que no ha finalizado la actualización o, dicho de otra manera, una vez
iniciada una operación de actualización de una variable compartida, ningún
proceso puede iniciar otra operación de actualización de aquella variable hasta
que no ha finalizado la primera.


Con este ejemplo se demuestra que la ejecución concurrente no sincronizada
de procesos que utilizan variables compartidas puede dar lugar a errores irre-
cuperables. Este tipo de situaciones recibe el nombre de race conditions (condi-
ción de carrera) porque pueden generar un resultado erróneo en función de
cómo hayan sido planificados los procesos y hay que erradicarlas de los pro-
gramas.


Descargar Markdown originalEstudiar este apartado