P. 1
[02] Sistemas Distribuidos - La Comunicación

[02] Sistemas Distribuidos - La Comunicación

4.5

|Views: 5.332|Likes:
Publicado porGust Alva
En un sistema convencional, la mayoría de los mecanismos
desarrollados de alguna manera asumen la existencia de una memoria que comparten todos los
procesos. Un ejemplo típico es el del productor-consumidor, donde la comunicación se realiza
mediante un buffer almacenado en memoria común.
En un sistema convencional, la mayoría de los mecanismos
desarrollados de alguna manera asumen la existencia de una memoria que comparten todos los
procesos. Un ejemplo típico es el del productor-consumidor, donde la comunicación se realiza
mediante un buffer almacenado en memoria común.

More info:

Published by: Gust Alva on Jul 29, 2009
Copyright:Attribution Non-commercial

Availability:

Read on Scribd mobile: iPhone, iPad and Android.
download as PDF, TXT or read online from Scribd
See more
See less

08/14/2013

pdf

text

original

Tema

2

La Comunicación en los Sistemas Distribuidos
La diferencia más importante entre un sistema distribuido y un sistema multiusuario convencional es la comunicación de procesos. En un sistema convencional, la mayoría de los mecanismos desarrollados de alguna manera asumen la existencia de una memoria que comparten todos los procesos. Un ejemplo típico es el del productor-consumidor, donde la comunicación se realiza mediante un buffer almacenado en memoria común. El buffer más simple es el semáforo, que requiere tan sólo una palabra de memoria, el semáforo mismo. En un sistema distribuido, sin embargo, no existe en absoluto la memoria compartida, de modo que los mecanismos de comunicación de procesos deben ser rediseñados desde el principio. En este capitulo, discutiremos las reglas que deben observar dos computadores para comunicarse entre sí. Estas reglas se denominan protocolos. Asumiremos que el alumno está ya familiarizado con la pila de protocolos OSI, de modo que introduciremos ATM, un estándar de comunicación de alta velocidad emergente que se está mostrando como el más eficaz en la construcción de sistemas operativos distribuidos. Después examinaremos el modelo de comunicación clienteservidor en detalle. Examinaremos las opciones disponibles en que los procesos pueden intercambiar mensajes. Una de estas formas es la llamada a procedimiento remoto. Este tópico merece un apartado especial por su interés y por su amplia implantación en sistemas comerciales actuales. El capítulo concluirá con la comunicación de más de dos procesos o grupos de procesos.

2.1 Redes ATM
El modelo de referencia OSI fue discutido y consensuado por empresas públicas y privadas de telecomunicaciones en los años setenta y fue implementado en parte en los ochenta. Los años noventa han alumbrado nuevos desarrollos tecnológicos en los niveles inferiores de la pila de protocolos OSI. Uno de ellos es ATM o modo de transferencia asícrono. Es previsible que esta tecnología sea adoptada en la comunicación de los sistemas distribuidos, de modo que es preciso un mínimo acercamiento a la misma por parte de un diseñador de sistemas distribuidos.

UNIVERSIDAD NACIONAL DE TRUJILLO 2 ESCUELA DE INFORMATICA
En los últimos veinticinco años, los computadores han experimentado avances en prestaciones de muchos órdenes de magnitud. Las redes no. Arpanet, la red precursora de Internet entró en funcionamiento en 1969 con líneas punto a punto de 56 Kbytes/s. Hoy todavía los usuarios finales de Internet se comunican a estas velocidades. Los nuevos desarrollos de los noventa proponen estándares que repentinamente saltan a velocidades de 155 Mbytes/s para el usuario final y a 1 Gbyte/s para el tronco principal de Internet. Este cambio de escala en las comunicaciones va a tener un impacto evidente en los sistemas distribuidos, donde van a aparecer nuevas aplicaciones que resultaban impensables hace tan sólo unos pocos años. Esta nueva tecnología, ATM, es la que ahora examinamos.

2.1.1 ¿Qué es el modo de transferencia asíncrono?
A finales de los años ochenta las compañías de telecomunicaciones de todo el mundo comenzaron a darse cuenta de que las telecomunicaciones eran algo más que transmitir la voz humana en una banda de cuatro Khz. Entonces ya hacía tiempo que existían las redes de datos, como X.25, pero eran aún inmaduras y operaban a un máximo de 56 Kb/s o 64 Kb/s. Sistemas como la red Internet no pasaban de ser curiosidades académicas. Cuando estas compañías decidieron construir redes para el siglo 21, se encontraron con un dilema. Por una parte, la voz requiere un ancho de banda muy bajo, pero constante. Por la otra, los datos de los computadores no aparecen en las líneas de comunicación de una forma predecible y con una tasa constante. Al contrario, son de naturaleza explosiva. Repentinamente surge una corriente a la que es preciso asignar un canal del mayor ancho de banda posible. Cuando la comunicación acaba, el ancho de banda que se precisa es nulo. En conclusión, ni la red telefónica de conmutación de circuitos es apropiada para transmitir datos ni las redes de datos de conmutación de paquetes son apropiadas para trasmitir la voz. El compromiso más razonable para atender ambos tipos de tráfico es el modelo híbrido ATM. ATM requiere que sea establecido un circuito virtual antes de establecer la comunicación entre el emisor y el receptor o los receptores de la comunicación. Durante el establecimiento de la conexión, la información del encaminamiento se almacena en los nodos de conmutación ATM que definen la misma. Los paquetes de los protocolos de nivel superior -TCP/IP, FILP, ...- son enviados a la tarjeta o adaptador ATM de la máquina donde corre el proceso de usuario, que los trocea en unidades pequeñas de tamaño fijo denominadas celdas. Las celdas de una conexión siguen la secuencia de nodos que se estableció al crearla. Cuando esta termina, la información relativa a la conexión es eliminada de los nodos de conmutación. Las ventajas de este método son evidentes. La principal es que ahora una única red es capaz de transportar voz, datos, radio, televisión por cable, vídeo, etc, reemplazando a la red de antenas y repetidores de radio y televisión, la maraña de cabes de la red telefónica, el nuevo cableado de la televisión por cable, el cableado de las redes de datos, etc. Además, permite la aparición de nuevos servicios como las videoconferencias, que serán accesibles desde todos los hogares con un único cable. En todos los casos, los nodos intermedios de la conexión ven sólo celdas, el contenido poco importa excepto al extremo final. El hecho de que las celdas sean de tamaño fijo hace que la conmutación sea mucho más rápida, sin necesidad de que sean almacenadas en disco duro como los paquetes de la red Internet. El segundo factor que incrementa la velocidad es que los conmutadores ATM no realizan control de flujo ni comprobación de errores en las celdas. A este método se le

UNIVERSIDAD NACIONAL DE TRUJILLO 3 ESCUELA DE INFORMATICA denomina "cell relay". ATM opera estableciendo circuitos virtuales, pero un circuito sólo es establecido si están disponibles los recursos suficientes para garantizar la calidad del servicio solicitado. ATM tiene su propia pila de protocolo como indica la figura 2.1. A continuación vamos a describir brevemente los tres niveles ATM.

Fig. 2.1 El modelo de referencia ATM.

2.1.2 El nivel físico
Una tarjeta adaptadora ATM se encarga de poner en el cable, sea de cobre o fibra óptica una corriente continua de celdas. Cuando no hay nada que transmitir, se envían celdas vacías. A este modo de transmisión se le llama modo nativo ATM y logra velocidades de transmisión superiores al Gigabit/s con fibra óptica. Alternativamente, las celdas ATM pueden ser enviadas como datos convencionales en los marcos de la red de servicios integrados de banda ancha, B-ISDN. Con este método, definido en el estandar CCITT I.150, se alcanza una velocidad de transmisión de 155 Mbits/s o 622 Mbits/s.

