Unidad 2

864 views
781 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
864
On SlideShare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
15
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Unidad 2

  1. 1. Unidad 2 - La Comunicación en los Sistemas Distribuidos 2.1 comunicacion sod 2.1 Llamada a procedimiento remoto (RPC) En el anterior epígrafe hemos estudiado un modelo de interacción entre los procesos de un sistema distribuido que es el modelo cliente-servidor. Para implementarlo, el sistema dispone de dos llamadas al sistema, send y receive, que las aplicaciones utilizan de forma conveniente. Estas primitivas, a pesar de constituir la base de la construcción de los sistemas distribuidos, pertenecen a un nivel demasiado bajo como para programar de forma eficiente aplicaciones distribuidas. Object 1 2 2.1.1 La operación RPC básica La llamada a procedimiento remoto constituye un mecanismo que integra el concepto cliente-servidor con la programación convencional basada en llamadas a procedimientos. La idea surgió en 1984 y consiste en que el cliente se comunica con el servidor mediante una llamada ordinaria a un procedimiento. Esta llamada se denomina a procedimiento remoto porque el procedimiento invocado se encuentra en otro espacio de direccionamiento, en la misma o en distinta máquina que el cliente. La información se transporta en los parámetros de la llamada y el resultado es devuelto en el retorno de la llamada. El cliente no tiene que utilizar las primitivas send y receive como en el modelo cliente-servidor puro, de modo que la programación de aplicacio-nes distribuidas se hace más sencilla. 2.1.2 El paso de parámetros La función del cabo cliente es empaquetar los parámetros en un mensaje y enviarlo al cabo servidor. Este examina el código del procedimiento en una sentencia tipo switch e invoca el procedimiento de forma local. El resultado es de nuevo empaquetado en un mensaje y enviado al cabo cliente. Esta interacción, que parece directa, no es tan simple como aparenta ser. Mientras las arquitecturas del cliente y del servidor sean iguales no se presenta ningún problema especial. En un entorno distribuido, sin embargo, cada arquitectura tiene su propia representación de números, caracteres, etc. Por ejemplo, los mainframe IBM utilizan el código EBCDIC para los caracteres en lugar del código ASCII, como el IBM PC. Problemas similares ocurren con la representación de coma flotante o los enteros negativos (complemento a 1 o complemento a 2). Otro problema surge con la forma de almacenar un entero. La arquitectura PC mantiene una representación “little endian”, a saber, el byte menos significativo en el byte más bajo de memoria. La arquitectura SPARC utiliza un almacenamiento “big endian”, es decir, el byte más significativo en el byte más bajo de la memoria. Un 486 operando en modo protegido representa un entero con cuatro bytes, de modo que el 5 lo representa como 0005. Un mecanismo adicional de paso de parámetros es denominado de copia-restauración. Consiste en realizar una copia del parámetro en la pila. Si esta copia parámetro es modificada en el procedimiento, se restaura su nuevo valor a la variable original. Algunos compiladores de Ada utilizan este mecanismo en parámetros in out, si bien ello no está especificado en la definición del lenguaje. La estrategia de copia con restauración puede ser utilizada para pasar punteros a procedimientos remotos. Supongamos la invocación del procedimiento remoto cuenta = read(df, buf, nbytes); La implementación más directa consiste en copiar al buffer del mensaje local un vector de “nbytes” caracteres en lugar de buf, que es la referencia al vector. El mensaje es entonces enviado al cabo servidor. Ahora el buffer del mensaje se encuentra en el espacio de direccionamiento de este último, invocará la rutina de servicio read con la referencia al
  2. 2. vector recibido en el mensaje, es decir de forma convencional por referencia. Ahora el mensaje es devuelto con su contenido actualizado a la rutina cabo del cliente, que restaura el vector del mensaje a su vector original. Para optimizar este mecanismo eludimos en el cabo cliente la copia del vector al mensaje en caso de lectura. En el caso de la escritura, prescindiremos de restaurarlo. Como vemos, es posible pasar punteros a procedimientos remotos siempre que sean referencias a estructuras sencillas como vectores. No obstante, no podemos enviar a procedimientos remotos referencias a estructuras de datos complejas como listas o árboles. 2.1.3 Especificación de interfase El servidor RPC puede ser considerado como un módulo u objeto que implementa unas operacio-nes sobre datos ocultos. El servidor da a conocer estas operaciones a los clientes mediante lo que se denomina su interface, constituida por las declaraciones de los métodos que soporta. Como sabemos, el objetivo del mecanismo RPC es lograr que un programa tenga acceso a los servicios de uno de estos módulos, aunque residan en otro espacio de direccionamiento, posiblemente remoto. La figura 2.8 muestra los componentes de una implementación RPC. La mitad rayada constituye el software adicional de empaquetamiento y desempaquetamiento de parámetros y primitivas de comunicación que debe ser ocultado al programador. Por brillante que sea la idea de la interacción cliente-servidor a través de RPC, si el programador debe encarar la construcción de los cabos, el progreso de los sistemas distribuidos se verá comprometido por la falta de uso en la práctica. Es preciso que la generación de cabos en el cliente y el servidor se realice de forma automática. Pues bien, la interfaz del servidor se utiliza como la base de esta generación automática de cabos. El primer paso es definir el interfaz, es decir, el conjunto de los prototipos de los procedimientos de servicio, mediante un lenguaje determinado, denominado lenguaje de interfase. La figura 2.8, a) muestra un ejemplo de servidor. La figura 2.8, b) su definición de interfaz. Un compilador especial toma como entrada el fichero escrito en el lenguaje de interfase y como salida genera el código objeto de los procedimientos que constituyen el cabo cliente, por una parte, y el cabo servidor, por la otra. El programa cliente se enlaza con estos procedimientos objeto para generar el ejecutable. En cuanto al servidor, los procedimientos de servicio, escritos por el programador, se compilan previamente a lenguaje objeto y, a continuación, se enlazan con los procedimientos cabo, entre los que figura el procedimiento principal del servidor RPC. 1. include <header.h> void main(void) { struct mensaje m1, m2; /* Mensajes entrante y saliente */ int r; while(1) { receive(FILE_SERVER, &m1); /* El servidor se bloquea esperando un mensaje */ switch(m1.opcode) { case CREATE: r = do_create(&m1, &m2); break; case READ: r = do_read(&m1, &m2); break; case WRITE: r = do_write(&m1, &m2); break; case DELETE: r = do_delete(&m1, &m2); break; case DELETE: r = E_BAD_OP; break; } m2.result = r; send(m1.source, &m2); }
  3. 3. } a) 1. include <header.h> specification of file_server, version 3.1 long read(in char name[MAX_PATH], out char buf[BUF_SIZE], in long bytes, in long position); long write(in char name[MAX_PATH], in char buf[BUF_SIZE], in long bytes, in long position); int create(in char[MAX_PATH], in int mode); int delete(in char[MAX_PATH]); end; b) 2.1.4 Enlace dinámico No es conveniente que un servicio esté ligado a una máquina. A veces las máquinas no están operativas, de modo que los servicios se mueven a otra máquina. El cliente está interesado en el servicio, no en qué máquina ejecuta el servidor. Para solicitar un servicio, un cliente podría enviar un mensaje en una sentencia como la siguiente send(33.33.33.33@20, &mens); en el código fuente que implementa la llamada RPC. Esta solución es inflexible, puesto que, si dentro de la máquina 33.33.33.33 el servidor pasa a escuchar en otro puerto distinto del 20, el cliente debe ser recompilado. Lo mismo ocurre si el servidor cambia de máquina. En contraste, el cliente debería invocar el servicio por su nombre, sin hacer referencia a dirección alguna. La figura 2.8 b) muestra la especificación formal del servidor de ficheros 2.8 a). En la especificación formal del servidor intervienen el nombre del servidor y su número de versión. Ambos datos identifican un servidor. Un servidor con el mismo nombre y la misma versión, no obstante, pueden estar replicados en dos o más máquinas a fin de mejorar el servicio o propor-cionar tolerancia a fallos. El cliente debe solicitar un servicio invocando su nombre y versión, sin citar dirección alguna. El número de versión es conveniente a fin de que un servidor que evoluciona conserve su nombre. Un servidor puede evolucionar ofertando nuevos servicios, cancelando otros y modificando otros. A pesar de esta evolución, es preciso que clientes antiguos, desconocedores de los cambios, sigan obteniendo el mismo tipo de servicio. Es preciso compren-der, no obstante, que un servidor con el mismo nombre pero con número de versión distinto es un servidor diferente. Las ventajas de identificar al servidor por su nombre son evidentes, pero, ¿cómo el cliente localiza al servidor? ¿Quién le proporciona su dirección? Este es un problema al que algunos sistemas RPC, entre ellos Sun RPC, dan respuesta mediante lo que se llama el enlace dinámico, que no es sino averiguar la dirección del servidor en tiempo de ejecución. El enlace dinámico está basado en un tercer proceso denominado el enlazador. Cuando el servidor arranca, en su código de inicialización previo al bucle principal, envía un mensaje al enlazador a fin de registrarse como un proceso que presta un servicio. El enlazador es, al fin y al cabo, un registrador de servicios, un servidor de nombres. El servidor entrega su nombre, su número de versión y su dirección. Se dice que el servidor exporta su interfaz. No sería extraño que dos programadores diesen el mismo nombre a dos servidores distintos, de modo que, a pesar de que no aparezca, cada servidor RPC tiene asociado, además del nombre y la versión, un identificador numérico, generalmente de 32 bits, que también entrega en la operación de registro. Dos servidores distintos deben tener identificadores distintos (se entiende aquí servidor como programa, no como proceso). Si bien sería raro, es posible que dos programadores que terminan dos
  4. 4. servidores RPC, coincidan en darles el mismo identificador. Lo que ocurra cuando ambos traten de registrarse depende de cada implementación particular del software RPC, pero es lógico pensar que el enlazador rechazaría la segunda petición de registro. 2.1.5 Semántica RPC en presencia de fallos El objetivo de la llamada a procedimiento remoto es conseguir la ejecución de procedimientos en otras máquinas sin cambiar la forma en que se invoca el procedimiento en el código fuente. Salvo algunas excepciones como que un procedimiento remoto no puede usar variables globales, el objetivo ha sido alcanzado plenamente. Mientras no se produzcan errores la transparencia de la llamada remota está conseguida, ya que el programador no necesita saber que la llamada que realiza es a un procedimiento remoto. Los problemas llegan cuando bien cliente o bien servidor dejan de operar correctamente, ya que las diferencias entre llamadas locales y remotas no son fáciles de enmascarar. Vamos a considerar cinco situaciones de fallo: 1. El cliente no es capaz de localizar al servidor. 2. El mensaje del cliente al servidor se pierde. 3. El mensaje de réplica de servidor a cliente se pierde. 4. El servidor se cae tras recibir el mensaje del cliente. 5. El cliente se cae tras recibir la réplica. El cliente no puede localizar al servidor Puede haber varias causas. Una es que el servidor se cae. Otra es que el servidor se actualiza y un cliente antiguo invoca una versión del servidor que ya no ejecuta. Una solución es que la llamada devuelva −1 como código de error. Sin embargo, −1 puede ser un resultado válido como en la llamada RPC de suma. Otro intento de solución es escribir un manejador de excepción (señales en C) para los errores producidos en las llamadas RPC. Sin embargo, esta solución acaba con la transparencia respecto a las llamadas locales. Lo mejor es recibir el resultado en parámetros de salida de la llamada RPC y que esta devuelva 0 en caso de ejecución con éxito y un código de error en otro caso. La petición del cliente se pierde Este caso tiene un tratamiento sencillo. El núcleo del cliente arranca un temporizador cuando envía el mensaje al servidor. Si al cabo de un plazo no ha llegado réplica o reconocimiento del servidor, el cliente da el mensaje por perdido y lo reenvía. Si todos los reenvíos se pierden, el kernel del cliente considera que el servidor no está activo y devuelve un error al cabo cliente, que lo pasa a la aplicación. Estamos en el caso anterior. La réplica del servidor se pierde Este problema es más complejo. Cuando en el caso anterior venció el plazo sin que llegase la respuesta del servidor, asumimos que se había perdido el mensaje enviado por el cliente. Pero existe la posibilidad de que el mensaje perdido fuese la réplica. De cualquier forma, establecimos que lo que había que hacer era el reenvío del mensaje. En el primer caso, el servidor recibe el mensaje reenviado por primera vez. Todo vuelve a funcionar correctamente. En el segundo caso el servidor recibe el mensaje por segunda vez. Sirve la petición dos veces, como si en el programa cliente hubiésemos invocado la llamada una segunda vez. En algunos casos, la repetición de una operación en el servidor no causa ningún daño. Por ejemplo, el leer los 20 primeros bytes de un fichero. Las peticiones que tienen esta propiedad se dicen que son idempotentes. Otras veces, la re-petición sí ocasiona perjuicios, como puede ser el añadir 20 bytes a ese fichero o retirar de una cuenta bancaria 500 millones de pesetas. La solución a este problema pasa por numerar las peticiones. Las retransmisiones llevan el mismo número de secuencia que la transmisión original. Entonces el kernel del servidor detecta que la réplica se ha perdido y la reenvía, sin operar sobre el servidor. Una versión distinta de esta solución es introducir en la cabecera de la petición un bit de retransmisión. El servidor se cae La figura 2.10 a) muestra la secuencia de eventos producidos en un servidor en el servicio de una petición. El sistema operativo del servidor recibe el paquete entrante con el mensaje, que entrega al cabo para que invoque la ejecución del procedimiento que sirve la petición. A continuación, el cabo pasa la réplica al sistema operativo que lo envía en un paquete al cliente. Ahora bien, tras haberse ejecutado el procedimiento de servicio, el servidor puede sufrir una caída que impide la emisión de la réplica al cliente. Es el caso b) de la figura. Por el contrario, la caída puede producirse tras la recepción del paquete, pero previamente a la ejecución del procedimiento de servicio. Es el caso c).
  5. 5. Las consecuencias de los casos b) y c) es diferente. Supongamos que la operación remota es escribir un texto en la impresora. El servidor primero carga el texto en la memoria interna de la impresora y después escribe en un registro de la misma para iniciar la impresión. El servidor puede caerse antes de establecer el bit o después de establecer el bit. En el primer caso, la operación no será realizada. En el segundo caso, sí, ya que la impresora continúa su trabajo con independencia de que el servidor esté activo o no lo esté. Podemos pensar que este caso tiene la misma complejidad que la pérdida de la réplica, examina-da en el caso anterior. No es así. La pérdida de la réplica supone un reenvío de la solicitud de impresión. El núcleo del servidor se dará cuenta de que el mensaje tiene un número de secuencia repetido y enviará de nuevo la réplica si repetir la impresión. En contraste, la caída del servidor supone la pérdida del registro de los números de secuencia de mensajes del cliente. Por esta razón, tras un rearranque, el servidor no puede saber si la re-petición de impresión ha sido servida con anterioridad, de modo que volverá a ser servida. Si la caída se produjo en el caso c) la impresión no se hizo, de modo que reiniciar la operación es lo correcto. Sin embargo, si la caída se produjo en el caso b) reiniciar la operación supone repetirla. Lo peor del caso b) es que ni cliente ni servidor sabrán nunca si la operación se realizó con anterioridad o no. 2.1.6 Implementación de software RPC El éxito o el fracaso de un sistema operativo distribuido radica en sus prestaciones. Si un sistema es lento, no tiene éxito. Las prestaciones de un sistema operativo distribuido radican en gran parte en la velocidad de las comunicaciones, y esta velocidad depende fundamentalmente de la implementación del protocolo de comunicación más que en sus principios abstractos. En esta sección vamos a discutir la implementación de software RPC. Protocolos RPC En los sistemas comerciales actuales, el software RPC está implementado como una biblioteca de funciones que el programa de usuario invoca. Tanto el cabo cliente como el cabo servidor no son sino un conjunto de llamadas a funciones de la biblioteca RPC. Veamos el ejemplo de Sun RPC. El siguiente mandato UNIX es el que se utiliza para compilar el programa cliente rdate de la figura 2.9. # cc -o rdate rdate.c date_clnt.c -lrpclib donde se aprecia el enlace de la biblioteca de Sun RPC, rpclib. El mandato para generar el servidor RPC es similar. La aplicación de usuario rdate invoca funciones RPC del cabo que, a su vez, invocan las llamadas al sistema de comunicaciones del sistema operativo para enviar los mensajes al servidor RPC. Así, el software RPC puede entenderse como un nivel de servicio entre cliente o servidor y el sistema operativo, tal como ilustra la figura 2.11. Generalmente, las llamadas al sistema de comunicaciones de los sistemas UNIX están implementadas mediante la pila de protocolos TCP/IP, que forman parte del núcleo de UNIX. IP es el protocolo de nivel de red (nivel tres en la pila OSI). TCP es uno de los protocolos de transporte de la pila TCP/IPTCP es un protocolo del nivel de transporte (nivel cuatro en la pila OSI), orientado a conexión. La comunicación entre dos procesos mediante conexión se basa en establecer un circuito virtual entre ambas máquinas antes del hacer envío alguno. El establecimiento de una conexión es costoso en tiempo y recursos, pero garantiza la fiabilidad total de la comunicación. 2.2 Comunicación de grupos La comunicación RPC tiene dos partes, una, el cliente, dos, el servidor. Hay situaciones en que este modelo no es el idóneo en un sistema con un conjunto de servidores de ficheros replicados a fin de proporcionar tolerancia a fallos. Un programa cliente que hace una escritura a disco debe hacer llegar la petición simultáneamente a todos los servidores y no a uno de ellos únicamente. El problema puede solucionarse invocando tantas llamadas RPC como servidores existentes. Existe, sin embargo, otro paradigma de comunicación disponible para los sistemas distribuidos que es la comunicación de grupos, más apropiado en casos como este. 2.2.1 Introducción a la comunicación de grupos
  6. 6. La característica principal de un grupo de procesos es que cuando se envía un mensaje al grupo, todos sus componentes lo reciben. Por otra parte, los grupos son dinámicos. Los grupos nacen y mueren y en cada grupo terminan algunos procesos y se incorporan otros. Por lo tanto es necesario un mecanismo de gestión de grupos. Un proceso puede pertenecer a más de un grupo y abandonar un grupo para unirse a otro. Un grupo es una abstracción cuyo propósito es evitar, en las llamadas de comunicación con grupos de procesos, parámetros molestos como el número de elementos en el grupo o la localización de cada proceso del grupo y proporcionar primitivas más potentes y transparentes, como el ejemplo anterior de envío de un mensaje a un conjunto de servidores de ficheros. El cliente no debería saber cuantos servidores están disponibles en cada momento ni en qué máquinas concretas han sido arrancados. La implementación de grupos depende mucho del hardware de comunicación disponible. En algunas redes, como ethernet, existen unas direcciones especiales en las que pueden escuchar más de una máquina. Para ello es necesario configurar el adaptador de red ethernet en cada máquina. Un único marco o enviado a esta dirección es leído por todas las máquinas que escuchan en ella. Estas direcciones se denominan multicasting. Si el nivel de enlace de la red soporta multicasting, como es el caso de ethernet, la implementación de grupos es sencilla. Basta asignar a cada grupo una dirección multicasting diferente. Existen redes que si bien no admiten multicasting, sí admiten difusión. Marcos ethernet con la dirección de destino 111…111 son recibidos por todas las máquinas de la red. La difusión puede ser utilizada para implementar grupos, pero es menos eficiente, porque el marco se envía a todas las máquinas, tanto las que pertenecen al grupo como las que no. De todas formas, el envío al grupo de procesos requiere de un solo paquete. Si el nivel de enlace no proporciona multicasting ni difusión, aún es posible la implementación de grupos. Para ello, el emisor debe enviar un paquete particular a cada miembro del grupo. Por supuesto, esta es la implementación menos eficiente. 2.2.2 Aspectos de diseño La comunicación de grupos comparte muchos aspectos de la comunicación estándar de paso de mensajes, tales como primitivas con buffer o sin buffer, bloqueantes o no bloqueantes, etc. No obstante, existen más posibilidades adicionales a elegir en la comunicación de grupos. En esta sección examinamos algunos de estos aspectos particulares en la comunicación de grupos. 2.2.2.1 Grupos cerrados frente a grupos abiertos Los protocolos que soportan comunicación de grupos pueden dividirse en dos categorías. Una es la de los grupos cerrados. En ellos sólo los miembros del grupo pueden enviarse mensajes entre sí. Procesos que no pertenecen al grupo no pueden enviar mensajes al grupo como tal, si bien, por supuesto, sí pueden enviar mensajes particulares a cada miembro del grupo a título particular. Otros protocolos soportan grupos abiertos. Un proceso que no pertenece a un grupo puede enviar mensajes a este grupo. Los grupos cerrados son utilizados típicamente en procesa-miento en paralelo, donde los procesos tienen su propio cometido y no interactúan con el mundo exterior. Los grupos abiertos son apropiados para problemas como el de los servidores replica-dos, donde un cliente envía una petición al grupo. Los miembros del grupo también necesitan hacer uso de la comunicación en grupo y no sólo un proceso externo, por ejemplo para decidir quién debe ejecutar una petición dada. 2.2.2.2 Grupos de iguales frente a grupos jerárquicos En algunos grupos un proceso es el jefe o coordinador y el resto trabajan para él. En otros, todos los procesos tienen la misma categoría y las decisiones se toman de forma colectiva. En el primer caso, cuando uno de los trabajadores o un cliente externo solicita una petición, esta es enviada al coordinador, que decide cuál de los trabajadores es el más apropiado para servirla. Cada una de estas organizaciones tiene sus ventajas y sus inconvenientes. Los grupos pares no tienen un único punto de fallo. Si un trabajador deja de existir, la carga se reparte en el resto. Sin embargo, incurren en el retraso que supone el ponerse de acuerdo para decidir quién sirve una petición. Los grupos jerárquicos tienen las propiedades opuestas. 2.2.2.3 Pertenencia a grupos
  7. 7. Se necesita un método para crear, destruir y modificar los grupos. Una solución es introducir un proceso denominado el servidor de grupos. El servidor de grupos mantiene tablas con los grupos existentes y sus integrantes. Esta solución es eficiente y fácil de implementar. El problema es que el servidor de grupos es una técnica centralizadora y, como tal, constituye un único punto de fallo. La opción opuesta es la gestión distribuida. Un nuevo proceso que se incorpora al grupo puede enviar un mensaje al grupo y todos los procesos toman nota de su existencia. Cuando un proceso del grupo termina, envía un mensaje de adiós. Surge un problema cuando un proceso del grupo termina inesperadamente. El resto del grupo tiene que descubrirlo experimentalmente, ya que el proceso nunca responde a nada. Cuando todos los miembros lo han constatado, se le da de baja. 2.2.2.4 Direccionamiento de grupos Para dirigir un mensaje a un grupo, el grupo debe tener una dirección. Si el nivel de enlace de la red -en adelante, la red- soporta multicasting, se puede implementar una dirección de grupo como una dirección multicasting. Si la red soporta difusión, pero no multicasting, el mensaje debe ser recibido por todos los núcleos y extraer de él la dirección del grupo. Si ninguno de los procesos de la máquina es miembro del grupo, el mensaje es descartado. En otro caso, es pasado a todos los procesos que pertenecen al grupo. Si la red no soporta ni difusión ni multicasting, el núcleo de la máquina emisora tendrá que tener una lista de los procesos que pertenecen al grupo y enviar un mensaje a cada uno de los componentes del grupo. 2.2.2.5 Primitivas send y receive No es conveniente que los procesos de usuario puedan utilizar las primitivas send y recei-ve por separado. Según Tanenbaum, send y receive se corresponden en programación distribuida con la construcción “go to” en programación convencional. Su utilización provoca más trastornos que utilidad. Si RPC es la primitiva de comunicación usada por los procesos de usuario, la comunicación de grupo debiera poder construirse mediante RPC. El problema es tratar con una llamada a procedimiento que devuelve tantos valores como procesos tiene un grupo. No parece posible, de modo que una aproximación común es volver a las primitivas send y receive en los procesos de usuario. Si se permite send y receive a los procesos de usuario, se pueden aprovechar estas primitivas para realizar la comunicación de grupo. En el caso de la comunicación convencional cliente servidor, el primer parámetro de send es un número de puerto en la red y el segundo parámetro es el mensaje. Se puede utilizar send para la comunicación de grupos empleando como primer parámetro la dirección de un grupo de procesos en lugar de un puerto determinado. Así unifica-mos la comunicación punto a punto y la comunicación de grupos en una primitiva única. La implementación de send que haga el núcleo o la rutina de biblioteca puede ser una de las tres citadas en el apartado de direccionamiento de grupos. send puede ser con buffer o sin buffer, bloqueante o no bloqueante, fiable o no fiable, etc, igual que en la comunicación punto a punto. La comunicación de grupo no cambia las cosas. Enviar a un grupo obliga al núcleo del sistema que envía a hacer una comunicación multicasting a todos los miembros del grupo, pero ¿qué es recibir de un grupo? Puede entenderse que todos los miembros del grupo van a enviar una réplica diferente y por lo tanto un envío a un grupo debería continuar en el programa de usuario con tantas operaciones receive como miembros tuviese el grupo. Ello, no obstante, significa una pérdida de transparencia, ya que un proceso externo no debe conocer la estructura interna de un grupo. 2.2.2.6 Atomicidad de la difusión Una de las características de la comunicación de grupos es que un mensaje enviado a un grupo debe ser recibido por todos los miembros del grupo o por ninguno de ellos. A esta propiedad se la denomina la atomicidad de la difusión o simplemente la atomicidad. La atomicidad facilita en gran medida la programación de sistemas distribuidos. Supongamos, por ejemplo, una base de datos distribuida. Una base de datos distribuida puede tener una relación cuyos atributos estén repartidos en máquinas dispersas geográficamente. El acceso a una tupla significa enviar un mensaje a
  8. 8. todas las máquinas. Supongamos la creación de una tupla de estas características. Si uno de los mensajes se pierde, la base de datos queda en un estado inconsistente. La tupla debe crearse en su totalidad o no crearse y la aplicación ser informada del éxito o del fracaso de la operación entera, no sólo un éxito parcial. Implementar la atomicidad de la difusión no es tan simple como parece. Por ejemplo, uno de los adaptadores de red tal vez acaba de recibir un paquete y antes de que esté preparado para recibir uno nuevo, llega el paquete de difusión, que no puede ser aceptado y simplemente se pierde. Todas las máquinas del grupo han recibido el paquete excepto una, lo que arruina la difusión completa. Para cerciorarse de que todo ha ido bien, el emisor de la difusión debe esperar las confirmaciones de los miembros del grupo. Si una falta, deberá repetir la difusión o reenviar el mensaje en particular. Puede ocurrir que antes de repetir la difusión o hacer el reenvío, el emisor de la difusión se caiga. En esta situación, el cliente debe rearrancar, pero nunca sabrá cuántos son los mensajes que realmente han sido enviados y cuántos los que han fallado. Algunos miembros del grupo han recibido el mensaje y otros no. Una situación inaceptable e ¿incorregible? No, queda alguna esperanza. Existe un algoritmo que demuestra que la atomicidad es posible. El emisor realiza la difusión, inicializa temporizadores y realiza los reenvíos necesarios. Cuando un miembro del grupo recibe un mensaje nuevo, envía el mensaje a todos los miembros del grupo -de nuevo utilizando temporizadores y retransmisiones si son necesarias-. Si el mensaje no es nuevo, simplemente es descartado. No importa cuantas máquinas caigan en el proceso de envíos y reenvíos, lo importante es que de los procesos supervivientes en el grupo todos han recibido el mensaje. 2.2.2.7 Ordenación de mensajes Para que la comunicación en grupo sea fácil de usar y comprender requiere de dos propieda-des. La primera es la atomicidad, examinada más arriba y la segunda es el orden de los mensajes. Supongamos cinco máquinas, cada una con un proceso. Los procesos 0, 1, 3 y 4 pertenecen a un grupo de procesos. Supongamos que los procesos 0 y 4 deciden enviar un mensaje al resto de los procesos del grupo. Supongamos que el proceso 0 lo ha decidido un instante de tiempo antes. La propiedad de orden temporal global exige que cada miembro del grupo reciba primero el mensaje del proceso 0 y después el mensaje del proceso 4. Una red local que soporte difusión garantiza el orden temporal global. Todos los mensajes del proceso 0 llegan al resto del grupo ya que el paquete con dirección difusión es recibido por todos los adaptadores de red de forma simultánea. Sólo después, cuando el mensaje ha sido enviado y deja libre el canal el proceso 4 puede emitir su paquete de difusión. Una red sin difusión no garantiza el orden temporal global, que debe implementar la difusión enviando cuatro mensajes consecutivos que pueden entrelazarse con los del proceso 4. Así, el proceso 1 puede recibir primero el mensaje del proceso 0 y después el del 4 y el proceso 3 puede recibir primero el mensaje del proceso 4 y después el del proceso 0. 2.2.2.8 Grupos solapados En el apartado anterior vimos el problema de la ordenación temporal global aplicada a un grupo dado. Dos procesos del mismo grupo enviaban simultáneamente un mensaje al grupo. Hemos mencionado previamente que un proceso puede pertenecer a más de un grupo, de manera que los grupos pueden solaparse. Este solapamiento produce un nuevo tipo de inconsistencia temporal. La figura 2.14 muestra el problema planteado. Los procesos A y D, en distinto grupo, envían simultáneamente un mensaje a su propio grupo. Como en el caso anterior supongamos que la red no proporciona difusión y esta se implementa mediante unicasting. El envío simultáneo es consitente si B y C reciben primero el mensaje del grupo 1 y después el mensaje del grupo 2 (o viceversa). Unicasting no puede garantizar esto, de modo que es posible el escenario de la figura 2.14. B recibe el mensaje dirigido al grupo 1 y después el mensaje al grupo 2. C, por el contrario, primero recibe el mensaje dirigido al grupo 2 y después el mensaje al grupo 1. Fig. 2.14 Cuatro procesos y cuatro mensajes. B y C toman los mensajes de A y D en diferente orden. Una comunicación de grupo que garantiza que esto no ocurre se dice que tiene la propiedad de orden temporal entre grupos solapados. La implementación del orden temporal entre grupos solapados es costosa y difícil de implementar, de modo que algunos sistemas distribuidos la implementan y otros no, actualmente la mayoría de ellos.
  9. 9. 2.2.2.9 Escalabilidad Muchos algoritmos de comunicación de grupos funcionan bien siempre que los grupos sean pequeños y haya pocos grupos, pero ¿qué ocurre cuando cada grupo tiene muchos procesos y hay miles de grupos? Y ¿qué ocurre cuando los grupos se extienden geográficamente entre continen-tes? Para ellos es preciso sobrepasar los estrechos límites de la red local e introducir encaminado-res y muchas redes locales. La presencia de encaminadores afecta a las propiedades de la comunicación del sistema operativo distribuido, ya que están basadas en la suposición de ausencia de encaminadores. Un encaminador es básicamente una máquina que pertenece a dos o más redes. Su propósito es unir dos o más redes locales distantes geográficamente, de distinto nivel de enlace o ambas cosas a la vez, permitiendo que todas ellas compartan un mismo protocolo de nivel de red. En la definición de grupo nada impide que los procesos que forman el grupo se encuentren en una misma Inter-red pero en distintas redes locales. Cuando la comunicación de grupo se realiza mediante unicasting la presencia de gateways no es problemática. Cada mensaje enviado por el proceso emisor llega al proceso destinatario siguiendo los cauces convencionales del algoritmo utilizado por el protocolo de red. Consideremos un sistema operativo distribuido en el que las redes locales que abarca soportan multicasting, como ethernet. 3.1 Sincronización de relojes Los algoritmos distribuidos tienen las siguientes propiedades: 1. La información relevante está repartida entre múltiples máquinas 2. Los procesos toman decisiones basados únicamente en información local 3. Es preciso evitar un único punto de fallo 4. No existe un reloj común Los primeros tres aspectos dicen que es inaceptable recoger toda la información en un único punto. El último aspecto es que ahora nos interesa. En un sistema centralizado, el tiempo se solicita mediante una llamada al sistema, como la llamada UNIX time. Si un proceso A solicita el tiempo y poco después lo solicita el proceso B, B obtiene un tiempo posterior al de A, ya que ambos consultan el mismo reloj. En un sistema distribuido, en que A y B corren en máquinas distintas y consultan distintos relojes, si el reloj de A es ligeramente más lento que el de B, A puede conseguir un tiempo posterior al de B a pesar de habero solicitado antes. 3.1.1 Relojes lógicos Leslie Lamport, en 1978 ([Les78]), mostró que la sincronización de relojes para producir un patrón de tiempo común a más de una máquina es posible y presentó un algoritmo para lograrlo. Lamport afirmó que no es necesario disponer de un registro común absoluto del tiempo cuando los procesos no interactúan y, cuando lo hacen, tampoco es necesario en la mayoría de las aplicaciones. Para muchos casos, lo imporante es que los procesos que interactúan se pongan de acuerdo en el tiempo en que los eventos ocurren. En el ejemplo de make, lo importante es que pepo.c sea más antiguo que pepo.o, no el instante preciso de creación de ambos. Así, para ciertas clases de algoritmos, lo que importa es la consistencia interna de los relojes, no la exactitud particular de cada uno de ellos. Para estos algoritmos, es conveniente hablar de relojes lógicos. Cuando el objetivo es mantener todos los relojes dentro de un margen error dado respecto al tiempo absoluto, es conveniente hablar de relojes físicos. Lamport definió la relación ocurre-antes, a m b, leída “a ocurre antes que b”, y significa que a ocurre antes que b y todos los procesos están de acuerdo en ello. Lamport definió esta relación como sigue: 1. Si los eventos a y b ocurren en el mismo proceso y a ocurre antes que b, entonces a b. 2. Si a es el evento que consiste en el envío de un mensaje a otro proceso y b es el evento que consiste en la recepción del mensaje en otro proceso, entonces a b. 3. Si a e b y b c, entonces b e c.
  10. 10. 3.1.2 Relojes físicos El día solar es el tiempo que transcurre desde que el sol alcanza su punto más alto en el horizonte hasta que vuelve a alcanzarlo. Un día tiene 24 horas, de modo que definimos el segundo solar como 1/(24*60*60) = 1/86400 de un día solar. En 1940 se descubrió que la duración del día no era constante. Estudios actuales sobre coral antiguo han llevado a los geólogos a pensar que hace 300 millones de años había 400 días en un año. No es que el año fuera más largo. Es que los días eran más cortos. La tierra rotaba sobre sí misma más rápido que en la actualidad. Además de esta tendencia a largo plazo, la tierra experimenta perturbaciones esporádicas en su tiempo de rotación debido a las turbulencias de su núcleo de hierro. Estas oscilaciones llevaron a los astrónomos a determinar la duración del segundo como la media de un gran número de ellas. Dividida esta cantidad por 86400 obtenemos el segundo solar medio. Con la invención del reloj atómico en 1948, la medida del tiempo pasó de ser responsabilidad de los astrónomos a ser responsabilidad de los físicos. Definieron el segundo atómico como el tiempo que tarda el isótopo 133 del cesio en realizar 9192631770 transiciones. Este número de transiciones fue escogido, por supuesto, porque son las que igualaban la duración del segundo solar medio el día que el segundo atómico fue introducido. El segundo atómico es más preciso que el segundo solar, pero no es absolutamente preciso. En el mundo existen actualmente unos 50 laboratorios que disponen de un reloj de 133Cs. Cada uno de ellos registra el número de ticks acumulados desde las cero horas del primero de enero de 1958. En París existe una organización que se llama la Oficina Internacional de la Hora que promedia los ticks de estos 50 laboratorios. Al resultado lo divide por 9192631770 para obtener el Tiempo Atómico Internacional (TAI). El TAI es extrordinariamente estable y está a disposición de cualquiera que lo solicite. Sin embargo, como el periodo de rotación de la tierra está aumentando continuamente, el segundo solar aumenta en la misma medida. Así, un día solar, que son 86400 segundos solares, tiene ahora 86400.003 segundos TAI. Usar el tiempo TAI es más exacto, pero llegará un momento que el mediodía no será a las 12, sino a las doce y cuarto. Los segundos TAI duran menos que los segundos solares. Para ello, cuando un reloj solar ha perdido 0.8 segundos respecto al tiempo TAI, por ejemplo, el tiempo es de 4.0 TAI y de 3.2 solar, se extingue ese segundo solar para que pase directamente de 3.2 a 4.0 y mantener la sincronía con el tiempo TAI. Esto da una medida del tiempo con intervalos irregulares, llamado el Tiempo Universal Coordinado (UTC), que es la base actual del registro del tiempo. Ha reemplazado al antiguo estándar de la medida del tiempo, el GMT (Greenwich Meridian Time), que es tiempo solar. Para proporcionar el tiempo UTC, una institución de Estados Unidos, el Instituto Nacional del Tiempo Estándar (NIST), mantiene una estación de radio de onda corta que radia un pulso cada vez que comienza un segundo UTC. La precisión de esta estación es de un milisegundo, pero el ruido atmosférico eleva este error en la práctica a 10 milisegundos. En Inglaterra y otros países existen estaciones similares. También satélites proporcionan el tiempo UTC, y lo hacen con una precisión de 0.5 milisegundos, veinte veces mayor que las estaciones de radio de onda corta. El costo de este servicio varía, según su exactitud, entre 100.000 pts y varios millones de pesetas según su precisión. Hay un medio más barato, que es obtenerlo del NIST por teléfono. Este es el método más inexacto, ya que hay que corregir el retraso de la señal en la línea y el modem. Concluyendo, podemos decir que el tiempo absoluto puede ser proporcionado al computador, pero a un precio alto y siempre con un margen de error no despreciable. Mas información: http://www.cstv.to.cnr.it/toi/uk/utctime.html
  11. 11. 3.1.3 Algoritmos de sincronización de relojes La medida del tiempo en las máquinas se lleva a cabo mediante un oscilador de cristal. Un chip denominado temporizador recibe la señal periódica del oscilador e interrumpe la UCP cada cierto número de oscilaciones, previamente programado. Valores típicos oscilan entre 50 y 100 interrupciones por segundo a la UCP. Por preciso que sea un oscilador a cristal, siempre existe un margen de error que conlleva la discrepancia de la medida del tiempo de dos máquinas diferentes. En una red local, por ejemplo, ninguna máquina tiene el mismo registro del tiempo. Para disminuir la discrepancia entre relojes, se puede tener acceso a una estación de onda corta de las ya citadas. El caso general, sin embargo, es que este servicio no está disponible, y el problema que se plantea es, dado un conjunto de máquinas, mantener sus relojes lo más cercanos que sea posible mediante software. Se han propuesto para ello muchos algoritmos, todos ellos con el mismo principio, que ahora describimos. Se supone que cada máquina dispone de un temporizador que interrumpe a la UCP H veces por segundo. El núcleo dispone de una variable que es incrementada en la unidad por la rutina de interrupción del reloj. Esta variable registra el número de ticks recibidos desde la puesta en marcha del sistema, por ejemplo. Esta variable se considera el reloj del sistema y vamos a denominar el valor que almacena como C. Cuando el tiempo es t, el tiempo registrado por la máquina p es Cp(t). Idealmente Cp(t) debiera ser igual a t, para todo p y todo t. En otras palabras, dC/dt debiera ser idealmente 1. Teóricamente, un temporizador con H=60 interrumpe al reloj sesenta veces por segundo. En una hora interrumpe 60*60*60 = 216000 veces. En la práctica, se puede contar este número de interrupciones y descubrir que no son exactamente esas, sino que el dato varía entre 215998 y 216002 ticks en una hora, lo que representa un error relativo de aproximadamente 10–5. La precisión de un temporizador viene dada por su tasa de deriva máxima 0, de modo que si se dice que el reloj opera dentro de sus especificaciones. Dos relojes iguales dentro de sus especificaciones pueden generar una direferencia máxima en su medida del tiempo cuando la deriva toma en ellos el valor máximo y de signo opuesto. Así, partiendo ambos de cero, en un intervalo , el reloj uno toma un valor de y el reloj dos un valor de 0, obteniendo una diferencia máxima en la medida de 0. Si los diseñadores del sistema desean que nunca dos relojes muestren diferencias mayores a una constante 0, 0, de modo que 0, lo que significa que los relojes deben ser sincronizados cada 0 segundos. A continuación vamos a ver algunos algoritmos que llevan a cabo esta resincronización. 3.1.3.1 El algoritmo de Cristian Este algoritmo requiere que una de las máquinas disponga de un receptor de onda corta y el objetivo es lograr que todas las demás operen sincronizadas con ella. A esta máquina la vamos a llamar un servidor de tiempo. Periódicamente, y siempre antes de segundos, cada una de las máquinas envía un mensaje al servidor de tiempo solicitando el tiempo CUTC, que es servido tan rápido como es posible como indica la figura 3.5 XX falta XX. El algoritmo tiene dos problemas, uno más leve y otro más serio. El más serio es que un reloj nunca puede ser retrasado. Si el reloj de la máquina que solicita el tiempo es rápido, el tiempo CUTC recogido es menor y su reloj debe ser atrasado. Esto no se puede permitir porque muchas aplicaciones, como make, confían en la secuencia temporal de eventos en el sistema como la base de su operación. A un evento que ocurre después de otro, como la generación de un fichero objeto, no se le puede asignar un tiempo de creación o última modificación inferior al del programa fuente.
  12. 12. La modificación del reloj debe realizarse gradualmente. Una forma de hacerlo es la siguiente. Supongamos que el temporizador interrumpe la UCP cien veces por segundo, lo que significa que un tick de reloj es un intervalo de tiempo de diez milisegundos. La rutina de interrupción incrementa un contador en el núcleo, el reloj, en una unidad, lo que equivale a sumar al tiempo diez milisegundos. Para retrasar el reloj un segundo se puede dejar de incrementar el contador una de cada cien interrupciones -por ejemplo, la décima-, lo que significa que cada segundo retrasamos el reloj diez milisegundos. Para retrasarlo un segundo necesitamos cien segundos. Para adelantar el reloj se puede utilizar esta misma técnica. Al cabo de 100 segundos, habremos adelantado el reloj un segundo. También se puede adelantar el reloj de una sóla vez añadiendo 100 ticks al reloj, ya que el adelantamiento del tiempo no causa problemas. El problema secundario es que desde que una máquina solicita el tiempo CUTC, la réplica del servidor de tiempo tarda en llegar una cantidad de tiempo no despreciable y, lo que es peor, que varía con la congestión de la red. El algoritmo de Cristian aborda este problema intentando medirlo. El cliente registra el tiempo local T0 en que envía el mensaje y el tiempo T1 en el que llega y estima que la réplica tardó en llegar (T1-T0)/2. Este tiempo que es local y, por ser pequeño, relativo exacto aunque el reloj se haya alejado sensiblemente del tiempo UTC. (T1-T0)/2 se suma al CUTC que trae el mensaje y el resulado es el CUTC que finalmente el cliente adopta. Para mejorar la exactitud se puede realizar un muestreo entre distintos tiempos de retorno de la petición de tiempo y realizar una media. Se aconseja descartar los valores que superan un umbral dado para evitar introducir en la estimación réplicas obtenidas en momentos de congestión. 3.1.3.2 El algoritmo de Berkeley Es el adoptado por UNIX BSD. Frente al algoritmo de Cristian, basado en un servidor pasivo que responde a las peticiones de clientes, el algoritmo de Berkeley toma una aproximación activa. Es útil cuando no se dispone del tiempo UTC, vía un receptor de onda u otro. Un demonio UNIX periódicamente realiza un escrutinio de las máquinas, aquella en la que reside incluida, a fin de obtener el valor de sus relojes. Realiza una media de todos ellos y la comunica a todas la máquinas para que avancen o retrasen sus relojes. 3.1.3.3 Algoritmos de promediado Los algoritmos anteriores tienen la desventaja de su aproximación centralizada y, por lo tanto, tienen un único punto de fallo. Presentamos a continuación un algoritmo descentralizado. Las máquinas dividen el tiempo en intervalos de longitud R, de modo que el comienzo del i-ésimo intervalo comienza en el instante T0+iR se prolonga hasta el instante T0+(i+1)R, donde T0 es un instante pasado previamente acordado. Cuando comienza uno de estos intervalos, cada máquina realiza una difusión del tiempo según su reloj. Debido a la deriba particular de cada reloj, dos difusiones no ocurren simultáneamente. Después de la difusión de su tiempo, cada máquina establece un temporizador y espera el mensaje correspondiente al broadcast del resto de las máquinas en un intervalo S. Cuando han llegado todos los mesajes, un algoritmo de promediado proporciona el nuevo tiempo. El algoritmo más simple es realizar la media aritmética de los tiempos. Una variación es descartar previamente los valores extremos a fin de protegernos frente a relojes defectuosos. Otra variación es estimar el tiempo de propagación de cada mensaje para añadirlo al tiempo que el mensaje transporta. Esta estimación puede llevarse a cabo a partir de un conocimiento previo de la topología de la red o realizando mediciones del tiempo de retorno de algunos mensajes de prueba. 3.1.4 El empleo de la sincronización de relojes
  13. 13. Hasta hace poco tiempo no se ha presentado la necesidad de sincronizar los relojes de máquinas en una red de área ancha. Ahora es posible sincronizar relojes distribuidos a lo largo de toda la Internet en márgenes de precisión de unos pocos milisegundos respecto al tiempo UTC. La disponibilidad de algoritmos de bajo costo para mantener la sincronización de relojes ha incitado el desarrollo de algoritmos distribuidos que explotan esta circunstancia disminuyendo el número de mensajes implicados en las versiones que no la explotan. A continuación ilustramos el empleo de la sincronización de relojes en el problema de la consistencia de la caché de un sistema de ficheros distribuidos. La referencia [Lis93] contiene más ejemplos. Un ejemplo de la utilidad de algoritmos basados en el uso de relojes sincronizados está relacionado con la consistencia de la cache de disco en un sistema de ficheros distribuido. Razones de prestaciones exigen una caché en el cliente. Dos clientes operando sobre un mismo fichero mantienen cada uno de ellos una copia del fichero en su propia máquina. La inconsistencia de las cachés surge cuando uno de los clientes trata de escribir en el fichero. Tradicionalmente, cuando un cliente deseaba escribir un fichero de su caché solicitaba permiso al servidor. Inmediatamente, el servidor está obligado a solicitar permiso al proceso o procesos que están leyendo del fichero para que dejen de hacerlo (y lo descarten de la caché), y esperar a que todos los permisos lleguen antes de conceder el permiso de escritura, lo que introduce una fuerte sobrecarga en tiempo y ancho de banda en la red. La introducción de los relojes sincronizados agiliza este tipo de protocolos de los sistemas de ficheros distribuidos. La idea básica es que cuando un cliente solicita un fichero, el servidor le otorga una concesión en la que detalla el tiempo de expiración de la misma E. Como cliente y servidor tienen los tiempos sincronizados, el plazo es el mismo en ambos. Mientras dura la concesión, el cliente tiene la garantía de que opera sobre el fichero de forma consistente. Un cliente no necesita comunicar al servidor que ha terminado la operación de lectura. Si un cliente solicita la escritura de un fichero, el servidor debe pedir a los clientes lectores la terminación prematura de la concesión. ¿Qué ocurre cuando no hay respuesta por parte del cliente lector? El servidor no sabe si el cliente simplemente se ha caído. En este caso, el servidor no obtiene respuesta, lo que plantearía un problema en el algoritmo tradicional. Con los relojes sincronizados, simplemente espera a que cumpla el plazo de la concesión del fichero. 3.2 Exclusión mutua Cuando dos o más procesos comparten una estructura de datos, su lectura o actualización no debe ser simultánea. Para evitar la simultáneidad de acceso, y con ello la incosistencia de la estructura, el código de acceso y actualización de la misma se denomina región crítica y su ejecución es protegida mediante construcciones como semáforos, monitores, etc. En esta sección examinamos algunos ejemplos de cómo construir regiones críticas en sistemas distribuidos. 3.2.1 Un algoritmo centralizado La forma más directa de conseguir la exclusión mutua en un sistema distribuido es simular al mecanismo de los sistemas centralizados. Se requiere de un proceso que actúa como coordinador. Este registra las regiones críticas de los procesos. Cuando un proceso desea entrar en una región crítica envía un mensaje al coordinador con el número de la región crítica. Si ningún otro proceso está ejecutando la región crítica, el coordinador envía una réplica al proceso con la concesión de entrada, tal y como muestra la figura 3.5. Cuando la réplica llega, el proceso entra en la región crítica.
  14. 14. Fig. 3.5 a) Solicitud y concesión de entrada en región crítica. b) La concesión se retrasa hasta mientras la región crítica esté en uso. c) Concesión tras ser liberada Supongamos que el proceso 3 de la figura desea entrar en la misma región crítica antes de que el proceso 2 salga. La petición llega al coordinador, que sabiendo que el proceso 2 está dentro, no envía réplica alguna al proceso 3, que permanece bloqueado esperándola -también se puede implementar la denegación mediante un mensaje-, pero lo registra en la cola de la región como solicitante. Cuando el proceso 2 sale de la región crítica lo comunica al coordinador mediante un mensaje de liberación. El coordinador procesa el mensaje determinando si existe en la cola de la región recién liberada algún proceso esperando. En nuestro caso, efectivamente lo hay. Es el proceso 3, que por fin recibe el mensaje de concesión de entrada. Este algoritmo garantiza la exclusión mutua. Primero, el coordinador sólo permite entrar en la misma a un proceso. Segundo, es justo, ya que las peticiones son atendidas en el orden en que llegan. Tercero, ningún proceso espera indefinidamente por la entrada. El esquema es fácil de implementar y es eficiente, ya que requiere tres mensajes para usar una región crítica. El defecto principal del algoritmo, como todos los algoritmos centralizados es la existencia de un único punto de fallo. 3.2.2 El algoritmo distribuido de Ricart y Agrawala Ricart y Agrawala presentaron en 1981 un algoritmo de exclusión mutua distribuido. Consideramos un conjunto de N procesos con una esctructura sencilla en la que alternan los cálculos fuera de la región crítica y dentro de la región crítica. Las condiciones del algoritmo son las siguientes: 1. Los procesos se comunican mediante mensajes de capacidad no nula. 2. Los mensajes pueden llegar a un proceso en cualquier punto de la ejecución, bien dentro o bien fuera de la región crítica. De cualquier modo una interrupción, excepción, manejador de señal, etc, se encarga de procesar la llegada del mensaje. 3. Se asume que la comunicación es fiable y los mensajes entre dos procesos son entregados en el orden en el que fueron enviados. 4. Es preciso una relación de orden total entre los eventos de todo el sistema. Consideramos que para que un proceso entre en la región crítica deben tener el permiso todos y cada uno del resto de los procesos, permiso que solicita enviando un mensaje a todos ellos, vía multicasting, difusión o uno a uno. El mensaje acarrea una etiqueta temporal que es el valor del reloj lógico local correspondiente a su envío. Cuando un proceso recibe un mensaje de solicitud de permiso, la acción que toma el proceso receptor es la siguiente: 1. Si el receptor no está en su región crítica y no desea entrar en ella, se dice que está en situación de permiso concedido (CONCEDIDO) y envía inmediatamente un mensaje de réplica al proceso que solitó el permiso. 2. Si el receptor está ejecutando en su región crítica, se dice que tiene el permiso (OTORGADO), no envía réplica al emisor y encola la petición. Como vemos, si un proceso no contesta significa que no concede el permiso de entrada. 3. Si el receptor desea también entrar en la región crítica, pero aún no lo ha conseguido se dice que está en estado de solicitud (SOLICITANDO), compara el reloj del mensaje entrante con el del mensaje que ha enviado al resto de los procesos para solicitar el permiso. El más bajo gana. Si gana el emisor del mensaje entrante, el receptor envía a este la réplica. Si gana el receptor, encola el mensaje y no envía réplica. Inicialización: estado:=CONCEDIDO;
  15. 15. Para obtener el permiso: estado:=SOLICITANDO; multicast de solicitud de permiso al resto; T:=Reloj lógico del mensaje de petición; Wait until(Número de réplicas recibidas=n-1); estado:=OTORGADO; Cuando se recibe una petición <Ci, pi> en pj: if(estado=OTORGADO or (estado=SOLICITANDO and C.pj < C.pi) ) then Encola la petición de pi sin replicar else Replica inmediatamente a pi fi Para conceder el permiso tras abandonar la región crítica: estado=CONCEDIDO; Replica a todos las peticiones en la cola; Fig. 3.6 El algoritmo de Ricart y Agrawala En conjunto, el algoritmo es más lento, más complicado y menos robusto que el algoritmo centralizado, de modo que ¿porqué molestarse estudiándolo? Porque demuestra que un algoritmo distribuido, sin un control central, es posible, lo que estimula el estudio de soluciones distribuidas más avanzadas. 3.2.3 El algoritmo en anillo Esta basado en una disposición de los n procesos que están interesados en utilizar la región crítica en un anillo lógico. Se concede la entrada en la región crítica al proceso que obtiene el denominado testigo. El testigo es un mensaje que se pasa de proceso en proceso en un sentido único recorriendo el anillo. Cada proceso tiene la dirección del proceso al que pasa el testigo. Cuando un proceso recibe el testigo, si no está interesado en la región crítica, pasa es testigo a su vecino. Si necesita entrar en la región crítica, espera bloqueado la llegada del testigo, que es retenido y no es entregado al vecino sino cuando abandona la región crítica. Para obtener el testigo, como máximo se emplean n-1 mensajes. Una desventaja es que los mensajes se envían continuamente aun en el caso de que ningún proceso reclame el testigo. Otra es que este algoritmo tiene n puntos de fallo, si bien la recuperación es más fácil que en los casos anteriores. Si cada vez que se recibe el testigo se envía un mensaje de confirmación, es sencillo detectar la caída de un proceso y retirarlo del anillo. 3.3 Algoritmos de elección Una elección es un procedimiento cuya función es elegir un proceso en un grupo a fin de que este desempeñe un papel determinado, como puede ser centralizar peticiones de entrada en una región crítica, a modo de coordinador. Vamos a considerar que los procesos implicados en la elección son idénticos, sin que ninguno de ellos tenga una característica destacable como para ser el coordinador idóneo. Cada proceso tiene un identificador único como puede ser su dirección de red. En general, los algoritmos de
  16. 16. elección intentan localizar al proceso con el identificador más alto para hacerlo coordinador. Para enviar los mensajes, los procesos necesitan conocer las direcciones de red de todo el grupo de procesos en busca de coordinador, de modo que la elección ya estaría hecha de antemano. El problema es que los procesos desconocen cuáles de ellos están aún activos y cuáles no. El requisito que debe cumplir una elección de coordinador es que esta sea única. Los algoritmos difieren unos de otros en el modo de conseguirlo. El algoritmo del matón Este algoritmo data de 1982 y es debido a García-Molina. Cuando un proceso se apercibe de que el coordinador no responde a sus mensajes, inicia una convocatoria de elección. Una elección se desarrolla como sigue: 1. P envía un mensaje de tipo ELECCION a todos los procesos con identificadores más altos. 2. Si ninguno de ellos responde, P gana la elección y se convierte en el coordinador. 3. En cuanto P recibe el mensaje de respuesta de alguno de ellos, renuncia a ser el coordinador y su trabajo ha terminado. Cada uno de estos procesos, tras enviar el mensaje, convocan una elección En cualquier momento, un proceso puede recibir un mensaje ELECTION de uno de los procesos con identificador inferior. La rutina que sirve el mensaje envía un mensaje de confirmación y toma el control iniciando una elección, a menos que ya esté celebrando una. Llega un momento en que todos los procesos renuncian y uno de ellos se convierte en el nuevo coordinador, hecho que anuncia al resto de los procesos mediante el envío de un mensaje. Si un proceso que inicialmente estaba caído se recupera, inicia una elección. Si es el de identificador más alto, ganará la elección y asumirá el papel de coordinador. Siempre gana el proceso de identifica-dor más grande, de ahí el nombre del algoritmo. En la figura 3.7 vemos el desarrollo de una elección en un grupo de ocho procesos numerados del 0 al 7, siendo este último el coordinador que, en un momento dado, deja de estar operativo. El proceso 4 es el primero en darse cuenta de que el coordinador no responde y convoca una elección, enviando un mensaje de tipo ELECCION a los procesos 5, 6 y 7, los dos primeros confirmando el mensaje. En cuanto el proceso 4 recibe la confirmación del proceso 5 da su trabajo por terminado. Sabe que él no será el coordinador, sino uno de los superiores que han confirmado. Los procesos 5 y 6 convocan elecciones de forma más o menos simultánea. El proceso cinco recibe confirmación del 6 y renuncia. El proceso 6 no recibe confirmación alguna, de modo que se erige en ganador, algo que anuncia al resto enviándoles un mensaje COORDINADOR. El proceso 4, que estaba esperando el resultado de la elección -aunque ya lo casi lo conocía- reanuda su trabajo, esta vez con un nuevo coordinador. 3.4 Transacciones atómicas El paradigma cliente-servidor proporciona una buena forma de estructurar el sistema y de desarrollar aplicaciones, pero necesita del concepto de transacción para controlar secuencias complejas de interacciones entre el cliente y el servidor. Sin transacciones no se puede conseguir que los sistemas distribuidos funcionen en las aplicaciones típicas de la vida real. Los conceptos de transacciones fueron concebidos para poder abordar la complejidad de las aplicaciones on-line en sistemas de un único procesador. Estos conceptos, ya veteranos, son hoy día incluso más críticos en la implementa-ción con éxito de sistemas masivamente distribuidos, que operan y fallan en formas mucho más complejas. Los sistemas de proceso de trasacciones fueron pioneros en conceptos de computación distribuida y computación tolerante a fallos. Ellos introdujeron los datos distribuidos en aras de la fiabilidad, disponibilidad y prestaciones. Ellos desarrollaron el almacenamiento tolerante a fallos y el proceso tolerante a fallos a fin de garantizar la disponibilidad de datos y aplicaciones. Y fueron ellos quienes desarrollaron el modelo cliente-servidor y las llamadas a procedimiento remoto para la computación distribuida. Y, lo más importante, las propiedades ACID de las transacciones han emergido como los conceptos unificadores de la computación distribuida ([Gra93]). Como puede apreciarse, no es posible obviar el tópico de las transacciones atómicas en un curso sobre sistemas distribuidos. En esta sección nos ocupamos de ellas, tratando algunos de sus múltiples aspectos.
  17. 17. 3.4.1 Introducción a las transacciones atómicas Pensemos en una primitiva de sincronización como un semáforo. Subir o bajar un semáforno es una operación de muy bajo nivel que obliga al programador a tratar los detalles de la exclusión mutua, la gestión de la región crítica, la recuperación en caso de fallo, etc, cuando opera sobre datos compartidos. Lo que nos gustaría en un entorno de datos compartidos y con componentes suscepti-bles de fallo es disponer de primitivas de manipulación de datos de más alto nivel que permitan al programador: 1. Concentrarse en el problema, ignorando que los datos son accedidos de forma concurrente 2. Ignorar que un fallo puede dejar los datos en un estado inconsistente. Estas primitivas se utilizan ampliamente en los sistemas distribuidos (su finalidad es compartir datos y recursos y tienen más posibilidades de fallo) y se llaman transacciones atómicas. El uso de las transacciones se remonta a los años 60 cuando los datos se almacenaban en cintas magnéticas. Una base de datos residía en una cinta. que se llamaba el “fichero maestro”. Las actualizaciones residían en una o más cintas (por ejemplo las ventas diarias o semanales) llamadas “ficheros de movimientos”. Maestro y movimientos se montaban en el computador para producir un nuevo fichero maestro, que sería utilizado al día o a la semana siguiente con nuevos ficheros de movimientos. La ventaja de este método -no reconocida suficientemente en aquellos años- es que si el programa que llevaba a cabo las actualizaciones fallaba, se producía una caída en la alimentación eléctirica, etc y el proceso de actualización se interrumpía, siempre se podía descartar la nueva cinta que había quedado a medias y volver sobre el maestro y las actualizaciones para generar de nuevo el fichero maestro. Así, una actualización del maestro procedía correctamente hasta el final o no se modificaba en absoluto. Esta propiedad era una transacción atómica sobre el objeto “fichero maestro”. Consideremos ahora una aplicación bancaria que realiza una retirada de una cantidad de una cuenta y el ingreso correspondiente en otra cuenta distinta. Si el computador falla después de la retirada y antes del ingreso, el dinero se desvanece. Puede que ambas cuentas residan en distintas máquinas y el fallo se deba a una pérdida de la conexión telefónica entre ambas operaciones. Sería necesario agrupar ambas operaciones en una transacción atómica como en el ejemplo de la cinta magnética que garantizase que la operación se realiza completamente o no se realiza. La clave reside en volver al estado inicial de las cuentas si es que se ha producido un fallo en mitad del proceso. Esta habilidad es proporcionada por las transacciones atómicas. 3.4.2 Servicios transaccionales Conviene examinar la aplicación bancaria anterior en el contexto del modelo cliente-servidor. Cuando decimos que un servidor porporciona operaciones atómicas significa que el efecto de desarrollar una operación en beneficio del cliente: Está libre de interferencia de las operaciones realizadas en beneficio de otros clientes Bien la operación concluye completamente o no tiene efecto alguno si el servidor falla. Una transacción entre un cliente y un servidor es una secuencia de interacciones. El servidor bancario proporciona las operaciones Depósito, Retirada, Balance, Total Sucursal?. sobre una serie de objetos, en este caso cuentas: Depósito(Cuenta, Cantidad) Deposita la cantidad Cantidad en la cuenta Cuenta Retirada(Cuenta, Cantidad) Retira la cantidad Cantidad de la cuenta Cuenta
  18. 18. Balance(Cuenta) p Cantidad Devuelve el balance de la cuenta Cuenta Total Sucursal e Total Devuelve la suma de todos los balances Consideremos un cliente que quiere realizar una serie de operaciones sobre las cuentas A, B, C. La primera operación transfiere 100 pesetas de A a B. La segunda transfiere 200 pesetas de C a B: Transacción: T: Retirada(A, 100); Depósito(B, 100); Retirada(C, 200); Depósito(B, 200); End Transacción?(T) Como vemos, desde el punto de vista del cliente, una transacción es una secuencia de operaciones que se realizan en un sólo paso, llevando al servidor de un estado consistente a otro. El cliente no es consciente de que otros clientes pueden realizar operaciones sobre las cuentas A y B. A un servidor de este tipo se le conoce como servidor transaccional o que provee de un servicio transaccional. En un servicio transaccional, el cliente, cuando inicia una transacción con el servidor, emite la petición Abrir Transacción? y el servidor le devuelve un indentificador de transacción. Este indentificador será usado en las operaciones siguientes. El cliente notifica al servidor el final de la secuencia de operaciones mediante la primitiva Cierra Transacción?. Abrir Transacción n Trans Arranca en el servidor una nueva transacción y devuelve un único identificador de transacción o TID, que será usado como parámetro en el resto de las operaciones de la transacción Cerrar Transacción?(Trans) r (Compromiso, Aborto) Termina la transacción. Si devuelve un compromiso, indica que la transacción se ha comprometido (se ha realizado en su totalidad). Si devuelve un valor de Aborto, la transac-ción no se ha realizado. Abortar Transacción?(Trans) Aborta la transacción La transacción puede abortar por varias razones. Entre ellas la naturaleza misma de la transacción y su desarrollo, conflictos con otra transacción o el fallo de procesos o máquinas. La transacción puede ser abortada, tanto por el cliente como por el servidor. Cuando el servidor decide unilateralmente abortar la transacción en curso, la operación en curso devuelve un código de error como SERVER_ABORT. Veamos cual es el comportamiento de cliente y servidor en presencia de fallos: Fallo en el servidor Si un servidor transaccional falla inesperadamente, cuando vuelve a arrancar, aborta toda transacción no comprometida utilizando un procedimiento de recuperación para restaurar los valores de los items de datos provisionales a los valores definitivos producidos por la transacción comprometida más recientemente previa al fallo. Replica al cliente la operación solicitada con un valor SERVER_ABORT. Otra posibilidad es que el cliente dé un plazo de respuesta al servidor para cada operación emitida. Si el servidor se recupera dentro del plazo, continúa con la transacción en curso en lugar de abortarla. Fallo en el cliente Si un cliente falla inesperadamente en el desarrollo de una transacción, el servidor puede dar un plazo de expiración a la transacción. Si una nueva operación de la transacción no llega en ese plazo el servidor aborta la transacción e inicia el procedimiento de recuperación.
  19. 19. 3.4.3 Propiedades de las transacciones atómicas Las transacciones tienen cuatro propiedades fundamentales que se conocen por el acrónimo ACID: Atomicidad, Consistencia, serializabilidad o aislamiento (“Isolation”) y Durabilidad. 3.4.3.1 Atomicidad La atomicidad garantiza que la transacción procede hasta que se completa o no se realiza en absoluto. Si se completa, esto ocurre en una acción indivisible e instantánea. Mientras una transacción está en progreso, otros procesos, estén o no estén implicados en transacciones, no pueden ver los estados intermedios. Por ejemplo, supongamos una transacción que se ocupa de añadir octetos a un fichero de 10 octetos. Mientras la transacción esta en curso, otro proceso debe ver un fichero de sólo 10 bytes. Cuando la transacción se compromete, el fichero instantánemente aparece con su nuevo tamaño. Un sistema de ficheros que garantiza este comportamiento es un sistema de ficheros transaccional. La mayoría de los sistemas de ficheros no son transaccionales. 3.4.3.2 Consistencia La propiedad de la consistencia obliga cumplir ciertos invariantes de los datos del servidor. Por ejemplo, como resultado de una transferencia interna, una sucursal bancaria debe mantener el mismo dinero en su saldo que antes de que la transacción se realice. La consistencia de los datos puede ser violada por el cliente si las operaciones que emite no son consistentes y por el servidor si no dispone de un adecuado mecanismo de control de concurrencia. Si las operaciones de forman una transacción T que emite un cliente son consistentes, el servidor garantiza que la consistencia de los datos que T comparte con otra transacción U no va ser violada por la concurrencia de las operaciones de U. 3.4.3.3 Serializabilidad La tercera propiedad dice que las transacciones deben ser aisladas. Aislada significa transacciones concurrentes en un servidor no interfieren las unas con las otras. Una forma de lograr el aislamiento es anular la concurrencia del servidor de modo que ejecute las transacciones de forma estrictamente secuencial, una tras otra. El objetivo de un servidor, sin embargo es maximizar sus prestaciones, algo que pasa por la atención concurrente al mayor número de transacciones posible. Esto significa que si un servidor está atendiendo a dos o más transacciones simultáneamente que operan sobre datos compartidos por los clientes -un fichero, por ejemplo-, el servidor transaccional debe garantizar que el resultado final de estos datos es aquel que produce una realización estrictamente secuencial de las transacciones. Esta realización, no obstante, es decidida arbitrariamente por el servidor. Supongamos que un servidor transaccional mantiene una variable x que es accedida por tres clientes. Las transacciones de los clientes en un momento dado son las siguientes: Proceso 1: Abrir Transacción; x := 0; x := x + 1; Cerrar Transacción; Proceso 2: Abrir Transacción; x := 0; x := x + 2; Cerrar Transacción; Proceso 3: Abrir Transacción; x := 0; x := x + 3; Cerrar Transacción; Las peticiones de las distintas transacciones llegan al servidor entrelazadas. A cada secuencia de ejecución de las peticiones por parte del servidor se le llama entrelazado o planificación. Si el servidor es transaccional, no todas las planificaciones son válidas. En la tabla que sigue vemos tres planificaciones posibles, donde el tiempo transcurre hacia la derecha: Planificación 1 x := 0; x := x + 1; x := 0; x := x + 2; x := 0; x := x + 3; legal Planificación 2 x := 0; x := 0; x := x + 1; x := x + 2; x := 0; x := x + 3; legal
  20. 20. Planificación 3 x := 0; x := 0; x := x + 1; x := 0; x := x + 2; x := x + 3; ilegal tiempo En la planificación 1 las transacciones son ejecutadas de forma estrictamente secuencial, y el valor final es 3. Decimos que esta planificación ha sido serializada. La planificación 2 no es serializada, pero es legal por que, al final, x toma un valor que podría haber sido alcanzado por una planificación estrictamente secuencial. La planificación 3 es ilegal, ya que x termina con un valor de 5, valor que imposible alcanzar con una planificación serializada. Equivalencia serial Si un entrelazado de dos o más transacciones tiene el mismo efecto sobre los datos que alguna ejecución serializada de dichas transacciones decimos que el entrelazado es serialmente equivalente. La planificación 2 de la figura es serialmente equivalente. 3.4.3.4 Durabilidad Una transacción debe ser durable. Esta propiedad establece que si un servidor compromete una transacción -por ejemplo con una réplica a una primitiva Cerrar Transacción(Trans)que devuelva el valor Compromiso-, no importa lo que ocurra, los cambios son ya permanentes. Ningún fallo después del compromiso puede desacer los resultados o causar su desaparición, con la consiguiente confusión posterior en el cliente. 2.3 Bibliografía [Cou95] Coulouris A. et al., Distributed Systems, 2nd. Edition. Addison-Wesley, 1995. [Tan95] Tanenbaum, A., Distributed Operating Systems, Prentice-Hall, 1995. [Cou94] Coulouris, G., Distributed Systems, Concepts and Design, Second Edition, Addison-Wesley, 1994. [Gra93] Gray, J., Reuter, A., Transaction Processing, Concepts and Techniques, Morgan Kaufmann Publishers, 1993. [Lam78] Lamport, L., “Time, Clocks, and the Ordering of Events in a Distributed System”, Communications of the ACM, Number 7, Volume 21, July, 1978. [Lis93] Liskov, B., “Practical uses of syncronized clocks in distributed systems”, Distributed Computing, No. 6, pp. 211–219, 1993. 2.1.1. Comunicación con los clientes-Servidor (Socket) Origen de los socket tuvo lugar en una variante del sistema operativo Unix conocida como BSD Unix. En la universidad de Berkeley, en los inicios del Internet, pronto se hizo evidente que los programadores necesitarían un medio sencillo y eficaz para escribir programas capaces de intercomunicarse entre sí. Esta necesidad dio origen a la primera especificación e implementación de sockets. Cliente-Servidor es el modelo que actualmente domina el ámbito de comunicación, ya que descentraliza los procesos y los recursos. Es un Sistema donde el cliente es una aplicación, en un equipo, que solicita un determinado servicio y existe un software, en otro equipo, que lo proporciona.
  21. 21. Los servicios pueden ser; a)Ejecución de un programa. b)Acceso a una Base 2.1.2 comunicacion con rpc RCP (REMOTE PROCEDURE CALL) El mecanismo general para las aplicaciones cliente-servidor se proporciona por el paquete Remote Procedure Call (RPC). RPC fue desarrollado por Sun Microsystems y es una colección de herramientas y funciones de biblioteca. Aplicaciones importantes construidas sobre RPC son NIS, Sistema de Información de Red y NFS, Sistema de Ficheros de Red. Un servidor RPC consiste en una colección de procedimientos que un cliente puede solicitar por el envío de una petición RPC al servidor junto con los parámetros del procedimiento. El servidor invocará el procedimiento indicado en nombre del cliente, entregando el valor de retorno, si hay alguno. Para ser independiente de la máquina, todos los datos intercambiados entre el cliente y el servidor se convierten al formato External Data Representation (XDR) por el emisor, y son reconvertidos a la representación local por el receptor. RPC confía en sockets estandard UDP y TCP para transportar los datos en formato XDR hacia el host remoto. Sun amablemente a puesto RPC en el dominio público; se describe en una serie de RFCs. La comunicación entre servidores RPC y clientes es un tanto peculiar. Un servidor RPC ofrece una o más colecciones de procedimientos; cada conjunto se llama un programa y es idenficado de forma única por un número de programa. Una lista que relaciona nombres de servicio con números de programa se mantiene usualmente en /etc/rpc. En esta figura, la llamada remota toma 10 pasos, en el primero de los cuales el programa cliente (o procedimiento) llama al procedimiento stub enlazado en su propio espacio de direcciones. Los parámetros pueden pasarse de la manera usual y hasta aquí el cliente no nota nada inusual en esta llamada ya que es una llamada local normal. El stub cliente reúne luego los parámetros y los empaqueta en un mensaje. Esta operación se conoce como reunión de argumentos (parameter marshalling). Después que se ha construido el mensaje, se lo pasa a la capa de transporte para su transmisión (paso 2). En un sistema LAN con un servicio sin conexiones, la entidad de transporte probablemente sólo le agrega al mensaje un encabezamiento y lo coloca en la subred sin mayor trabajo (paso 3). En una WAN, la transmisión real puede ser más complicada. Cuando el mensaje llega al servidor, la entidad de transporte lo pasa al stub del servidor (paso 4), que desempaqueta los parámetros. El stub servidor llama luego al procedimiento servidor (paso 5), pasándole los parámetros de manera estándar. El procedimiento servidor no tiene forma de saber que está siendo activado remotamente, debido a que se lo llama desde un procedimiento local que cumple con todas las reglas estándares. Únicamente el stub sabe que está ocurriendo algo particular. Después que ha completado su trabajo, el procedimiento servidor retorna (paso 6) de la misma forma en que retornan otros procedimientos cuando terminan y, desde luego, puede retornar un resultado a un llamador. El stub servidor empaqueta luego el resultado en un mensaje y lo entrega a la interfaz con transporte (paso 7), posiblemente mediante una llamada al sistema, al igual que en el paso 2. Después que la respuesta retorna a la máquina cliente (paso 8), la misma se entrega al stub cliente (paso 9) que desempaqueta las respuestas. Finalmente, el stub cliente retorna a su llamador, el procedimiento cliente y cualquier valor devuelto por el servidor en el paso 6, se entrega al cliente en el paso 10. El propósito de todo el mecanismo de la Fig.1 es darle al cliente (procedimiento cliente) la ilusión de que está haciendo una llamada a un procedimiento local. Dado el éxito de la ilusión, ya que el cliente no puede saber que el
  22. 22. servidor es remoto, se dice que el mecanismo es transparente. Sin embargo, una inspección más de cerca revela algunas dificultades en alcanzar la total transparencia. 2.1.3 comunicacion en grupo La comunicación se clasifica deacuerdo al numero de usuarios alos que se le a enviado el mensaje. -BROADCAST O DIFUSION FORZADA un nodo emite todos los escuchan y solo contesta a quien va dirigido el mensaje -MULTICAST se entrega el msj a todos los anfitriones HOST que están compuestos de ciertas características. -UNICAST o POINTCAST un nodo emite y otro recibe, solo escucha aquel a quien se dirigió el msj. Una clasificación adicional es la realizada en base a grupos -LISTAS DE DESTINARIOS se tiene una lista de aquellos alos que se les enviara el mensaje. -IDENTIFICADOR DE GRUPO se forman grupos y el msj es dirigido solo a los miembros de ese grupo. -PREDICADOR DE PERTENENCIA se envía y otro recibe, solo escucha aquel a quien se dirigió el mensaje. 2.1.4 tolerancia a fallos La difusión de los sistemas distribuidos incrementa la demanda de sistemas que esencialmente nunca fallen [25, Tanenbaum]. Los sistemas tolerantes a fallos requerirán cada vez más una considerable redundancia en hardware, comunicaciones, software, datos, etc. La réplica de archivos sería un requisito esencial. También debería contemplarse la posibilidad de que los sistemas funcionen aún con la carencia de parte de los datos. Los tiempos de fallo aceptables por los usuarios serán cada vez menores. 2.2 sincronizacion sistemas distribuidos En sistemas con una única CPU las regiones críticas, la exclusión mutua y otros problemas de sincronización son resueltos generalmente utilizando métodos como semáforos y monitores. Estos métodos no se ajustan para ser usados en sistemas distribuidos ya que invariablemente se basan en la existencia de una memoria compartida. No es posible reunir toda la información sobre el sistema en un punto y que luego algún proceso la examine y tome las decisiones. En general los algoritmos distribuidos tienen las siguientes propiedades: 1)- la información relevante está repartida entre muchas máquinas 2)- los procesos toman decisiones basadas solamente en la información local disponible 3) - debería poder evitarse un solo punto que falle en el sistema 4)- no existe un reloj común u otro tiempo global exacto
  23. 23. Si para asignar un recurso de E/S un proceso debe recolectar información de todos los procesos para tomar la decisión esto implica una gran carga para este proceso y su caída afectaría en mucho al sistema. Idealmente, un sistema distribuido debería ser más confiable que las máquinas individuales. Alcanzar la sincronización sin la centralización requiere hacer cosas en forma distinta a los sistemas operativos tradicionales. 2.2.1 relojes fisicos El algoritmo de Lamport proporciona un orden de eventos sin ambigüedades, pero [25, Tanenbaum]: Los valores de tiempo asignados a los eventos no tienen porqué ser cercanos a los tiempos reales en los que ocurren. En ciertos sistemas (ej.: sistemas de tiempo real ), es importante la hora real del reloj: Se precisan relojes físicos externos (más de uno). Se deben sincronizar: Con los relojes del mundo real. Entre sí. La medición del tiempo real con alta precisión no es sencilla. Desde antiguo el tiempo se ha medido astronómicamente. Se considera el día solar al intervalo entre dos tránsitos consecutivos del sol, donde el tránsito del sol es el evento en que el sol alcanza su punto aparentemente más alto en el cielo. El segundo solar se define como 1 / 86.400 de un día solar. Como el período de rotación de la tierra no es constante, se considera el segundo solar promedio de un gran número de días. Los físicos definieron al segundo como el tiempo que tarda el átomo de cesio 133 para hacer 9.192.631.770 transiciones: Se tomó este número para que el segundo atómico coincida con el segundo solar promedio de 1958. La Oficina Internacional de la Hora en París (BIH) recibe las indicaciones de cerca de 50 relojes atómicos en el mundo y calcula el tiempo atómico internacional (TAI). Como consecuencia de que el día solar promedio (DSP) es cada vez mayor, un día TAI es 3 mseg menor que un DSP: La BIH introduce segundos de salto para hacer las correcciones necesarias para que permanezcan en fase: El sistema de tiempo basado en los segundos TAI. El movimiento aparente del sol. Surge el tiempo coordenado universal (UTC). El Instituto Nacional del Tiempo Estándar (NIST) de EE. UU. y de otros países: Operan estaciones de radio de onda corta o satélites de comunicaciones. Transmiten pulsos UTC con cierta regularidad establecida (cada segundo, cada 0,5 mseg, etc.). Se deben conocer con precisión la posición relativa del emisor y del receptor: Se debe compensar el retraso de propagación de la señal. Si la señal se recibe por módem también se debe compensar por la ruta de la señal y la velocidad del módem. Se dificulta la obtención del tiempo con una precisión extremadamente alta. 2.2.2 relojes logicos Las computadoras poseen un circuito para el registro del tiempo conocido como dispositivo reloj [25, Tanenbaum]. Es un cronómetro consistente en un cristal de cuarzo de precisión sometido a una tensión eléctrica que: • Oscila con una frecuencia bien definida que depende de: o Al forma en que se corte el cristal. o El tipo de cristal. o La magnitud de la tensión. • A cada cristal se le asocian dos registros:
  24. 24. o Registro contador. o Registro mantenedor. • Cada oscilación del cristal decrementa en “1” al contador. • Cuando el contador llega a “0”: o Se genera una interrupción. o El contador se vuelve a cargar mediante el registro mantenedor. • Se puede programar un cronómetro para que genere una interrupción “x” veces por segundo. • Cada interrupción se denomina marca de reloj. Para una computadora y un reloj: • No interesan pequeños desfasajes del reloj porque: o Todos los procesos de la máquina usan el mismo reloj y tendrán consistencia interna. o Importan los tiempos relativos. Para varias computadoras con sus respectivos relojes: • Es imposible garantizar que los cristales de computadoras distintas oscilen con la misma frecuencia. • Habrá una pérdida de sincronía en los relojes (de software), es decir que tendrán valores distintos al ser leidos. La diferencia entre los valores del tiempo se llama distorsión del reloj y podría generar fallas en los programas dependientes del tiempo. Lamport demostró que la sincronización de relojes es posible y presentó un algoritmo para lograrlo. Lamport señaló que la sincronización de relojes no tiene que ser absoluta: • Si 2 procesos no interactúan no es necesario que sus relojes estén sincronizados. • Generalmente lo importante no es que los procesos estén de acuerdo en la hora, pero sí importa que coincidan en el orden en que ocurren los eventos. 2.2.3 usos de la sincroninizacion manejo de cache comunicacion en grupo exclusion mutua eleccion transacciones atomicas e interbloqueo memoria cache En los sistemas de archivos convencionales, el fundamento para la memoria caché es la reducción de la E/S de disco (lo que aumenta el rendimiento), en un SAD el objetivo es reducir el tráfico en la red. Esquema Básico, el concepto de memoria caché es sencillo, si los datos necesarios para satisfacer la solicitud de acceso no se encuentran en la memoria cache, se trae una copia de servicio al usuario y los accesos se llevan a cabo con la copia de memoria caché. La idea es conservar allí los bloques de disco de acceso mas reciente, para así manejar localmente los accesos repetidos a la misma información y no aumentar el tráfico de la red. Se utiliza una política de reemplazo (por ejemplo, la de utilización menos reciente) para limitar el tamaño de la memoria caché. Políticas de Actualización, la política empleada para escribir los bloques de datos modificados en la copia maestra del servidor tiene un efecto decisivo sobre la
  25. 25. confiabilidad y el rendimiento del sistema. La política mas sencilla consiste en escribir los datos directamente en el disco tan pronto se coloquen en una memoria caché. La ventaja de la escritura directa es su confiabilidad, ya que se pierde poca información si un sistema cliente falla. Sin embargo, esta política requiere que cada acceso de escritura espere hasta que se envíe la información al servidor, por lo que representa una escritura de escaso rendimiento. La memoria caché con escritura directa equivale a usar el servicio remoto para accesos de escritura y explotar la memoria cache únicamente para accesos de lectura. NFS proporciona el acceso de escritura directa. Consistencia, una maquina cliente se enfrenta al problema de decidir si una copia de datos en memoria caché local es consistente con la copia maestra ( y por tanto, puede usarse). Si la maquina cliente determina que sus datos en memoria caché están desfasados, ya no pueden servir para los accesos y hay que colocar en la memoria caché una copia actualizada de los datos. Existen dos enfoques para verificar la validez de los datos en memoria caché ..: Enfoque iniciado por el cliente, el cliente inicia una comprobación de validez, en la cual se pone en contacto con el servidor y comprueban si los datos locales son consistentes con la copia maestra. Enfoque iniciado por el servidor, el servidor anota, para cada cliente, las partes de los archivos que coloca en memoria cache, y cuando detecta una inconsistencia, debe reaccionar. Una posible fuente inconsistencia ocurre cuando dos clientes, que trabajan en modos conflictivos, colocan en memoria caché un archivo. Comunicación en grupos (Algoritmos Para la Sincronización de Relojes) Si una máquina tiene un receptor de UTC, todas las máquinas deben sincronizarse con ella. Si ninguna máquina tiene un receptor de UTC: • Cada máquina lleva el registro de su propio tiempo. • Se debe mantener el tiempo de todas las máquinas tan cercano como sea posible. Se supone que cada máquina tiene un cronómetro que provoca una interrupción “h” veces por segundo. Cuando el cronómetro se detiene, el manejador de interrupciones añade “1” a un reloj en software. El reloj en software mantiene un registro del número de marcas (interrupciones) a partir de cierta fecha acordada antes; al valor de este reloj se lo llama “C”. Algoritmo de Cristian Es adecuado para sistemas en los que: • Una máquina tiene un receptor UTC, por lo que se la llama despachador del tiempo. • El objetivo es sincronizar todas las máquinas con ella. Cada máquina envía un mensaje al servidor para solicitar el tiempo actual, periódicamente, en un tiempo no mayor que è prontamente con un mensaje que contiene el tiempo actual “CUTC”. Cuando el emisor obtiene la respuesta puede hacer que su tiempo sea “CUTC”. Un gran problema es que el tiempo no puede correr hacia atrás: • “CUTC” no puede ser menor que el tiempo actual “C” del emisor. • La atención del requerimiento en el servidor de tiempos requiere un tiempo del manejador de interrupciones. • También se debe considerar el tiempo de transmisión. El cambio del reloj se debe introducir de manera global: • Si el cronómetro genera 100 interrupciones por segundo: Cada interrupción añade 10 mseg al tiempo. Para atrasar solo agregaría 9 mseg. Para adelantar agregaría 11 mseg. La corrección por el tiempo del servidor y el tiempo de transmisión se hace midiendo en el emisor: • El tiempo inicial (envío) “T0”. • El tiempo final (recepción) “T1”. • Ambos tiempos se miden con el mismo reloj. El tiempo de propagación del mensaje será (T1 - T0) / 2. Si el tiempo del servidor para manejar la interrupción y procesar el mensaje es “I”: • El tiempo de propagación será (T1 - T0 - I) / 2. Para mejorar la precisión: • Se toman varias mediciones. • Se descartan los valores extremos. • Se promedia el resto. El tiempo de propagación se suma al tiempo del servidor para sincronizar al emisor cuando éste recibe la respuesta. Algoritmo de Berkeley
  26. 26. En el algoritmo de Cristian el servidor de tiempo es pasivo. En el algoritmo de Berkeley el servidor de tiempo: • Es activo. • Realiza un muestreo periódico de todas las máquinas para preguntarles el tiempo. • Con las respuestas: Calcula un tiempo promedio. Indica a las demás máquinas que avancen su reloj o disminuyan la velocidad del mismo hasta lograr la disminución requerida. Es adecuado cuando no se dispone de un receptor UTC. Algoritmos con Promedio Los anteriores son algoritmos centralizados. Una clase de algoritmos descentralizados divide el tiempo en intervalos de resincronización de longitud fija: • El i -ésimo intervalo: Inicia en “T0 + i R” y va hasta “T0 + (i + 1) R”. “T0” es un momento ya acordado en el pasado. “R” es un parámetro del sistema. Al inicio de cada intervalo cada máquina transmite el tiempo actual según su reloj. Debido a la diferente velocidad de los relojes las transmisiones no serán simultáneas. Luego de que una máquina transmite su hora, inicializa un cronómetro local para reunir las demás transmisiones que lleguen en cierto intervalo “S”. Cuando recibe todas las transmisiones se ejecuta un algoritmo para calcular una nueva hora para los relojes. Una variante es promediar los valores de todas las demás máquinas. Otra variante es descartar los valores extremos antes de promediar (los “m” mayores y los “m” menores). Una mejora al algoritmo considera la corrección por tiempos de propagación. Varias Fuentes Externas de Tiempo Los sistemas que requieren una sincronización muy precisa con UTC se pueden equipar con varios receptores de UTC. Las distintas fuentes de tiempo generaran distintos rangos (intervalos de tiempo) donde “caerán” los respectivos UTC, por lo que es necesaria una sincronización. Como la transmisión no es instantánea se genera una cierta incertidumbre en el tiempo. Cuando un procesador obtiene todos los rangos de UTC: • Verifica si alguno de ellos es ajeno a los demás y de serlo lo descarta por ser un valor extremo. • Calcula la intersección (en el tiempo) de los demás rangos. • La intersección determina un intervalo cuyo punto medio será el UTC y la hora del reloj interno. Se deben compensar los retrasos de transmisión y las diferencias de velocidades de los relojes. Se debe asegurar que el tiempo no corra hacia atrás. Se debe resincronizar periódicamente desde las fuentes externas de UTC. Exclusión Mutua Cuando un proceso debe leer o actualizar ciertas estructuras de datos compartidas: • Primero ingresa a una región crítica para lograr la exclusión mutua y garantizar que ningún otro proceso utilizará las estructuras de datos al mismo tiempo. En sistemas monoprocesadores las regiones críticas se protegen con semáforos, monitores y similares. En sistemas distribuidos la cuestión es más compleja. Un Algoritmo Centralizado La forma más directa de lograr la exclusión mutua en un sistema distribuido es simular a la forma en que se lleva a cabo en un sistema monoprocesador. Se elige un proceso coordinador. Cuando un proceso desea ingresar a una región crítica: • Envía un mensaje de solicitud al coordinador: Indicando la región crítica. Solicitando permiso de acceso. • Si ningún otro proceso está en ese momento en esa región crítica: El coordinador envía una respuesta otorgando el permiso.

×