Un sistema operativo distribuido es el encargado de cumplir lo anterior. Hay un particionado y cooperación entre todos los componentes, ninguno sobrevive solo, el propio kernel está distribuido por las máquinas. El hardware no da ninguna facilidad, es el software el que determina que un sistema sea distribuido o no. El usuario no sabe dónde se están ejecutando los procesos ni dónde están los recursos. Llevar a la práctica la idea de un kernel global distribuido es muy difícil, pues podría haber inconsistencias de datos en el kernel, además se necesita al menos el kernel mínimo para enviar y recibir información y un mecanismo robusto de comunicación y eficiente para evitar latencias demasiado elevadas.
Lo que se han desarrollado hasta ahora son los llamados sistemas operativos en red. En estos sistemas cada máquina tiene su propio kernel. Los sistemas están conectados por una red y se puede hacer remote login en ellos, en todo momento el usuario sabe donde se encuentran todos los recursos (ficheros, procesos, etc.). No es un sistema distribuido por lo que no tiene las ventajas que se verán en el siguiente apartado.
Actualmente se está caminando desde los sistemas operativos en red a los sistemas distribuidos, aunque aún no se han cumplido los objetivos de un sistema distribuido completamente tenemos ya algunos avances. Por ejemplo ya hay grandes avances en sistemas de ficheros para conseguir que exista un solo directorio raíz al igual que la existencia de una ubicación automática por parte del sistema de los ficheros. Se puede implementar un balanceo de la capacidad y redundancia en los datos para minimizar el impacto de posibles caídas de nodos.
Un cluster openMosix ya implementa una migración de procesos, que a ojos del usuario es transparente. El cluster se usa como un gran computador donde se ejecutan los procesos en todos sus nodos. La ubicación de los procesos la elige el sistema operativo o el usuario, en un intento de balancear la carga.
Normalmente se apuesta por la replicación de información. Si tenemos 3 servidores de ficheros, sirviendo los mismos ficheros, si uno de ellos cae podemos seguir obteniendo el servicio de alguno de los otros servidores (en el peor de los casis el servicio quizás se ralentice).
Este caso además ilustra otro problema que es la necesidad de actualizar todas las réplicas de un servicio: si un nodo escribe en uno de los servidores éste debe mandar los nuevos datos a los otros servidores para mantenerlos todos coherentes. De otro modo al caer uno de estos servidores perderíamos toda la información que hubiéramos ido grabando en este servidor y no en el resto.
También se tiene que disponer de los mecanismos adecuados para que el nodo que ve el fallo del servidor busque los servidores alternativos en busca de la información que necesita. Además también se debe disponer de los mecanismos necesarios para que los nodos que han caído, cuando vuelvan a conectarse al cluster puedan continuar con su trabajo normalmente.
Una solución para este problema son los clusters jerárquicos propuestos por Stephen Tweedie: clusters divididos en niveles. Un cluster como lo conocemos es un cluster de primer nivel, los nodos forman una lista de miembros. Hay un nodo privilegiado llamado líder. Para hacer clústeres más grandes se juntan todos los líderes en un metacluster. Se crean entonces dos listas, una de ellas contiene todos los nodos llamada subcluster membership y la otra contiene únicamente los líderes llamada metacluster membership. Si se cambia de líder se cambia también en el metacluster. Así podemos agrupar líderes de metaclusters en un cluster de tercer nivel, etc.
Esta disposición evita transiciones de todo el cluster, pues si hace falta añadir un nodo y crear una transición en el cluster sólo involucramos a los nodos de esa rama. Cuando el cluster necesite recuperación habrá nodos que participen y otros que no.
Entre los problemas que nos encontramos en los sistemas de sabor UNIX se encuentran por ejemplo cómo manejar los directorios /proc y /dev. Hacer un solo directorio con ellos significaría en /proc tener PIDs independientes para cada nodo, algo que no es tan complicado de conseguir y que tiene beneficios como que cada nodo sabe a ciencia cierta y sin ningún retardo cual es el siguiente PID que tiene que asignar.
Pero además incluye otra información como /proc/cpuinfo, /proc/meminfo, etc. que es específica del nodo y que o se modifican esos ficheros para poner la información de todos los nodos en ellos (lo cual rompería la compatibilidad con algunas aplicaciones de usuario) o poner la información en directorios distintos que tuvieran como nombre el número del nodo (lo que traería la duda de qué información colocar en /proc). Aún peor es el caso de /dev pues aquí están los dispositivos de cada nodo. Cuando los dispositivos de un nodo son totalmente distintos a los de otro no hay ningún problema (incluso poder acceder al dispositivo de otro nodo por este fichero sería perfecto) pero si todos los nodos tuvieran por ejemplo un disco duro IDE conectado en el primer slot como maestro con 3 particiones, tendríamos repetidos en cada nodo hda1, hda2 y hda3; por lo tanto tendremos que tener un nuevo esquema para nombrar los dispositivos.
Implica tener que mantener el viejo sistema para el nuevo cluster, por ejemplo mantener un árbol de directorios usual para manejar todos los dispositivos de almacenamiento del cluster. No tenemos que romper las APIs para introducir las nuevas funcionalidades.
A nivel más bajo tenemos que implantar una forma de conocer donde se encuentran los recursos, tradicionalmente se han usado servidores centralizados que lo sabían todo, ahora ya se va consiguie ndo que esta información se distribuya por la red.
Esto que no es excesivamente complicado en un sistema local, se ha convertido en un quebradero de cabeza en un sistema distribuido. Se han desarrollado varios métodos para conseguirlo. El mayor problema es la desincronización de los relojes pues es muy complejo que todos los relojes hardware lleven exactamente la misma temporización por tanto algunos ordenadores ven los acontecimientos como futuros o pasados respecto a otros ordenadores. Este tema se tratará con más profundidad en el capítulo de sistemas operativos.
Básicamente el problema es que el sistema sepa que esas réplicas están ahíy mantenerlas coherentes y sincronizadas. También tiene que activar los mecanismos necesarios cuando ocurra un error en un nodo.
Aunque haya fallos el sistema seguirá funcionando. Las aplicaciones y los usuarios no sabrán nada de estos fallos o intentarán ser mínimamente afectados, como mucho, el sistema funcionará más lentamente. Este punto está muy relacionado con la transparencia de replicación.
Tenemos que solucionar problemas sobre las decisione s que tomamos para migrar un proceso, hay que tener en cuenta las políticas de migración, ubicación, etc. Además tenemos que ver otros aspectos prácticos como si al nodo que vamos encontraremos los recursos que necesitamos, etc. La aplicación no tiene que saber que fue migrada.
Implica una buena capa de software que de una apariencia similar a capas inferiores distintas.
La más compleja. Implica que los programas no tienen porque usar llamadas al sistema nuevas para tener ventaja del cluster. Mosix hace esto muy inteligentemente tomando la natural división en procesos de los programas para migrarlos de forma transparentemente.
RPC.-
El concepto de RPC o llamada a procedimiento remoto ha sido explotado desde hace ya varias décadas, de hecho, existen varias implementaciones creadas por distintos grupos e incluso varios protocolos. El concepto de llamada a procedimiento remoto no es otra que la que su propio nombre indica, es decir, se pretende ejecutar funciones o procedimientos en otras máquinas distintas a la nuestra y que el resultado retorne a nuestra máquina, todo ello de manera transparente. Hasta aquí el concepto de llamada a procedimiento remoto parece bastante asequible. El problema surge como siempre a la hora de implementarlo, codificarlo y tener en cuenta consideraciones de sistema.
Para empezar se deben tener en cuenta cosas como que tipo de protocolo se va a utilizar en capas inferiores. Puede ser conectivo, perfecto para olvidarse de las retransmisiones y fallos o no conectivos y aprovechar al máximo la capacidad de la red. También hay que prestar atención al formato de representación entre distintas máquinas de un mismo sistema, como podrían ser ASCII, EBCDIC o Little Endian y Big Endian, y en otros muchos problemas que implican las llamadas a procedimientos remotos2.17.
Por último (como colofón de las pegas de los sistemas implementados de los RPC) está el que generalmente no son tan transparentes como debieran. En el caso ideal, se supone que, el programador no debe saber si los procedimientos de una biblioteca son locales o remotos, ni necesitar de ningún tipo de interface para poder llamar a dichos procedimientos. Esto no se cumple, no sólo en el caso de RPC, sino en prácticamente cualquier sistema distribuido.
El RPC por excelencia es el RPC diseñado e implementado por Sun Microsystems cuya versión 2 esta especificada y tipificada en el RFC 1831. En esta RFC se puede leer no sólo el modelo de llamada a procedimiento remoto, sino también ejemplos de programación de RPC, semánticas de transporte, sistema de autenticación y modelo de programación en general de esta implementación. Dicha implementación utiliza como modelo de comunicación IP/UDP ya que la mayoría de las aplicaciones suelen estar en una LAN y por tanto se obtiene más efectividad que con TCP. Se utiliza también XDR para hacer compatible entre varios formatos de representación. Los programas requieren de librerías y funciones especiales y al mismo tiempo se requiere rpcportmapper instalado como demonio para efectuar las transacciones. Existen varias referencias acerca de como podría implementarse un sistema distribuido de este tipo en el libro Sistemas Operativos Distribuidos de Andrew S. Tanembaum.
¿Por qué no utilizar RPC en sistemas distribuidos o clusters? Existen diversas causas, unas con más peso que otras. Por norma general el modelo RPC suele ser bastante lento a pesar de utilizar UDP, por ejemplo en lo que se refiere al checksum, cambio de referencias en memoria, traducción XDR, rellenado de cabeceras y otras tantas operaciones que hacen que dicho sistema sea compatible en un entorno de trabajo hetereogéneo.
Es decir, en entornos de trabajo en los cuales no se requiere de confiabilidad, autenticación (y seguridad en general), eficiencia, y consistencia; la solución RPC es aceptable. En el caso de los clusters, suele implementarse con unos requerimientos de sistema bastante exigentes en lo que se requiere a eficiencia, y es mejor considerada una macro en el programa que utilizar todo un sistema como puede ser XDR. Generalmente los sistemas distribuidos suelen basar su desarrollo en nuevas implementaciones o modelos, que no tienen por que ser el de RPC, y no utilizan RPC debido a la poca eficiencia que este reporta y a la mala fama en seguridad que este posee.
RMI.-
También desarrollado por Sun para Java, mucha gente considera a RMI el sucesor de RPC.
RMI es un concepto confuso, tiene dos aceptaciones:
Hay 3 procesos que participan en que la invocación de métodos remotos se haga efectiva:
Hay dos tipos de clases que pueden ser usadas en Java RMI.
CORBA.-
Es un estándar desarrollado por una fundación creada por distintas empresas llamada OMG (Object Management Group) para poder hacer fácilmente programación distribuida, reutilización de objetos y de código ya hecho. CORBA cumple correctamente estos objetivos, para ello la estructura de CORBA cuenta de un lenguaje estandar llamado IDL (Interfaz Definition Language) que sirve para determinar los interfaces entre objetos, la implementación de los objetos se hace con cualquier lenguaje que tenga un compilador capaz de enlazar IDL.
CORBA es un middleware entre el sistema operativo y las aplicaciones usuario,
entre ese middleware se encuentra el ORB encargado de hacer las llamadas
entre objetos.
En resumidas cuentas, CORBA es un nivel más de abstracción.
-- Ventajas:
--Desventajas:
Aún se podría pensar en usar CORBA para envío de mensajes, pero la sobrecarga que genera no merece la pena. CORBA es muy potente para aplicaciones distribuidas o no de nueva creación y seguramente se le pudieran añadir funciones de balanceo de carga, pero para que nuestra solución sea más general es preferible no usar CORBA y tratar todo a nivel de proceso, sin abstracciones mayores. Aunque el estándar CORBA no añade estas capacidades de balanceo, implementación es específicas si que tienen reconfiguración dinámica, por ejemplo, Dinamic TAO.
Bonobo.-
Abstracción por encima de CORBA desarrollada por el proyecto GNOME.
Bonobo es un conjunto de interfaces CORBA, algunos implementados por objetos contenedores y otros por los objetos que serán contenidos (por ejemplo cuando pulsamos sobre un pdf y este se abre en nuestro propio navegador, el navegador es el contenedor y el programa lector de pdf el contenido). Bonobo es también una implementación por defecto de estos interfaces CORBA que es exportado en forma de API C para evitar a la mayoría de los programadores de preocuparse por tener que codificar directamente los interfaces CORBA.
Aunque no lo hemos mostrado en el punto anterior la programación en CORBA es ciertamente compleja, por tanto dentro del esfuerzo del proyecto GNOME para incorporar CORBA se ha desarrollado una librería de un nivel más alto llamada Bonobo (el nombre es de ciertos monos en vías de extinción que sirven para ilustrar la teoría de conexión de componentes). Otro de estos desarrollos fue ORBit que es un ORB pero con la particularidad de estar altamente mejorado en velocidad, siendo el más rápido existente (casi el doble que su más cercano competidor Omniorb) y de los que menos memoria necesita.
A parte de ORBit tenemos Gnorba que facilita en algo algunas tareas de ORBit y es más específico de GNOME, por ejemplo permite la integración del bucle principal de ORBit con el bucle principal de Gtk+, añade seguridad a las comunicaciones y un método estándar para acceder al servicio de nombres de GNOME. Para permitir acceso a un objeto en concreto se dispone la información en GOAD (GNOME Object Activation Directory) donde tenemos el id del objeto, las interfaces que exporta y como crear un ejemplar, gracias a esto se pueden usar métodos a través de la red.
Bonobo se creó para intentar mantener las aplicaciones con poca complejidad para que a los nuevos programadores les cueste poco saber cómo funciona el código y sea más fácil el mantenimiento y desarrollo. Usando todas las facilidades de CORBA que es usado para la capa de comunicación y para unir todos los componentes Bonobo entre sí. Cada componente tiene su interfaz (definido en términos de las interfaces CORBA) que exporta, ahíes donde cualquier otro componente puede conseguir información o unirse al componente.
El interface CORBA se introduce en un objeto GTK+, esto quiere decir que los objetos GTK+ son la implementación del interface IDL. Las implementaciones contienen ciertas acciones por defecto para no molestar al programador con detalles de CORBA, pero se dan unos métodos para cambiar este comportamiento.
KDE.-
En este apartado vamos a ver tres tecnologías que utiliza KDE a partir de sus versiones 2.0 y posteriores:
Estas tres tecnologías permiten acercarnos a un proceso distribuido y a una transparencia de red enormes. KDE es muy buen ejemplo de cómo se intenta cumplir algunos de los objetivos de los sistemas distribuidos, y cómo usuarios que somos de este entorno podemos decir que esta tecnología hace el día a día más sencillo.
Esta es la librería que implementa prácticamente todas la funciones de manejo de ficheros. Es la librería que se usa para proveer un manejo de ficheros transparente a red. La implementación de los distintos protocolos (FTP, HTTP, SMB, POP3, IMAP4, gopher, printer, man, gzip, etc.) es hecha por un proceso independiente llamado kioslave, uno por cada protocolo, kio_ftp implementa FTP, kio_http implementa HTTP etc. La aplicación indica el protocolo a usar y KIO decide que kioslave usar.
Por tanto gracias a estos kioslaves por ejemplo podemos tener acceso transparent e a servidores ftp, subir y bajar información simplemente copiando y pegando o haciendo drag'n drop. Podemos tener acceso a discos remotos e información remota de forma totalmente transparente. Un usuario sin mucho conocimiento de informática podría incluso nunca darse cuenta de que no estaba accediendo a su propio disco duro.
Esta tecnología no es específica de KDE sino que se usa en este entorno como en otros, vamos a ilustrar el ejemplo de KDE. En KDE existe el kxmlrpcd que permite que un cliente escrito en cualquier lenguaje, en cualquier plataforma pueda acceder a los servidores DCOP de KDE . Como casi todas las aplicaciones de KDE son servidores DCOP, se puede manejar este entorno remotamente, de forma transparente. Por tanto vemos una vez más que el límite entre local y remoto se difumina permitiéndonos ejecutar KDE como si estuviéramos ejecutando un telnet.
XmlRpc es la forma estándar de implementar RPC usando XML y HTTP. Con XML se marcan todas las llamadas a funciones , sus parámetros y sus valores de retorno. Entonces se utiliza HTTP para transferir la llamada al método. Muchísimos lenguajes disponen de parsers XML y clientes HTTP por lo que la implementación es bastante sencilla. Como el proyecto KDE ya tenía el protocolo DCOP para hacer RPC e IPC, kxmlrpcd es básicamente un traductor (servidor web por soportar HTTP), que traduce las peticiones XmlRpc en llamadas a DCOP y viceversa. Tanto el cliente XmlRpc como el servidor DCOP creen que están comunicándose solamente en su protocolo nativo con el demonio.
Fue creado por la necesidad de comunicación entre aplicaciones, no podían usar X Atoms por ser demasiado simples y crear una dependencia con Xwindow. También estuvieron intentando implementar CORBA pero se dieron cuenta que para la simple tarea de intercomunicar tareas era demasiado lento y requería demasiada memoria, además no tenía ningún tipo de autentificación.
Lo que realmente se quería era un protocolo simple con autentificación básica, aunque no fuera capaz de hacer todo lo que era capaz de hacer CORBA, pero que fuera suficientemente bueno para la tarea encomendada. Un ejemplo de estas llamadas es una aplicación recién iniciada que pregunta si hay otra aplicación con el mismo nombre abierta, si es así abrirá una nueva ventana en la antigua aplicación en vez de abrir una nueva.
DCOP es un mecanismos IPC/RPC muy simple que funciona sobre sockets. Están soportados los sockets del dominio UNIX y TCP/IP. Como capa inferior utiliza el protocolo ICE (Inter Client Exchange), que viene estándar como parte de X11R6. También depende de Qt, pero más allá de estos requerimientos no necesita más librerías. Es una capa de software muy ligera.
Las aplicaciones son clientes DCOP y se comunican entre sía través de un servidor DCOP, que funciona como director del tráfico, enviando los mensajes o las llamadas a los destinos apropiados. Todos los clientes son pares entre ellos. Hay dos tipos de acciones posibles:
Mensajes: en estos mensajes se envía el mensaje y no se espera la vuelta, por lo que la llamada no bloquea y es muy rápida.
Llamadas: se llama a una función y se debe esperar a que se devuelva algún dato.
Para crear las llamadas se ha desarrollado un compilador al estilo del compilador IDL que vimos en CORBA, llamado dcopidl (y dcopidl2cpp) que generan los stubs y skels al estilo que CORBA lo hacía para ahorrar el trabajo del programador.
En definitiva DCOP provee un método sencillo y eficiente que fue desarrollado exactamente para ese uso que evita la complejidad de CORBA pero que también permite la interconexión de objetos en una máquina o en máquinas distintas. Además en unión con XML-RPC del capítulo anterior se permite usar este protocolo desde cualquier lenguaje que era otra de las características de CORBA.
Las ventajas para crear aplicaciones distribuidas usando este protocolo son las mismas que se vieron en el apartado de CORBA.
SOAP.-
Hoy en día lo que se le pide a un sistema que use RPC o RMI es que sea confiable, robusto, que tenga APIs desarrolladas para varios lenguajes, interoperabilidad entre lenguajes, plataformas y modelos de componentes.
Desafortunadamente no hay un único sistema que tenga todas esas características. El formato de los datos y el protocolo usado para intercambiarlos es un factor determinante en el grado de interoperabilidad que pueden alcanzar las aplicaciones.
XML(eXtensible Markup Language) se está convirtiendo en un estandar para representar datos en un formato independiente de la plataforma. XML es un lenguaje fácil de general y de leer. HTTP (HyperText Transfer Protocol) también se está convirtiendo en un protocolo muy usado gracias a las aplicaciones web, que está soportado por todos los sistemas.
Las peticiones/respuesta de HTTP se pasan a través de firewall y son manejadas de forma segura, todo lo contrario que una ejecución de una llamada RPC o RMI.
Por lo tanto XML sobre HTTP parece una forma bastante inteligente para las aplicaciones distribuidas de comunicarse entre ellas. SOAP hace exactamente eso.
Representando RPCs de forma independiente a la plataforma, abre la posibilidad de implementar otros protocolos específicos de arquitectura en SOAP. Por ejemplo Microsoft parece querer usar SOAP como protocolo intermedio de intercambio al que otros protocolos pueden ser fácilmente traducidos, para potenciar el uso de sus componentes COM.
RPC en SOAP son interacciones cliente servidor sobre HTTP donde la petición/resp uesta se hacen de acuerdo con las reglas SOAP. Para elegir el objeto se usa el URI (Universal Resource Identifier) que es propio de HTTP o la cabecera SOAPAction que indica el nombre del interface. Se especifica una convención de la llamada remota , que determina la representa ción y el formato de las llamadas y respuestas. Una llamada es un dato compuesto con varios campos, uno por cada parámetro. Un mensaje de retorno es un valor de retorno y unos parámetros.