2.1.3 El nivel ATM
Este es el nivel que define el formato de las células y el protocolo orientado a conexión que las transmite. Cuando se discutían las propuestas, los comités europeo y norteamericano estaban enfrentados. Los americanos disponían de líneas de mayor calidad, con supresores de eco, lo que les permitía celdas más grandes, de 64 bits. Los europeos no disponemos de este tipo de líneas, de modo que celdas de 32 eran las más adecuadas. Se llegó a un compromiso de celdas de 48 bytes. Una celda de 48 bytes es demasiado grande para transmisión de voz y demasiado pequeña para transmisión de datos. Y peor aún, a la celda se le añadió un cabecero de 5 bytes. Resultaba una celda de 53 bytes que no enjaba bien en los marcos de la red BISDN. Una celda debe ser partida en dos marcos B-ISDN. A pesar de todo, el estándar ya esta elaborado y existen implementaciones hardware en funcionamiento. La figura 2.2 muestra el formato del cabecero de una celda ATM intercambiada entre un host de usuario y un conmutador ATM. Desgraciadamente, el cabecero entre dos conmutadores ATM es ligeramente distinto, lo que impone adaptadores distintos en hosts y conmutadores. La parte de datos, no obstante, es de 48 bytes en ambos tipos de celdas. Los campos VPI y VCI son los más importantes. VCI significa identificador de canal virtual. VPI es identificación de ruta virtual (Virtual Path Identifier). En cada conmutador, tal y como indica la figura 2.3, existe una tabla de encaminamiento que se establece cuando se crea la conexión. Cuando una celda llega a un conmutador, se examinan los campos VCI y VPI y la celda sale por el puerto correspondiente. Por lo tanto, los campos VCI y VPI de una celda se modifican cada vez que esta atraviesa un conmutador. El campo GFC significa control genérico de flujo. No se utiliza a falta de un acuerdo sobre cómo emplearlo. El campo CLP significa prioridad en la

UNIVERSIDAD NACIONAL DE TRUJILLO 4 ESCUELA DE INFORMATICA
pérdida de celdas y puede utilizarse para distinguir entre unas celdas más importantes que otras en función de su contenido. En caso de congestión en un conmutador, este puede descartar las celdas menos importantes. El campo tipo de datos distingue entre celdas de datos y celdas de control y distingue entre varios tipos de celdas de control. El campo CRC -código de redundancia cíclica- es una comprobación sobre la cabecera completa (no los datos).

Fig. 2.3 Disposición de la cabecera de una celda ATM.

La figura 2.3 muestra cómo se realiza el encaminamiento de las celdas en un conmutador en base a la tabla establecida en el establecimiento de la conexión. Los nodos de una red ATM son de tres tipos:

1. Hosts, que envían y reciben mensajes. 2. Conmutadores de rutas virtuales, que mantienen las tablas de comutación de rutas virtuales. 3. Conmutadores de canales y rutas virtuales, que mantienen tablas de conmutación de canales y de rutas.

Fig. 2.3 Conmutando rutas virtuales en una red ATM.

ATM proporciona un servicio de baja latencia. El retardo que introduce un conmutador es de alrededor de 25 microsegundos. Una ruta transcontinental de 10 conmutadores introduce un retraso de 250 microsegundos, un cuarto de milisegundo, en cada celda que atraviesa los diez conmutadores. Esto significa que la comunicación cliente servidor tendría las mismas prestaciones que si se hiciese en un red de área local en un contexto de área ancha ATM.

2.1.4 El nivel de adaptación ATM

UNIVERSIDAD NACIONAL DE TRUJILLO 5 ESCUELA DE INFORMATICA
Una celda ATM tiene 53 bytes o 53x8=424 bits. A una velocidad de transmisión de 155 Mbps, 424 bits se transmiten en 424/(155x106)=2.74 microsegundos. Pocas UCP pueden soportar interrupciones de sus adaptadores de red a una tasa de 2.74 µs, lo que impone en el adaptador un mecanismo para ensamblar las celdas entrantes en que se reparte el mensaje de usuario. Este mecanismo es el que trocea el mensaje en las celdas que salen al cable. Estos procedimientos de ensamblado y desensamblado constituyen el nivel de adaptación ATM. Están implementados en el adaptador de red y provocan una interrupción por paquete y no por celda. Una propuesta del nivel de adaptación es SEAL, que significa "Nivel de Adaptación Simple y Eficiente". Por su nombre, fácilmente puede adivinarse lo que pensaron sus creadores de las propuestas anteriores. SEAL utiliza un bit del campo de tipo de datos de la celda. Este bit es normalmente cero, pero es uno en la última celda de un paquete.

2.1.5 Implicaciones de ATM en los sistemas distribuidos
La disponibilidad de líneas de comunicación con anchos de banda de 155 Mbps, 622 Mbps y potencialmente hasta 2.5 Gbps tiene importantes implicaciones en el diseño de sistemas distribuidos, sobre todo en aquellos que abarcan áreas supranacionales, con componentes en distintos países o incluso continentes, los llamados sistemas distribuidos de área ancha. 2.1.5.1 Latencia El problema fundamental en estos sistemas es el inevitable retraso que sufre un impulso eléctrico aplicado en el extremo de un conductor en llegar al otro extremo. Este retraso se denomina latencia. La electricidad se transmite en un cable de cobre a 2/3 de la velocidad de la luz. La luz en fibra óptica se transmite también a una velocidad que es 2/3 de la que alcanza en el vacío. Si disponemos de una línea transcontinental de 3000 Km, el retraso que experimenta una señal es de 3000/200000 = 15 ms. Supongamos que queremos enviar 1 Mb de datos por esa línea a 64 Kb/s y esperar la confirmación. Sólamente bombear a la línea el Mb lleva 1000/64 = 15.6 segundos. El retraso de propagación del impulso eléctrico es de 0.015 * 2 = 0.03 segundos, que no es significativo frente a los 15.6, de modo que la distancia entre máquinas no es un factor que influya en el diseño del sistema global. Consideremos ahora la instalación de adaptadores ATM en ambas máquinas a una velocidad de 622 Mbs. Ahora poner en la línea 1 Mb supone 1/622 = 0.0016 s. Un retardo de propagación de 0.03 segundos, no sólo sí es significativo frente a 0.0016 segundos que se emplea en sacar el fichero a la línea, sino que es 18 veces mayor. Se tarda muchísimo más en que el primer bit llegue al otro extremo que en poner en el cable el megabit entero. Desde que comienza el envío hasta que vuelve la confirmación transcurren 0.0316 segundos, de los cuales la línea está ocupada 0.0016 s y ociosa 0.03 segundos, es decir, el 95% del tiempo. Un aumento de la velocidad de transmisión no provoca provoca más que un menor aprovechamiento de la línea, que se aproxima asintóticamente al 100%. La conclusión es que en la construcción de sistemas de distribuidos de área ancha, la latencia de las comunicaciones lleva a reconsiderar las arquitecturas y los protocolos empleados. 2.1.5.2 Control de flujo

UNIVERSIDAD NACIONAL DE TRUJILLO 6 ESCUELA DE INFORMATICA Dispositivos que se comunican a 622 Mbps también plantean el problema del control de flujo de la comunicación. Supongamos que una máquina envía a otra, a 3000 Km, una cinta de video que ocupa 10 Gb. Seguramente la estación receptora no va a tener disponible un buffer de 10 Gb, de modo que, cuando el espacio se agota, envía una celda a la estación emisora con el mandato STOP. La celda STOP va a tardar un mínimo de 0.015 segundos. En ese intervalo de tiempo, han llegado a la máquina receptora 0.015 s * 622 Mbps = 9.3 Mb, que deben ser descartados. Será necesario introducir un protocolo clásico de ventana deslizante que imponga a la estación emisora el envío de 1 Mb y la espera de una celda de confirmación antes de enviar el segundo Mb. Ahora estamos en la situación antes descrita, en que la ocupación de la línea no supera el 5% de tiempo. La solución a este problema pasa por aumentar el número de Mbits de memoria RAM en los adaptadores de red, lo que supone un alto costo. Otra solución es la negociación de la velocidad de transmisión en bits/s en la fase del establecimiento del circuito virtual. De cualquier modo, el salto de 64Kbps a 622 Mbps no va suponer que las prestaciones de un sistema distribuido se multipliquen por 10.000 y será necesario investigar aún para sacar el mayor partido posible a la tecnología ATM.

2.2 El Modelo Cliente-Servidor
El modelo de referencia OSI es aparentemente una buena herramienta para construir sistemas operativos distribuidos, ya que sus protocolos garantizan que los bits que se envían por la red de comunicación llegan correctamente a su destino. Sin embargo, no es así. Por una parte, hay que considerar que la implantación de los protocolos OSI representa una sobrecarga muy importante en la tarea del transporte de los bits. En líneas de baja capacidad, como 64Kbps, la ejecución de los procesos que implementan los protocolos que tratan un paquete IP, por ejemplo, terminan antes de que llegue el siguiente, de modo de la comunicación no se ve retardada por la sobrecarga OSI. En modelos de comunicación más rápidos, como ATM, los protocolos OSI representan el cuello de botella de la comunicación. Es preciso tener en cuenta que la pila OSI no trata más que una pequeña parte del problema de la comunicación y es que los datos se transmitan entre los extremos finales correctamente, no necesariamente rápidamente. Para construir sistemas distribuidos las prestaciones son fundamentales, de modo que al modelo OSI la hace falta algo más. Este algo más es la arquitectura cliente-servidor.

2.2.1 Clientes y servidores
Un sistema operativo distribuido se puede estructurar mediante un grupo de procesos que cooperan. Unos prestan servicios y se denominan servidores y otros los solicitan y se denominan clientes. Generalmente, en el contexto de un sistema operativo distribuido, clientes y servidores son programas de usuario que corren en máquinas con el mismo microkernel. Así, una máquina puede albergar un sólo cliente o un sólo servidor o bien una mezcla de clientes y de servidores. El modelo cliente-servidor se basa en un protocolo no orientado a conexión de peticiónréplica (Fig 2.4). Consiste en el envío de un mensaje por parte del proceso cliente al proceso servidor. El servidor devuelve los datos requeridos o bien un indicador de error. Nada más. No es necesario establecer ni liberar ninguna conexión. Supongamos que las máquinas son idénticas y se opera en un entorno de red local altamente fiable como ethernet o token-ring. No es necesario el nivel cuatro de OSI porque los datos se transmiten sin error. No es necesaria ninguna función de encaminamiento por que el hardware se

UNIVERSIDAD NACIONAL DE TRUJILLO 7 ESCUELA DE INFORMATICA encarga directamente. Así, no es necesario el nivel tres de OSI. El protocolo peticiónréplica reside en el nivel cinco de la pila OSI, que invoca directamente los servicios del nivel dos.

Fig. 2.4 El modelo de interacción cliente-servidor.

Cuando los procesos que residen en máquinas distintas se comunican a través de una red local, es preciso añadir al microkernel servicios de comunicación, que presentan a los programas de usuario mediante llamadas al sistema. En un modelo de interacción clienteservidor, estas llamadas al sistema pueden ser funciones de biblioteca como send(direccion, &mens) y receive(direccion, &mens). El nivel cinco puede ser implementado en funciones de este tipo que invocan las llamadas al sistema de comunicación de procesos. Estas funciones examinan los parámetros con que el usuario ha invocado send y receive y, finalmente, ejecutan una función de trap al sistema operativo. mens es una estructura de datos en espacio de direccionamiento del proceso de usuario. direccion es el identificador de un punto de acceso al servicio de petición-réplica donde receive espera las peticiones o bien donde send las envía. Los procesos se comunican enviando datos y recibiendo datos en un punto de acceso previamente acordado. A estos puntos de acceso se les denomina comúnmente puertos.

2.2.2 Direccionamiento
Cada punto de acceso al servicio de comunicación cliente-servidor debe tener una dirección que lo identifica en todo el sistema distribuido y lo distingue del resto. El criterio seguido para identificarlos de una forma global a todo el sistema distribuido, es decir, darles una dirección única, es el objeto de esta sección. En toda red de computadores cada máquina tiene una dirección que la identifica, de modo que la primera alternativa es dar al punto de acceso la dirección de la máquina en la red. Si sólo hay un proceso ejecutando en la máquina, cuando un mensaje llega, el kernel se lo envía al único proceso que existe. Ahora bien, si hay más procesos ejecutando, ¿a qué proceso se lo envía? Desde luego, esta no es la solución adecuada. A continuación vamos a examinar tres posibles aproximaciones al problema (Fig. 2.5).

UNIVERSIDAD NACIONAL DE TRUJILLO 8 ESCUELA DE INFORMATICA

Fig 2.5 [1] direccionamiento máquina-puerto. [2] direccionamiento mediante difusión. [3] direccionamiento vía un servidor de nombres.

La solución pasa por direcciones de puntos de acceso que consisten en dos partes. La primera es la dirección de la máquina en la red y la segunda es una dirección particular en cada máquina. Así, la máquina 245 tiene las direcciones 245@0, 245@1, etc. La pila de protocolos TCP/IP usa este método. La primera parte de la dirección se llama dirección IP, e identifica una máquina en la Internet. La segunda parte se llama puerto. Servicios TCP/IP bien conocidos como finger o ftp utilizan siempre el mismo puerto en todas las máquinas. El servidor de FTP, por ejemplo, recibe en el puerto 21, de modo que, conceptualmente, realiza un equivalente a receive(@21, &mens). Un cliente FTP que accede a la máquina de dirección IP 33.33.33.33 conceptualmente invoca send(33.33.33.33@20, &mens). Este modo de direccionamiento, si bien es simple, está lejos de ser ideal por que no es transparente. El cliente debe ser consciente de la máquina en que reside el servicio y la transparencia es una de las metas en el diseño de un sistema operativo distribuido. Una alternativa es numerar los puntos de acceso de forma global y no relativos a cada máquina. Una posibilidad es elegirlos de forma aleatoria en un rango numérico muy grande, por ejemplo el definido por los números enteros de 64 bits. Según este sistema, un proceso cliente no necesita conocer direcciones de máquinas, sino sólo números de puerto de 64 bits. Ahora bien, aunque un proceso cliente conozca la identificación del punto de acceso a un servicio, sin importarle en qué máquina reside el servidor, los protocolos de red sí necesitan conocer la máquina en la que se encuentra, a fin de enviar allí el mensaje. Del identificador aleatorio de 64 bits no se deriva ninguna dirección de red, de modo que se plantea un problema que es necesario resolver. Redes locales como ethernet soportan difusión o "broadcasting", es decir, la posiblidad de que una máquina envíe un paquete al resto de los nodos de la red. Un cliente que conoce la

UNIVERSIDAD NACIONAL DE TRUJILLO 9 ESCUELA DE INFORMATICA
dirección de un servicio en la red local pero no conoce la máquina en la que reside, puede hacer uso de la difusión para encontrarla. Este paquete, denominado paquete de localización, lleva el identificador de 64 bits del punto de acceso y ocasiona una réplica de la máquina que alberga dicho punto de acceso. Esta respuesta lleva su dirección de red. Dicha dirección es registrada por el kernel de la máquina solicitante en una caché a fin de evitar una nueva difusión en un segundo uso del servicio. Como es de apreciar, este esquema es transparente al cliente, que sólo sabe de la identificación del servicio y no de su ubicación. Existe una tercera solución al direccionamiento de los servicios. Consiste en que los clientes accedan a un servicio especificando su nombre, una cadena ascii. Es necesario incorporar a la red un servidor de nombres, que almacena pares (Nombre, XXX@YYY). Con este esquema, un cliente, antes de acceder a un servicio debe realizar una consulta al servidor de nombres. Cada una de las tres soluciones tiene sus inconvenientes. La primera no es transparente, la segunda añade sobrecarga en el sistema y la tercera requiere un componente centralizado, que no escala adecuadamente.

2.2.3 Primitivas con bloqueo y sin bloqueo
Las primitivas send y receive pueden ser diseñadas como bloqueantes o síncronas y no bloqueantes o asíncronas. A veces, ambas opciones, con bloqueo y sin bloqueo, están disponibles para el usuario. Si send es bloqueante, significa que, tras ser invocada por un proceso de usuario, este es suspendido hasta que el mensaje ha sido enviado. Enviado puede significar dos cosas sutilmente distintas y que es preciso aclarar en cada contexto. Unos autores, sobre todo los implicados en construcción de sistemas operativos, entienden por "enviado" como aquel mensaje que ha sido copiado al kernel para su envío o que ha sido puesto directamente en la línea de comunicación. Lo importante, consideran ellos, es que el buffer del mensaje saliente quede libre de nuevo. Lo de menos es que el mensaje haya sido recibido. Otros autores, sobre todos los involucrados en la construcción de lenguajes, argumentan que un mensaje puede considerarse como enviado cuando ha llegado a su destino, es decir, cuando el kernel de la máquina destino ha replicado al kernel de la máquina remitente con un mensaje de reconocimiento. En lo que ambos están de acuerdo es que si la primitiva send retorna cuando se confirma la recepción del mensaje, esta semántica es síncrona. También todos están de acuerdo en que si send retorna antes de que el mensaje haya sido copiado al núcleo o enviado por la línea, estamos ante una primitiva asíncrona. Si receive es bloqueante, el proceso que la invoca es suspendido mientras el mensaje no haya sido recibido. Si receive es no bloqueante, el proceso que la invoca no se suspende en ella. Al cabo de algún tiempo que el proceso considere oportuno, acudirá al buffer a recoger el mensaje solicitado con la esperanza de que haya llegado o bien con la información de que efectivamente así ha ocurrido. La ventaja de las llamadas no bloqueantes reside que el proceso de usuario invocante puede seguir ejecutando concurrentemente con la transmisión del mensaje. La ventaja del aumento de prestaciones de las llamadas no bloqueantes se ve superada, no obstante por una desventaja muy seria: el emisor de send(direccion, &mens) no puede modificar el buffer mens hasta que este haya sido enviado y, lo que es peor, en principio no conoce cuándo el envío se produce, de modo que no puede volver a reutilizarlo para enviar o recibir un nuevo mensaje. A este problema se le pueden aplicar dos soluciones: 1. La primera es que el kernel copie el contenido del mensaje a un buffer interno y desde allí lo envíe. Ahora el proceso de usuario puede reutilizar el buffer y send se comporta como

UNIVERSIDAD NACIONAL DE TRUJILLO 10 ESCUELA DE INFORMATICA
en modo síncrono. La desventaja de esta solución es que el kernel debe copiar el mensaje al adaptador de comunicaciones cuando este esté libre para transmitir. Son necesarias dos copias, una de espacio de usuario al kernel y otra del kernel al dispositivo de E/S. Cuando el mensaje es grande y los envíos numerosos, esta solución puede degradar notablemente las prestaciones del sistema. 2. La segunda solución es interrumpir al emisor cuando el mensaje ha sido enviado. Entonces el proceso entiende que ya puede modificar el buffer. Esta es una opción que, si bien ahorra tiempo, hace muy complicada la programación de aplicaciones de usuario y, debido a la aparición de las condiciones de carrera, hace muy difícil de depurar este tipo de programas. Por lo tanto, las desventajas superan ampliamente a las ventajas. El utilizar primitivas síncronas parece la opción más adecuada. No maximiza el paralelismo, pero es fácil de comprender y de programar. Las mismas consideraciones que hemos hecho sobre send se aplican a receive. En el diseño de las primitivas de comunicación puede considerarse la introducción de plazos. Tras invocar send, el kernel desbloquea el proceso cuando recibe el reconocimiento de que el mensaje ha llegado a su destino. Si este no llega, el proceso quedará bloqueado indefinidamente. Algunos sistemas proporcionan primitivas con un parámetro de temporización. Si el mensaje no ha sido enviado o recibido en el plazo especificado, la llamada vuelve con error.

2.2.4 Primitivas con buffer y sin buffer
Una llamada receive(direccion, &mens) indica al kernel que el próximo mensaje entrante dirigido al punto de acceso direccion se copie en el buffer mens del espacio de direccionamiento del proceso que la invoca. Esta primitiva se conoce como sin buffer porque el kernel no almacena el mensaje entrante antes de entregarlo a la aplicación. Funciona correctamente cuando receive se invoca antes de que llegue el mensaje a la dirección especificada. En contraste, cuando el mensaje llega antes de que se produzca la llamada receive, el kernel no sabe a quién entregar el mensaje y, como no dispone de espacio para almacenarlo, simplemente lo descarta, enviando al kernel del emisor un código de error. En la aplicación que envía, send retorna con error y siempre puede volver a hacer un reintento. Cuando la aplicación que envía considera que el número de reintentos execede un límite, renuncia a seguir, presumiendo que el servidor no está ejecutando, cuando tal vez esté sirviendo un mensaje que simplemente exige más tiempo que el habitual. El problema se palía disponiendo un buffer en el kernel para cada punto de acceso. A este buffer se le suele llamar buzón. Las primitivas con buzones asociados se denominan "con buffer". La solución no es completa, sin embargo. El buzón tiene capacidad finita, de modo que cuando se llena volvemos a la situación de la primitiva sin buffer, donde los mensajes son descartados.

2.2.5 Primitivas fiables y no fiables
Hasta aquí hemos asumido tácitamente que, cuando se envía un mensaje, el kernel del receptor lo recibe. Sin embargo, los mensajes en ocasiones se pierden en las líneas de comunicación. Vamos a considerar tres estrategias ante el problema. La primera es reconsiderar la semántica de send en el programa de usuario como no fiable. Implementar una comunicación fiable se deja en manos del usuario. Al fin y al cabo, el correo convencional funciona así. Una carta se deja en el buzón y el servicio de correos pone todo de su parte, pero no garantiza que la carta va a llegar a su destino. Las primitivas fiables se implementan mediante mensajes de confirmación, que a continuación examinamos. Así, la segunda aproximación se

UNIVERSIDAD NACIONAL DE TRUJILLO 11 ESCUELA DE INFORMATICA
basa en que el kernel de la máquina receptora envíe un mensaje de confirmación al kernel de la máquina emisora. El programa de usuario que invoca send queda suspendido hasta que este mensaje se recibe. La réplica del servidor al cliente también va acompañada de este tipo de confirmación (Fig 2.6, izq.), de modo que el servidor queda bloqueado en send hasta que la confirmación se produce. La tercera vía es sacar partido a que la interacción clienteservidor está diseñada como una réplica del servidor al cliente que siempre sigue a la solicitud de este último. En la figura 2.6, izq. vemos que el servidor envía los mensajes 2 y 3 al cliente, el 2 para confirmar la llegada de la petición y 3 lleva la réplica. Podemos prescindir de la confirmación 2 que modo que ésta venga dada implícitamente por la réplica 3 (Fig. 2.6, der.). El cliente queda ahora suspendido en send hasta que se llegue la réplica del servidor, que se confirma con el mensaje 4.

Fig. 2.6 Izq. Mensajes de usuario individualmente reconocidos.

2.3 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.

2.3.1 La operación RPC básica

UNIVERSIDAD NACIONAL DE TRUJILLO 12 ESCUELA DE INFORMATICA
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 aplicaciones distribuidas se hace más sencilla. La figura 2.7 muestra un ejemplo del uso de RPC. El cliente necesita de un servicio que es la suma de dos enteros. El servicio se invoca mediante a una llamada a función tal y como se hace en un programa convencional escrito en C. La única diferencia es que la función se ejecuta en una máquina diferente de una forma completamente transparente al usuario.

Fig. 2.7 Computando una suma de forma remota.

La función suma(2, 3); de la figura se implementa en el cliente como una rutina de biblioteca que se denomina el cabo o "stub" cliente. Recordemos cómo se implementan las llamadas al sistema en un lenguaje como C en un sistema operativo centralizado. Estas acaban almacenando los parámetros de la llamada en los registros de la UCP y ejecutando una instrucción de trap al sistema operativo. En un sistema distribuido las cosas son básicamente iguales. La función suma contiene una llamada al sistema send seguida de una llamada receive, donde el programa cliente que invoca suma(2, 3); es suspendido hasta que llega la respuesta del servidor, según el modelo de interacción cliente-servidor. Como podemos ver, el mecanismo RPC no es sino el modelo cliente-servidor, pero con una interfaz más sencilla que hace que el cliente invoque al servicio por su nombre y sin necesidad de gestionar números de puerto. En cuanto al servidor, el cabo no es una función como era suma en el cliente, sino que es el programa principal. El programador del servicio sólo se ocupa del procedimiento que invoca el cliente. Las llamadas de receive y send en el servidor son ejecutadas por el cabo, que recibe el mensaje, determina el procedimiento solicitado -suma en este caso- y, finalmente, lo invoca. Todos los pasos implicados son los siguientes:

1. El cliente llama al cabo cliente en la manera convencional. 2. El cabo cliente construye un mensaje empaquetando los parámetros del procedimiento y salta al kernel mediante un trap. 3. El kernel local envía el mensaje al kernel remoto. 4. El kernel remoto entrega el mensaje al cabo servidor. 5. El cabo servidor desempaqueta los parámetros y llama al procedimiento de servicio.

UNIVERSIDAD NACIONAL DE TRUJILLO 13 ESCUELA DE INFORMATICA 6. 7. 8. 9. 10.
El procedimiento de servicio hace el trabajo y devuelve el resultado. El cabo servidor empaqueta el resultado y salta al kernel mediante un trap. El kernel remoto envía el mensaje al kernel local. El kernel local envía el mensaje al cabo del cliente, ahora suspendido esperándolo. El cabo local desempaqueta el resultado y lo devuelve al cliente.

2.3.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. Supongamos que es el primer parámetro de la llamada a procedimiento suma de la Fig. 2.7 y que el servidor ejecuta en una estación SPARC. Los bytes se envían a la red siguiendo su posición en la memoria, de modo que circulará por ella la secuencia de bits del cinco, luego la del cero tres veces. Según llegan los bytes se van almacenando en el buffer del mensaje comenzando por el byte más bajo, de modo que en la memoria de la máquina servidora tenemos llos cuatro bytes almacenados de la misma forma que en la memoria del 486, es decir, 0005, el 5 en el byte más bajo. Sin embargo, para la UCP SPARC, este número no es el cinco, sino 5*2563, casi 84 millones. Por lo tanto, es preciso realizar una convesión en la máquina SPARC antes de entregar los parámetros al procecimiento de servicio. Nótese, no obstante, que si es otra máquina SPARC la que envía el entero 5, no es necesario realizar la conversión. Una forma de solucionar todos estos problemas de representación es convertir los datos a una forma canónica antes de ser enviados a la red. Cuando los parámetros llegan a su destino, por ejemplo, es preciso reconvertir el formato canónico al de la arquitectura del servidor. El problema de este método es que es ineficiente en interacciones entre máquinas de igual arquitectura, de modo que otra aproximación al problema es incorporar en el mensaje un primer byte que identifique la arquitectura del emisor. Si es la misma que la del receptor, no es necesaria conversión alguna. El problema más difícil de tratar en las llamadas a procedimiento remoto es el paso de punteros, ya que estos sólo tienen sentido en un espacio de direccionamiento dado. Un entero pasado a una rutina de servicio tiene un significado pleno, pero un puntero es simplemente una referencia inútil. No obstante, el paso de punteros en las aplicaciones de usuario es tan común que el prescindir de ellos en las llamadas a rutinas remotas haría perder al concepto de RPC gran parte de su atractivo. Afortunadamente, el problema puede abordarse. En el lenguaje C, por ejemplo, están disponibles dos formas de pasar parámetros. Los enteros y caracteres se pasan por valor. Esto significa que, al invocar un procedimiento con un parámetro que es un entero, se hace una copia del entero en la pila. La rutina invocada trabaja con esta copia. La variable original no se toca. Los vectores, sin embargo, se pasan por referencia.

UNIVERSIDAD NACIONAL DE TRUJILLO 14 ESCUELA DE INFORMATICA
Ello significa que no se copia en la pila el vector completo en la llamada, sino sólo su dirección. El programador de la rutina que recibe la referencia es consciente de que trabaja con un puntero al vector original, que es realmente modificado en la rutina. En Pascal, por defecto, todas las variables se pasan por valor. El paso por referencia es controlado por el programador añadiendo el modificador var a la definición del parámetro que se pasa por referencia en la declaración de la rutina. 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 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.3.3 Especificación de interface
El servidor RPC puede ser considerado como un módulo u objeto que implementa unas operaciones sobre datos ocultos. El servidor de 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 realize 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 interface. 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 interface 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

UNIVERSIDAD NACIONAL DE TRUJILLO 15 ESCUELA DE INFORMATICA 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.
#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); } } a) #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)

Fig. 2.8 Ejemplo de un servidor y su definición de interface.

Una de las implementaciones RPC más populares es Sun RPC, desarrollada por Sun Microsystems. Supongamos que deseamos un servidor de tiempo que proporcione dos servicios. Uno, invocado por la función bin_date_1 que devuelve el número de segundos transcurridos desde las cero horas del 1 de enero de 1970. Otro, invocado por la función str_date_2. Esta función toma un parámetro, que es un número de segundos transcurridos desde el instante antes citado, y devuelve la hora y la fecha en una cadena de caracteres. La figura 2.9 describe los pasos implicados en la creación de los procesos servidor y cliente a partir de la especificación de interfaz en el sistema Sun RPC.

UNIVERSIDAD NACIONAL DE TRUJILLO 16 ESCUELA DE INFORMATICA

Fig. 2.9 Sun RPC

2.3.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 proporcionar 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 comprender, 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 denomindado 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

UNIVERSIDAD NACIONAL DE TRUJILLO 17 ESCUELA DE INFORMATICA 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 en la figura 2.9 b), 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 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. Supongamos que un cliente invoca el procedimiento remoto read de la figura 2.8 b). El cabo del cliente percibe que aún no dispone de la dirección del servidor, de manera que envía un mensaje al enlazador solicitando la dirección de un servidor cuyo nombre y versión el cliente proporciona en los campos del mensaje. El enlazador busca en sus tablas tal servidor y, si está registrado, proporciona su dirección al cliente en el mensaje de réplica. La principal ventaja del enlace dinámico es que el servidor puede estar en cualquier máquina. Incluso puede haber varios servidores. En enlazador proporciona al cliente la dirección del servidor menos sobrecargado, más cercano, etc. Una desventaja es el tiempo que lleva el registro del servidor y la solicitud del cliente. Muchos procesos cliente sólo hacen una llamada a procecimiento remoto. Por otra parte el enlazador puede llegar a ser una máquina muy solicitada, hasta el punto de necesitar varios enlazadores replicados. Mantener la consistencia de los mismos conlleva más tráfico en el sistema.

2.3.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 correcatamente, ya que las diferencias entre llamadas locales y remotas no son fáciles de enmascarar. Vamos a considerar cinco situaciones de fallo: 1. 2. 3. 4. 5. El cliente no es capaz de localizar al servidor. El mensaje del cliente al servidor se pierde. El mensaje de réplica de servidor a cliente se pierde. El servidor se cae tras recibir el mensaje del cliente. 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 de la figura 2.7. Otro intento de solución es escirbir 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.

UNIVERSIDAD NACIONAL DE TRUJILLO 18 ESCUELA DE INFORMATICA 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). 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, examinada 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

UNIVERSIDAD NACIONAL DE TRUJILLO 19 ESCUELA DE INFORMATICA peor del caso b) es que ni cliente ni servidor sabrán nunca si la operación se realizó con anterioridad o no.

Fig. 2.10 Caída del servidor. a) Operación correcta. b) Caída tras servir el mensaje c) Caída antes de servir el mensaje.

La discusión anterior viene a decir, en esencia, que la ejecución de un procedimiento remoto está envuelta en un factor de incertidumbre que hay que asumir a la hora de programar una aplicación distribuida. Para reducir este factor aleatorio, los implementadores de un sistema RPC toman una de estas tres aproximaciones en cuanto a la semántica de la llamada RPC. La primera se denomina semántica de "al menos una vez". Esta filosofía de implementación de un software RPC garantiza a la aplicación cliente que el procedimiento remoto va a ejecutarse al menos una vez, pero posiblemente más. Mientras el servidor de figura 2.10 está caído, el cliente no cesa de enviarle nuevas solicitudes de impresión. Llega un momento en que el servidor rearranca y su decisión es ejecutar la petición. Ante el caso b) de la figura 2.10, el servidor está repitiendo la operación. En el caso c) no la repite. De cualquier forma, este mecanismo garantiza la impresión al menos una vez. La segunda aproximación se denomina semántica de "como máximo una vez". A la aplicación cliente no se le garantiza que la llamada se ejecute. Tras el rearranque, el servidor RPC puede tomar la decisión de ignorar las peticiones entrantes y de enviar un mensaje de aviso al cliente informándole de la caída, para que este tome las medidas oportunas. La llamada devolverá un código de error parecido a "rearranque de servidor". El cliente tendrá garantizado que el procedimiento no se ha ejecutado más de una vez. Lo que nunca sabrá es si se ejecutó alguna vez. Otra aproximación que puede tomar el cliente, si la caída se prolonga, es renunciar tras un número de reintentos determinado. También en este caso, desconoce si el procedimiento se ejecutó. La tercera filosofía que puede adoptar el software RPC es no garantizar nada a la aplicación de usuario. Si el servidor se cae, tras el rearranque, informa de la caída y sigue ejecutando peticiones como si nada hubiese ocurrido. El procedimiento puede no haberse ejecutado o puede haberse ejecutado una o más veces. La ventaja de esta semántica es que es fácil de implementar. El cliente se cae Puede ocurrir que un cliente haya invocado una llamada RPC y, antes de que la respuesta llegue, el cliente se cae. La operación que se realiza en el servidor no se hace ahora en beneficio de nadie y se dice que ha quedado huérfana. Las operaciones huérfanas son un problema. Cuando el cliente rearranca, puede eliminar las operaciones iniciadas mediante un mensaje especial. Esta solución se denomina exterminación de huérfanos. Esta solución, sin embargo, es problematica porque puede tener consecuencias impredecibles. Por ejemplo, un huérfano puede haber bloqueado uno o más ficheros o registros en una base de datos. Si la operación se aborta, quedarán bloqueados para siempre. Por otra, parte, una operación RPC puede haber solicitado servicios remotos en otras máquinas que habría que abortar igualmente. Desde

UNIVERSIDAD NACIONAL DE TRUJILLO 20 ESCUELA DE INFORMATICA luego, una de las soluciones, tal vez la mejor, es esperar a que terminen los huérfanos aunque ellos represente una carga inútil de ciclos de UCP.

2.3.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 abstratos. 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. El otro protocolo de transporte de la pila TCP/IP es UDP, no orientado a conexión. UDP no es fiable. No usa reconocimientos para hacer saber al proceso emisor la llegada de un paquete. No garantiza la estricta secuencia de los paquetes en el receptor, ya que cada uno de ellos circula por una ruta que no tiene por qué ser la misma. Los paquetes entrantes no son ordenados, sino entregados a la aplicación según llegan. Tampoco dispone de control de flujo, de modo que una máquina rápida puede desbordar a una más lenta o más cargada, con la consecuente pérdida de paquetes. En resumen, si se usa UDP como protocolo de transporte, los paquetes pueden ser perdidos y, si llegan, hacerlo desordenadamente. Una aplicación de usuario que use UDP asume toda la responsabilidad de implementar la fiabilidad de la comunicación. UDP es, sin embargo, mucho más rápido que TCP. Se usa en redes altamente fiables como las redes locales. En ellas prácticamente no existen errores de comunicación que corrompan paquetes, de modo que la sobrecarga que TCP representa no está justificada y el programador de una aplicación de red puede entender perfectamente UDP como un protocolo fiable. En entornos de área ancha, sin embargo, el uso de TCP es imperativo.

UNIVERSIDAD NACIONAL DE TRUJILLO 21 ESCUELA DE INFORMATICA

Fig. 2.11 Contexto del software de Sun RPC.

A la hora de construir un software RPC, hay que considerar varias cuestiones. Una de ellas es el un protocolo orientado a conexión es que la comunicación es mucho más fácil de implementar. Se envía un mensaje y tenemos la seguridad plena de que ha llegado a su destino. No hay que preocuparse por reconocimientos ni paquetes desordenados. En suma, el nivel de transporte ya está soportado por el protocolo de comunicación utilizado. Como alternativa, puede pensarse en utilizar protocolo de comunicación que utilizan las funciones de la biblioteca RPC. La primera decisión que tomar es la elección de un protocolo orientado a conexión o no orientado a conexión. La ventaja de un protocolo menos sofisticado como UDP en un entorno fiable como una red local. Una segunda cuestión a ponderar es el uso de un protocolo de comunicación de propósito general o bien diseñar un protocolo específico que se adapte mejor al paradigma RPC. Sun RPC utiliza la primera opción, construido sobre la pila TCP/IP. Las ventajas de usar un protocolo estándar como TCP/IP son evidentes, ya que 1. 2. El protocolo no hay que diseñarlo ni implementarlo. Está disponible en casi todos los sistemas UNIX.

El inconveniente de implementar RPC sobre un protocolo de comunicación estándar como TCP o UDP es la pérdida de prestaciones. Por ejemplo, uno de los campos de la cabecera de un paquete IP es el "checksum" de los campos de la cabecera, que es costoso de computar. La alternativa al protocolo estándar es un protocolo específicamente diseñado para soportar RPC. La desventaja es el tiempo de concepción y desarrollo de este protocolo y la integración en el sistema operativo. La ventaja es un aumento considerable en la velocidad de las comunicaciones, un factor clave en sistemas operativos distribuidos. Una tercera cuestión a considerar en el diseño del software RPC es la longitud permitida de los mensajes RPC. Supongamos una llamada RPC read para leer un fichero de 64 Kbytes. Una llamada RPC conlleva una fuerte carga de comunicación por sí misma, con independencia de la cantidad de datos implicados, de modo que conviene realizar la lectura en una sóla llamada. Para ello necesitamos mensajes suficientemente grandes. Sun RPC tiene un límite de mensaje limitado a 8 Kbytes. También hay que considerar la longitud de los paquetes de los protocolos subyacentes. Por ejemplo, los paquetes Ethernet están limitados a 1536 bytes, de manera que una llamada RPC a menudo debe ser partida en muchos marcos ethernet, aumentando así la sobrecarga de comunicación. Reconocimientos En la sección anterior discutimos la posibilidad de implementar RPC sobre un protocolo no fiable como UDP o incluso IP. La fiabilidad es entonces responsabilidad del protocolo RPC y debe ser implementada mediante reconocimientos. Una llamada RPC puede ser la escritura de un bloque de 4 Kbytes. Seguramente, la llamada RPC debe ser partida por el protocolo de

UNIVERSIDAD NACIONAL DE TRUJILLO 22 ESCUELA DE INFORMATICA
transporte RPC en varios paquetes de nivel inferior, supongamos cuatro paquetes como indica la figura 2.12. La estrategia de la figura 2.12b es que el servidor envíe un paquete de nivel inferior con un código de reconocimiento para cada paquete de nivel inferior recibido. Ya que la llamada RPC necesita cuatro paquetes, son precisos cuatro paquetes de reconocimiento. Esta estrategia se denomina protocolo de parada y espera. La alternativa es esperar a que llegue el último paquete de la llamada RPC y entonces el servidor envía un paquete de reconocimiento al cliente. Esta estrategia se denomina protocolo de ráfaga. Con parada y espera, si un paquete se pierde, el temporizador establecido para el paquete por el cliente agota su plazo y el cliente retransmite el paquete. Con el protocolo de ráfaga, supongamos que se pierde el mensaje 1 y el dos y el tres llegan correctamente. El servidor tiene dos alternativas. Una es no enviar el reconocimiento de la llamada RPC. El temporizador del cliente para la llamada agota el plazo y el cliente retransmite los cuatro paquetes de la llamada. La otra es lo que se llama la repetición selectiva. El servidor envía un paquete de reconocimiento de los paquetes 0, 2 y 3 y solicitud de reenvío del paquete 1. La repetición selectiva requiere más gestión, pero usa mejor el ancho de banda de la red. Ya que la pérdida de paquetes en redes locales raramente ocurre, conviene evitar el uso de la repetición selectiva. En redes de área ancha la repetición selectiva es una opción conveniente.

Fig. 2.12 a) Un mensaje de 4 Kbytes. b) Protocolo de parada y espera. c) Protocolo blast.

Un problema incluso más importante en el diseño de protocolos de red y transporte es el de control de flujo. Cuando llega un paquete a un adaptador de Ethernet, por ejemplo, este eleva una interrupción a la UCP para que el manejador copie el paquete del adaptador a la memoria principal. Algunos de estos adaptadores son desabilitados mientras el paquete se lee, de modo que si un segundo paquete llega en medio de la lectura, parte del mismo se pierde. En general, cuando los paquetes llegan más aprisa de lo que pueden ser atendidos, parte de los mismos se pierden, produciéndose un error denominado de "overrun" o sobreflujo. El sobreflujo es un problema más serio que los paquetes corrompidos por el ruido en las líneas. Volvamos a la figura 2.12. Con un protocolo de parada y espera el error de sobreflujo no puede producirse, porque el segundo paquete no se envía hasta de que no se ha reconocido el primero. Sí puede producirse sobreflujo en un servidor con un número alto de clientes, no obstante. Con el protocolo de ráfaga, mucho más eficiente que el de parada y espera, tenemos el problema del sobreflujo. La solución es que el cliente no envíe el segundo paquete hasta después de un tiempo suficiente como para que termine el trabajo de la rutina de interrupción y el adaptador vuelva a aceptar paquetes. Si el retraso necesario para enviar el segundo paquete es corto, el cliente puede realizar espera activa. Si es largo puede establecer una alarma.

UNIVERSIDAD NACIONAL DE TRUJILLO 23 ESCUELA DE INFORMATICA
Un protocolo de transporte de propósito general no tiene por qué tener conocimiento del tipo de adaptador de red existente en la máquina. Al fin y al cabo, este es un problema del nivel de enlace. Si el protocolo de nivel de enlace es Ethernet, no fiable, la fiabilidad se deja en manos del protocolo de red. Si este protocolo, como IP, tampoco es fiable, la fiabilidad descansa en el nivel de transporte. Si el protocolo de transporte es UDP, no fiable, es la aplicación de usuario -por ejemplo, el cabo RPC- el garante de la fiablilidad de la llamada RPC, a través de reconocimientos. Otra posibilidad es que el software RPC implemente el nivel de transporte, descansando directamente sobre el nivel de red. Esta segunda posibilidad permite la optimización del protocolo de transporte por dos razones. La primera es el conocimiento de que está sirviendo únicamente a llamadas RPC, lo que puede ahorrar reconocimientos debido al modelo de interacción cliente servidor. La segunda razón es que el transporte RPC puede ser codificado con conocimiento del tipo de adaptador hardware de la red a la que pertenecen clientes y servidores, evitando desde un principio el problema del sobreflujo, por ejemplo. Copias La copia de datos entre espacios de direccionamiento es el cuello de botella en las prestaciones de un software RPC. En el mejor de los casos, cuando un mensaje llega al adaptador de red, este puede copiarlo a un buffer del driver del adaptador en el kernel. El kernel lo copia a continuación al espacio del proceso de usuario en una estructura de datos del cabo.

2.3.7 Problemas abiertos
La llamada a procedimiento remoto es una primitiva de comunicación más abstracta y por tanto más potente que el paso de un mensaje. Ello hace de RPC una herramienta más valiosa que el paso de mensaje en los complejos problemas que plantea la escritura de un sistema operativo distribuido. No obstante, algunos problemas no han sido aún resueltos por las actuales implementaciones de RPC. Una de las metas de una llamada RPC es que sea completamente transparente al programador. Supongamos una aplicación que corre en una única UCP que queremos distribuir haciendo uso de llamadas a procedimientos remotos. La total transparencia de RPC supone:

1. La distribución de la aplicación o el sistema operativo no puede conllevar la prohibición de ciertas construcciones en las llamadas a procedimiento remoto que son legales en las llamadas a procedimieto local. 2. Tampoco debe requerir de construcciones que antes eran opcionales.
Uno de los problemas es el de las variables globales. Una de ellas es errno, para el lenguaje C. El servidor RPC es un proceso en la máquina remota. El cliente es un proceso en la máquina local. Esto significa que la variable errno es diferente en ambos procesos. Por definición, esta variable contiene un código de error correspondiente a la última llamada al sistema fallida. Si un procedimiento local realiza una llamada al sistema vuelve con error, la variable errno tiene el código del error y está disponible a todos los procedimientos del proceso. Falso. A todos los procedimientos del proceso, pero siempre que sean los locales. Los procedimientos remotos ven otra variable errno, la del servidor, que nada tiene que ver con el errno local. La solución de una aplicación distribuida es prohibir el acceso a la variable errno, lo cual viola el segundo principio. Otro problema es el paso de punteros. Es fácil pasar un arbol a un procedimiento local, pero cómo se pasa un árbol a un procedimiento remoto? No se puede pasar el puntero a la estructura padre del arbol, ya que no tiene ningún sentido en la máquina remota.

UNIVERSIDAD NACIONAL DE TRUJILLO 24 ESCUELA DE INFORMATICA
También la función C printf plantea problemas porque tiene un número de parámetros variable. La especificación del lenguaje C no pone límites al número de parámetros de printf. Si nuestra intención es implementar printf como uno de los procedimientos de un servidor RPC, es preciso empaquetar sus parámetros en el cabo antes de enviarlos. El problema es que este es de longitud variable, lo que viola la definición de interfaz del servidor, con declaraciones de rutinas de número fijo de parámetros. La solución es no hacer uso de printf remota. Pero esto viola la deseada transparencia de RPC. Hasta aquí hemos tratado algunos de los problemas que hay que superar para dotar de transparencia a RPC. Sin embargo, existen problemas mucho más serios, ya que cuestionan la utilidad misma del paradigma RPC. Considerar un sistema microkernel en el que el servidor de ficheros, escrito como servidor según el paradigma de la figura 2.9, ha sido dispuesto en otra máquina como un servidor RPC. De esta forma, todos los procesos locales actúan como clientes del servidor de ficheros. Consideremos el mandato UNIX:
# sort <f1 >f2

sort ordena su entrada estándar y el resultado se envía a la salida estándar, de modo que puede ser programado como un cliente RPC. Para leer la entrada estándar realiza una llamada RPC read y para escribir en la salida estándar realiza una llamada RPC write. El modelo cliente-servidor encaja perfertamente en la implementación distribuida del sistema de ficheros. Lo mismo ocurre con el mandato
# grep qsd <f3 >f4

que busca el patrón qsd en el fichero f3 y escribe en f4 las líneas que lo contienen. Consideremos un sistema operativo distribuido en que los procesos se ejecutan en máquinas diferentes. Supongamos que grep y sort han sido asignadas a máquinas distinas y el usuario del sistema emite el mandato
# grep qsd <f5 | sort >f6

De alguna manera, el sistema operativo debe implementarlo. Consideremos el paradigma cliente- servidor. El cliente se considera el elemento activo, por que de él parten las iniciativas de solicitud de servicio. El servidor se comporta como elemento pasivo, ya que no actúa a menos que reciba un estímulo de solicitud de servicio. Según lo hasta ahora establecido, tenemos un proceso actuando como servidor que es el servidor de ficheros (pasivo) y dos procesos actuando como clientes que son grep y sort (activos) por que así han sido programados. Veamos si esta implementación sirve en esta ocasión. Si grep actúa como cliente, puede solicitar una petición de escritura a sort para enviarle la salida de grep. Sin embargo, sort debería estar configurado como servidor (pasivo) a fin de recoger la salida de grep cuando grep lo solicite. Sin embargo, hemos decidido escribir sort como un cliente (activo) del sistema de ficheros. Bien sort, bien grep deberían haberse codificado como servidor en lugar de como cliente. El problema es que esta opción, que sirve para un mandato dado, es catastrófica para otro mandato. En general, el modelo cliente servidor no es apropiado en estado puro en el problema de la tubería
# p1 <f3 | p2 | p3 >f2

Son necesarias soluciones más elaboradas. Por ejemplo, la tubería misma puede ser implementada como un proceso servidor que atiende peticiones de lectura y de escritura. Ahora p1, p2 y p3 pueden ser escritos como clientes de las tuberías y del servidor de ficheros. Ni

UNIVERSIDAD NACIONAL DE TRUJILLO 25 ESCUELA DE INFORMATICA
que decir tiene que esta solución representa una complejidad añadida al sistema operativo. Una solución más sencilla es implementar las pipelines como ficheros temporales en el servidor de ficheros./**/

2.4 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ánemante 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.4.1 Introducción a la comunicación de grupos
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.4.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.4.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í.

UNIVERSIDAD NACIONAL DE TRUJILLO 26 ESCUELA DE INFORMATICA
Procesos que no pertenencen 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 procesamiento en paralelo, donde los procesos tienen su propio cometido y no interactúan con el mundo exterior. Los grupos abiertos son apropiados para poblemas como el de los servidores replicados, 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.4.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 inconvenietes. 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 dedidir quién sirve una petición. Los grupos jerárquicos tienen las propiedades opuestas.

2.4.2.3 Pertenencia a grupos
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.4.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.4.2.5 Primitivas send y receive
No es conveniente que los procesos de usuario puedan utilizar las primitivas send y receive por separarado. 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 constuirse 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.

UNIVERSIDAD NACIONAL DE TRUJILLO 27 ESCUELA DE INFORMATICA
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í unificamos 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.4.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 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.4.2.7 Ordenación de mensajes
Para que la comunicación en grupo sea fácil de usar y comprender requiere de dos propiedades. 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

UNIVERSIDAD NACIONAL DE TRUJILLO 28 ESCUELA DE INFORMATICA
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 como indica la figura 2.13.

Fig. 2.13 Violación del principio de ordenación temporal global en una red sin difusión. Los mensajes enviados se entrelazan.

Existen algunas versiones más relajadas del orden temporal global. Una de ellas es el orden temporal consistente. Si los procesos 0 y 4 toman la decisión de enviar los mensajes al grupo de forma independiente de forma casi instantánea, tal vez no haya forma de saber quien fue el primero en enviar. Tampoco importa, de manera que el sistema elige uno como el primero. A partir de aquí se exige el cumplimiento del principio de orden temporal global.

2.4.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.

UNIVERSIDAD NACIONAL DE TRUJILLO 29 ESCUELA DE INFORMATICA
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. 2.4.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 continentes? Para ellos es preciso sobrepasar los estrechos límites de la red local e introducir encaminadores 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 interred 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. Consideremos la red de la figura 2.15, en el que existe un grupo de procesos distribuido en las cuatro redes. Para implementar el envío del mensaje al grupo podemos hacer uso del multicasting de ethernet en la red del emisor y el encaminador se encarga de propagar el multicasting en la red adyacente. El problema es que la propagación se realimenta sin final. Este es un ejemplo que muestra los problemas añadidos que conlleva la comunicación en grupo de los sistemas distribuidos. Desde luego el simple algoritmo de propagar el paquete de multicasting es insuficiente.

Fig. 2.15 La difusión causa problemas en una red de redes.

El otro problema es que el uso de una red con multicasting o difusión asegura la propiedad de orden temporal. Con el uso de encaminadores, esta condición ya no es suficiente, lo que puede destruir una característica muy útil de un sistema distribuido.

2.4.3 Comunicación de grupo en ISIS

UNIVERSIDAD NACIONAL DE TRUJILLO 30 ESCUELA DE INFORMATICA
ISIS no es un sistema operativo distribuido, sino un conjunto de programas sobre UNIX. Es interesante por su protocolo de comunicación de grupo, que ha sido empleado en numerosas aplicaciones reales. El concepto principal en ISIS es el de sincronía. Un término que se usa en ISIS es evento. En ISIS la transmisión de un mensaje a todo el grupo de procesos por parte de un proceso dado se denomina un evento. ISIS distingue varias formas de sincronía. Un sistema es síncrono cuando todos los eventos se producen de forma estrictamente secuencial -sin simultaneidad posible- y los eventos duran un tiempo nulo. La figura 2.16 a) muestra un grupo de cuatro procesos en los que se producen dos eventos. Uno es el envío del mensaje M1 y otro es el envío del mensaje M2, ambos de duración nula y no simultáneos. Por supuesto, los sistemas síncronos no existen en la realidad, de modo que un modelo más realista es el sistema débilmente síncrono. Se caracteriza por que, primero, los eventos duran un determinado tiempo y, segundo, la duración de dos eventos no puede solaparse en el tiempo. Es el caso de la figura 2.16 b), en la que el evento M2 debe producirse necesariamente después de la terminación del evento M1. Un sistema débilmente síncrono tiene la propiedad del orden temporal consistente, ya que todos los mensajes aparecen en los destinatarios según el orden en el que fueron generados -o, al menos en el orden de generación asignado-. Así, en la figura 2.16 b) vemos que el mensaje M1 se generó antes y todos los procesos lo reciben antes que el mensaje M2, que se generó después. Estos sistemas se pueden construir sobre una red con difusión (de la que realmente son un modelo), como podemos apreciar en la figura 2.16b.

Fig. 2.16 a) Sistema síncrono. b) Sistema débilmente síncrono. c) Sistema virtualmente síncrono En ocasiones, una comunicación de grupo con la propiedad de ser débilmente síncrona puede ser demasiado exigente. Podemos relajar la semántica de una primitiva de envío de un mensaje al grupo a fin de que gane en prestaciones en cuanto a velocidad de ejecución. Antes de seguir, introduzcamos dos conceptos nuevos. En un sistema distribuido dos procesos de un grupo pueden tomar por su propia iniciativa la decisión de enviar un mensaje al resto del grupo. Estos dos eventos, que no están relacionados entre sí por ninguna forma de relación causa-efecto son denominados eventos concurrentes. Por el contrario, un evento en un proceso puede provocar la generación de un segundo evento en otro proceso. Estos eventos de denominan causalmente relacionados. Así, si un proceso P1 envía un mensaje al proceso P2 y, a continuación, P2 envía un mensaje a P3, ambos eventos están causalmente relacionados, ya que la emisión del segundo mensaje por parte de P2 pudo ser fruto de la recepción del mensaje enviado por P1. Puede que esta no fuera la causa en absoluto, pero esto es irrelevante. Lo importante es que pudo ser la causa. Conocidos estos dos tipos de relación entre eventos, introduzcamos el concepto de sistema virtualmente concurrente. En este sistema, si dos even-

UNIVERSIDAD NACIONAL DE TRUJILLO 31 ESCUELA DE INFORMATICA
tos están causalmente relacionados, todos los procesos del grupo deben recibirlos en orden. Primero el evento causa y después el evento efecto. Ahora bien, si ambos eventos son concurrentes, no se garantiza el orden en la recepción de mensajes. En otras palabras, el orden temporal consistente de recepción de mensajes sólo rige en eventos causalmente relacionados. Se dice que los sistemas débilmente síncronos cumplen el orden temporal causal. La primitiva de comunicación cbcast Las aplicaciones ISIS pueden invocar a una rutina de biblioteca denominca cbcast (primitiva de difusión tipo C) para enviar un mensaje al resto del grupo, que garantiza el orden temporal en la recepción siempre que los eventos estén causalmente relacionados. La primitiva está diseñada como sigue. Supongamos un grupo de n procesos. Existe una estructura de datos distribuida de n componentes. Cada uno de ellos reside en uno de los procesos del grupo. Cada componente en el proceso es un vector de n enteros. Si un proceso ha recibido x mensajes del proceso i, el i-ésimo entero es x. Por supuesto, ningún proceso se envía mensajes a sí mismo, de modo que la casilla i del proceso i significa otra cosa. Es el número de mensajes enviados al grupo. Si el grupo tiene 6 elementos y ha hecho 2 envíos al grupo, la casilla i contendrá 2. La gestión de los vectores la realiza la biblioteca ISIS. Los procesos se limitan a invocar la primitiva cbcast para enviar un mensaje al grupo. Conviene darse cuenta de que cuando el grupo está en reposo, es decir, un envío al grupo ha concluido y aún no se ha iniciado otro envío, en todos los procesos el vector tiene el mismo valor. No es así mientras se realiza el envío. Para garantizar el orden temporal causal, cbcast actúa como sigue. Supongamos un grupo de procesos. Un proceso P1 toma la iniciativa de realizar un envío al grupo. Supongamos que en un instante dado el mensaje ha llegado a la mitad del grupo. Un proceso P2 en la otra mitad, que aún no sabe que un envío al grupo está en progreso, también decide hacer un envío al grupo. Entre los dos envíos no hay relación de causalidad. Los procesos de la mitad que aún no han recibido ningún mensaje recibirán ambos mensajes con un orden incierto que cbcast no puede evitar, ya que no ha sido diseñada para ello. Supongamos ahora que uno de los procesos de la mitad que sí ha recibido el mensaje de P1, digamos P3, decide enviar un mensaje al grupo. Entre P1 y P3 sí hay relación de causalidad, ya que es posible que el mensaje recibido de P1 moviera a P3 a enviar al grupo su mensaje. La propiedad de orden temporal causal proporcionado por cbcast impide que ningún proceso del grupo reciba el mensaje de P3 antes que el de P1. Para ello, cbcast decide suspender a P3 cuando intenta enviar a un proceso que aún no ha recibido de P1. Veamos el ejemplo de la figura 2.17. Representa un grupo de tres procesos, A, B y C, recién arrancados, de forma que aún no se ha producido ningún envío al grupo. A toma la iniciativa de enviar al grupo el mensaje M1, de modo que incrementa su componente del vector que gestiona, indicando que incrementa su cómputo global de envíos al grupo. El mensaje es enviado a B y a C. La transmisión a C se retrasa y el proceso B, que no sabe que C aún no ha recibido, se dispone a enviar un mensaje M2 al grupo. El mensaje M2 llega a C antes que el mensaje M1. Sin embargo ISIS debe descubrir que M2 tiene como causa a M1 y, como C aún no ha recibido M1, no entrega M2 a C hasta que no reciba M1.

UNIVERSIDAD NACIONAL DE TRUJILLO 32 ESCUELA DE INFORMATICA

Fig. 12.17 Implementación de orden de entrega en caso de causalidad en ISIS.

¿Cómo descubre ISIS la relación causa efecto? El algoritmo es el siguiente. Sea MENSi el componente i-ésimo del mensaje entrante y sea VECi el componente i-ésimo del vector almacenado en la memoria del receptor. Supongamos que el emisor del mensaje al grupo fuese el proceso j. Para que el mensaje sea aceptado, tienen que darse dos condiciones. Primera, MENSj = VECj + 1. Significa que el proceso que está recibiendo el mensaje de j ha recibido todos los mensajes previos del proceso j, sin perder ninguno. Segunda, que MENSi ≤ VECi para todo i≠j. Significa que todos los mensajes que han sido recibidos por el emisor j también deben haber sido ya recibidos por el destinatario.

2.5 Bibliografía
[Cou95] Coulouris A. et al., Distributed Systems, 2nd. Edition. Addison-Wesley, 1995. Tanenbaum, A., Distributed Operating Systems, Prentice-Hall, 1995.

[Tan95]

You're Reading a Free Preview

Descarga
scribd
/*********** DO NOT ALTER ANYTHING BELOW THIS LINE ! ************/ var s_code=s.t();if(s_code)document.write(s_code)//-->