scieee AI-readable full text Open interactive document viewer

Repositorio Institucional de Documentos

Abstract

Un sistema distribuido es una colección de computadoras que se comunican entre sí a través de una red de comunicaciones y cooperan hacia la consecución de un objetivo común, siendo percibidas por el usuario como un único sistema. Cuando un sistema distribuido lleva a cabo tareas para las que el tiempo de respuesta es crítico, se lo denomina sistema de tiempo real distribuido. Algunos ejemplos de aplicación de estos sistemas son: prevención de accidentes automovilísticos (e.g. frenos ABS y sistemas de regulación de velocidad o autocrucero), control remoto de robots a partir de la información recogida por sensores, implementación de sistemas de control industrial, etc. A pesar de su extenso uso, hay una falta de especificaciones y técnicas estándar para comunicar de forma fácil y flexible los componentes de estos sistemas. Por ello, la mayor parte de los sistemas distribuidos emplean soluciones de comunicación a medida, específicamente diseñadas para funcionar en el entorno en que están situados los componentes del sistema, pero difícilmente aplicables a entornos de características diferentes. El objetivo de este proyecto no es proponer un nuevo estándar de comunicación para dichos sistemas, sino desarrollar una solución de comunicación en tiempo real que pueda ser fácilmente desplegada en una amplia variedad de entornos, liberando así de la tarea de diseñar una infraestructura de comunicación adaptada a cada situación específica. Este proyecto continúa la línea de trabajo iniciada por los departamentos de Ciencias de la Computación y de Control Automático de la Universidad de Lund, cuyo trabajo previo llevó al diseño e implementación de una herramienta para generar automáticamente infraestructuras de comunicación entre aplicaciones (LabComm) y a la especificación de un protocolo de comunicación en tiempo real basado en reserva de ancho de banda en redes Ethernet conmutadas (ThrottleNet). La realización de este Proyecto Fin de Carrera ha conllevado el diseño e implementación de dos sistemas independientes: en primer lugar, una aplicación interactiva que facilite el proceso de aprendizaje de LabComm. En esencia, esta aplicación será un simulador de transmisiones de datos que mostrará las virtudes y forma de uso del sistema LabComm. Por último, una implementación de ThrottleNet, a partir de la especificación proporcionada por estos departamentos. Colomer Vieitez, José Javier; Nilsson, Klas

Full text

Proyecto Fin de Carrera Ingenier´ıa en Inform´atica Gesti´on de comunicaci´on en tiempo real en redes Ethernet conmutadas Jos´e Javier Colomer Vieitez Director: Klas Nilsson Supervisores: Anders Blomdell y Sven Gesteg˚ard-Robertz Ponente: Jos´e Luis Villarroel Salcedo Departamentos de Ciencias de la Computaci´on y de Control Autom´atico Universidad de Lund, Suecia Departamento de Inform´atica e Ingenier´ıa de Sistemas Centro Polit´ecnico Superior Universidad de Zaragoza Zaragoza, Julio de 2011 Gesti´on de comunicaci´on en tiempo real en redes Ethernet conmutadas RESUMEN Un sistema distribuido es una colecci´on de computadoras que se comunican entre s´ı a trav´es de una red de comunicaciones y cooperan hacia la consecuci´on de un objetivo com´un, siendo percibidas por el usuario como un ´unico sistema. Cuando un sistema distribuido lleva a cabo tareas para las que el tiempo de respuesta es cr´ıtico, se lo denomina sistema de tiempo real distribuido. Algunos ejemplos de aplicaci´on de estos sistemas son: prevenci´on de accidentes automovil´ısticos (e.g. frenos ABS y sistemas de regulaci´on de velocidad o autocrucero), control remoto de robots a partir de la informaci´on recogida por sensores, implementaci´on de sistemas de control industrial, etc. A pesar de su extenso uso, hay una falta de especificaciones y t´ecnicas est´andar para comunicar de forma f´acil y flexible los componentes de estos sistemas. Por ello, la mayor parte de los sistemas distribuidos emplean soluciones de comunicaci´on a medida, espec´ıficamente dise˜nadas para funcionar en el entorno en que est´an situados los componentes del sistema, pero dif´ıcilmente aplicables a entornos de caracter´ısticas diferentes. El objetivo de este proyecto no es proponer un nuevo est´andar de comunicaci´on para dichos sistemas, sino desarrollar una soluci´on de comunicaci´on en tiempo real que pueda ser f´acilmente desplegada en una amplia variedad de entornos, liberando as´ı de la tarea de dise˜nar una infraestructura de comunicaci´on adaptada a cada situaci´on espec´ıfica. Este proyecto contin´ua la l´ınea de trabajo iniciada por los departamentos de Ciencias de la Computaci´on y de Control Autom´atico de la Universidad de Lund, cuyo trabajo previo llev´o al dise˜no e implementaci´on de una herramienta para generar autom´aticamente infraestructuras de comunicaci´on entre aplicaciones (Lab- Comm) y a la especificaci´on de un protocolo de comunicaci´on en tiempo real basado en reserva de ancho de banda en redes Ethernet conmutadas (ThrottleNet). La realizaci´on de este proyecto ha conllevado el dise˜no e implementaci´on de dos sistemas independientes: Una aplicaci´on interactiva que facilite el proceso de aprendizaje de Lab- Comm. En esencia, esta aplicaci´on ser´a un simulador de transmisiones de datos que mostrar´a las virtudes y forma de uso del sistema LabComm. Una implementaci´on de ThrottleNet, a partir de la especificaci´on proporcionada por estos departamentos. i ii Agradecimientos A mi director de proyecto, Klas Nilsson, por toda su ayuda y por haberme dado la posibilidad de trabajar en temas que me han parecido fascinantes. A Sven, por haberme propuesto la aventura de implementar ThrottleNet. A Anders Blomdell, por su interminable paciencia, por todo lo que me ha ense˜nado y por haberme demostrado que “todo es posible en Linux, si sabes d´onde buscar en el n´ucleo”. A Jos´e Luis Villarroel, por su orientaci´on y consejos como ponente. A mis amigos de Lund, por haberme regalado dos a˜nos inolvidables. A mis amigos del CPS, por las risas, los muses y los buenos ratos que han ayudado a superar la carrera. A todos mis amigos, por estar siempre a mi lado y ser como sois. Especialmente, a Violeta y a Clara, porque me conocen como nadie y son las dos hermanas que nunca he tenido. Tambi´en a mi familia, por todo su apoyo y cari˜no. Y, por supuesto, a mi peludo amigo Pitt. Pero, sobre todo, a mis padres. Por vuestra infinita paciencia, cari˜no y dedicaci´on, que me han acompa˜nado siempre. De no ser por vosotros no habr´ıa terminado cuerdo este invierno, aunque en el proceso os volviera yo locos a vosotros. Muchas gracias a todos vosotros, y a todos aquellos que olvido nombrar. iii iv ´ Indice general 1. Introducci´on 1 1.1. Objetivos y alcance del proyecto . . . . . . . . . . . . . . . . . 3 1.2. An´alisis de los problemas planteados . . . . . . . . . . . . . . 5 1.2.1. Facilitar el proceso de aprendizaje de LabComm . . . . 5 1.2.2. Comunicaci´on en red en tiempo real: posibles alternativas 7 1.3. Estructura del documento . . . . . . . . . . . . . . . . . . . . 8 2. Facilitando el proceso de aprendizaje de LabComm 11 2.1. Interactuando con el simulador . . . . . . . . . . . . . . . . . 12 2.2. Elcliente ............................. 18 2.3. Elservidor............................. 19 2.4. Comunicaci´on cliente-servidor . . . . . . . . . . . . . . . . . . 20 2.5. Ejecuci´on de simulaciones . . . . . . . . . . . . . . . . . . . . 22 2.5.1. Aislamiento de otros usuarios . . . . . . . . . . . . . . 22 2.5.2. Un marco para la ejecuci´on de simulaciones . . . . . . 24 2.5.3. Comunicaci´on productor-consumidor . . . . . . . . . . 27 2.6. Detalles t´ecnicos de la soluci´on . . . . . . . . . . . . . . . . . 28 2.6.1. Applets nofirmados.................... 28 2.6.2. Recarga din´amica de clases . . . . . . . . . . . . . . . . 28 3. ThrottleNet: comunicaci´on en red en tiempo real 31 3.1. Implementaci´on como driver ................... 33 3.2. Racionamiento de tr´afico . . . . . . . . . . . . . . . . . . . . . 34 3.2.1. Tr´afico saliente . . . . . . . . . . . . . . . . . . . . . . 35 3.2.2. Tr´afico entrante . . . . . . . . . . . . . . . . . . . . . . 36 3.3. GlobeThrottle........................... 37 3.3.1. Conexi´on a la red ThrottleNet . . . . . . . . . . . . . . 37 3.3.2. Gesti´on del ancho de banda . . . . . . . . . . . . . . . 38 3.3.3. Organizador de tr´afico NRT . . . . . . . . . . . . . . . 40 3.4. Tipos de tr´afico en una red ThrottleNet . . . . . . . . . . . . 44 3.4.1. Tr´aficoRT......................... 45 v 3.4.2. Tr´aficoNRT........................ 46 3.5. Tratamiento de ARP en situaciones de alta carga NRT . . . . 48 4. Gesti´on del proyecto 49 4.1. Metodolog´ıas de desarrollo y gesti´on del tiempo . . . . . . . . 49 4.1.1. Elsimulador........................ 49 4.1.2. La implementaci´on de ThrottleNet . . . . . . . . . . . 51 4.2. Tama˜no del proyecto . . . . . . . . . . . . . . . . . . . . . . . 55 5. Conclusiones 57 5.1. Contribuciones .......................... 57 5.2. Cumplimiento de objetivos . . . . . . . . . . . . . . . . . . . . 58 5.2.1. Elsimulador........................ 58 5.2.2. La implementaci´on de ThrottleNet . . . . . . . . . . . 59 5.3. Trabajofuturo .......................... 60 5.3.1. Posibles mejoras para el simulador . . . . . . . . . . . 60 5.3.2. Posibles mejoras para ThrottleNet . . . . . . . . . . . . 60 5.3.3. Pasos hacia la soluci´on integrada . . . . . . . . . . . . 61 5.4. Experiencia personal . . . . . . . . . . . . . . . . . . . . . . . 61 A. La herramienta de simulaci´on 63 A.1. Transferencia de ficheros mediante ObjectStreams . . . . . . . 63 A.2. Comunicaci´on productor-consumidor . . . . . . . . . . . . . . 66 A.3. Common mistakes when using the simulator . . . . . . . . . . 70 A.3.1. Producer code . . . . . . . . . . . . . . . . . . . . . . . 70 A.3.2. Consumer code . . . . . . . . . . . . . . . . . . . . . . 71 B. La implementaci´on de ThrottleNet 73 B.1. Racionamiento de tr´afico . . . . . . . . . . . . . . . . . . . . . 73 B.1.1. Procesamiento de tr´afico saliente . . . . . . . . . . . . 73 B.1.2. Procesamiento de tr´afico entrante . . . . . . . . . . . . 75 B.2. Gesti´on de ancho de banda . . . . . . . . . . . . . . . . . . . . 77 B.3. Interacci´on con la interfaz RT v´ıa ioctl . . . . . . . . . . . . . 81 C. El sistema LabComm 95 C.1. Componentes del sistema LabComm . . . . . . . . . . . . . . 95 C.2. El lenguaje LabComm . . . . . . . . . . . . . . . . . . . . . . 95 C.3. Ejemplos de protocolos de comunicaci´on . . . . . . . . . . . . 97 C.3.1.Ejemplo1......................... 97 C.3.2.Ejemplo2......................... 97 vi D. A LabComm tutorial 99 D.1.Introduction............................ 99 D.2. Step one: Generate the marshalling routines . . . . . . . . . . 101 D.3. Step two: Use the marshalling routines . . . . . . . . . . . . . 102 D.4. Protocol definition for Example 1 . . . . . . . . . . . . . . . . 105 vii Por tanto, esta aplicaci´on ser´a dise˜nada como una aplicaci´on Web, lo que permitir´a que: La aplicaci´on ejecut´andose en el lado del cliente sea ligera (e.g. un applet), no requiera configuraci´on alguna y toda la sobrecarga de generaci´on de c´odigo y manejo de ficheros se traslade al servidor. El usuario no se sienta desprotegido mientras utilice la aplicaci´on, pues- to que los applets Java[4][5] son ejecutados bajo restricciones de seguridad muy severas1[6]. Finalmente, con el fin de mostrar que LabComm puede trabajar sobre cualquier flujo de datos unidireccional sin importar su naturaleza (ya sea una pipe, un socket, etc.), el simulador permite decidir si el productor y el consumidor se ejecutar´an en la misma m´aquina (ya sea la del cliente o el servidor) o si lo har´an en m´aquinas diferentes. Ello muestra como LabComm es aplicable a pr´acticamente cualquier tipo de comunicaci´on. Se ha elegido Java para escribir esta aplicaci´on por las siguientes razones: El sistema LabComm (nos referimos al sistema en s´ı, no a las rutinas de serializaci´on generadas para comunicar aplicaciones) est´a escri- to completamente en Java, y escribir esta aplicaci´on en Java facilita la integraci´on de ambas partes. La funcionalidad de carga de clases de Java hace que sea muy sencillo ejecutar las simulaciones ¡e incluso cargar el c´odigo (binarios) del servidor en el cliente! Crear una aplicaci´on ligera con una interfaz gr´afica que resulte agradable al usuario es muy sencillo en Java, gracias a la clase Applet[4][5] y a las librer´ıas Swing[7]. 1El modelo de caj´on de arena de Java est´a basado en una defensa en tres capas: verificaci´on de bytecodes previa a su ejecuci´on, uso de un cargador de clases espec´ıfico para impedir intentos de sustituci´on de clases Java esenciales por parte del applet y un security manager que genera excepciones de seguridad al m´as m´ınimo comportamiento sospechoso por parte del applet. 6 1.2.2. Comunicaci´on en red en tiempo real: posibles alternativas Pese a que ya existe al menos una soluci´on para la comunicaci´on en tiempo real sobre hardware Ethernet est´andar, RTnet[8], este proyecto opta por implementar el protocolo ThrottleNet. Los motivos para esto son varios: Facilidad de despliegue: RTnet s´olo est´a disponible para Xenomai[9] y RTAI[10] mientras que ThrottleNet puede ser implementado para una variedad mayor de plataformas, como sistemas UNIX y similares (incluyendo Mac OS y distribuciones Linux) e incluso Windows. Eliminar limitaciones de los protocolos de paso de testigo. Por su propia naturaleza, estos protocolos limitan la eficiencia de aquellas redes en las que existen nodos con velocidades de procesamiento o transmisi´on de datos muy dispares, pues los nodos m´as r´apidos ven limitada la velocidad a la que pueden trabajar debido al uso de t´ecnicas de control de flujo[11]: •Si existen nodos que procesan datos a velocidades muy diferentes, los nodos m´as r´apidos tendr´an que disminuir su velocidad de transmisi´on para no saturar a los nodos m´as lentos. •Si existen conmutadores en la red con diferentes velocidades de transmisi´on, los conmutadores m´as r´apidos no trabajar´an al m´aximo de sus capacidades, sino a una velocidad apropiada para los conmutadores m´as lentos. Ello supone un desaprovechamiento de los recursos disponibles. RTnet es un protocolo de paso de testigo y, por tanto, se ve afectado por estas limitaciones. ThrottleNet, en cambio, no se ve afectado porque no est´a basado en paso de testigo. Como queremos que la red no s´olo sea eficiente sino que adem´as pueda ser f´acilmente desplegada en una amplia variedad de plataformas hardware y software (Objetivo 2 del proyecto), optaremos por implementar ThrottleNet. ThrottleNet ser´a instalado en cada nodo de la red para que el racionamiento de tr´afico se efect´ue de forma local a cada nodo. Aunque una implementaci´on de espacio de usuario ser´ıa m´as sencilla y directa, se ha elegido 7 implementarlo como una aplicaci´on de espacio de n´ucleo para lograr una mayor eficiencia1y facilitar la portabilidad a otros sistemas operativos en los que ThrottleNet habr´a de ser desplegado (e.g. Xenomai y VxWorks[12]). Por ello, escribiremos ThrottleNet completamente en C como un driver de red de Linux[13]. Esta implementaci´on de ThrottleNet utiliza un GlobeThrottle (ver Secci´on 3.3) para tareas de gesti´on de ancho de banda. GlobeThrottle tambi´en ha sido escrito en C por motivos de reutilizaci´on de c´odigo, pero ser´a dise˜nado como una aplicaci´on de espacio de usuario con el fin de mostrar c´omo el racionamiento de tr´afico y su planificaci´on tambi´en pueden hacerse en espacio de usuario de forma eficiente. 1.3. Estructura del documento Esta memoria consta de otros cuatro cap´ıtulos que resumen el proyecto en su totalidad: El cap´ıtulo 2 presenta la herramienta de simulaci´on dise˜nada para facilitar el aprendizaje de LabComm. En el cap´ıtulo 3 se describe c´omo funciona ThrottleNet y se ofrecen detalles espec´ıficos del dise˜no e implementaci´on realizados. En el cap´ıtulo 4 explica c´omo se ha gestionado el proyecto en cuanto a la administraci´on del tiempo disponible y a las metodolog´ıas utilizadas. Finalmente, el cap´ıtulo 5 analiza el cumplimiento de los objetivos planteados, expone las conclusiones alcanzadas y enumera las principales l´ıneas de trabajo que este proyecto deja abiertas. Por ´ultimo, los Ap´endices ofrecen informaci´on adicional que permite cubrir m´as en detalle algunos aspectos cr´ıticos de este trabajo, en particular: En el Ap´endice A se ofrece informaci´on relativa a la herramienta de simulaci´on web. El Ap´endice B detalla c´omo se efect´uan algunas tareas cr´ıticas en ThrottleNet como, por ejemplo, la gesti´on de ancho de banda. 1Una implementaci´on de espacio de usuario tambi´en funcionar´ıa, pero se ver´ıa sometida a un mayor n´umero de cambios de contexto y por tanto no ser´ıa tan eficiente como su equivalente de espacio de n´ucleo. 8 El Ap´endice C ofrece una descripci´on m´as profunda de LabComm y muestra el lenguaje que LabComm proporciona para la especificaci´on de protocolos de comunicaci´on junto con algunos ejemplos. Por ´ultimo, en el Ap´endice D se incluye un tutorial para LabComm escrito al comienzo del proyecto que pretende ofrecer una primera aproximaci´on a aquellos que desconocen LabComm por completo. 9 10 Cap´ıtulo 2 Facilitando el proceso de aprendizaje de LabComm A fin de asistir el proceso de aprendizaje de LabComm, se ha decidido dise˜nar un simulador de transmisiones de datos que utilice LabComm para codificar la informaci´on enviada. El simulador presenta un escenario con un productor y un consumidor y permite al usuario modificar estos para experimentar con diferentes tratamientos de los datos transmitidos. Como ya se explic´o en la Secci´on 1.2.1, esta aplicaci´on se ha dise˜nado siguiendo una arquitectura cliente-servidor para facilitar el manejo del simulador y disminuir la carga de trabajo en la computadora del usuario. El cliente de esta aplicaci´on es un applet que el usuario ejecuta en su navegador para configurar el simulador como desee. La m´as notable de estas opciones de configuraci´on es la posibilidad de elegir los computadores en que queremos que se ejecuten el productor y el consumidor. Estos equipos pueden ser tanto aquel en que se ejecuta el cliente como aquel en que se ejecuta el servidor, por tanto ambos subsistemas han sido dise˜nados para ser capaces de ejecutar simulaciones de forma aut´onoma o cooperando el uno con el otro. La Figura 2.1 muestra la arquitectura de la aplicaci´on dise˜nada junto con sus posibles casos de uso, en funci´on de la localizaci´on del cliente y del consumidor. M´as adelante, se ilustrar´an los componentes del cliente y del servidor y c´omo se ejecutan las simulaciones para cada posible combinaci´on de localizaciones del productor y del consumidor. Naturalmente, adem´as de la funcionalidad compartida para ejecutar simulaciones, cada subsistema a˜nade funcionalidad propia relacionada con las tareas que le corresponden a causa de su localizaci´on (en el cliente o en el servidor). La Secci´on 2.1 explica c´omo el usuario interacciona con la aplicaci´on 11 Figura 2.1: Arquitectura del simulador, ilustrando los diferentes modos de trabajo que ofrece en cuanto a la localizaci´on del productor y del consumidor (La notaci´on “productorconsumidor” es usada: “cliente-servidor” indica un escenario en que el productor corre en el cliente y el consumidor en el servidor). y las Secciones 2.2, 2.3 y 2.4 describen al cliente, al servidor y la comunicaci´on entre ambos, respectivamente. Por ´ultimo, la Secci´on 2.5 detalla c´omo se ejecutan las simulaciones y la Secci´on 2.6 rese˜na detalles t´ecnicos de la implementaci´on. 2.1. Interactuando con el simulador Dado que el cliente reside en la computadora del usuario, su principal funci´on es ofrecer a este una interfaz gr´afica desde la que poder manejar de forma c´omoda e intuitiva la aplicaci´on. Esta interfaz (ver Figura 2.2) ofrece multitud de opciones para configurar a medida cada simulaci´on. Entre estas opciones est´an: Determinar el computador en que se ejecutar´an el productor y el consumidor durante la simulaci´on. Como las ´unicas opciones son el cliente o el servidor, hay cuatro posibles combinaciones entre las que elegir. La notaci´on “producer - consumer”ser´a utilizada (e.g. “server - client” especifica una simulaci´on en la que el productor residir´a en el servidor mientras que el consumidor se ejecutar´a en el cliente). 12 Figura 2.2: Interfaz gr´afica ofrecida al usuario para configurar y ejecutar las simulaciones. Permite elegir d´onde se ejecutaran el productor y el consumidor, examinar y modificar el c´odigo fuente del protocolo LabComm, del productor y del consumidor y enviar peticiones de compilaci´on y ejecuci´on al servidor. Examinar la plantilla del protocolo de comunicaci´on proporcionada (ver Figura 2.3) y permitir modificarla para a˜nadir nuevos tipos de datos, modificar los ya existentes o incluso eliminarlos. Elegir los lenguajes de programaci´on1en que el productor y el consumidor estar´an escritos (ver Figura 2.4 para el caso del productor, el caso del consumidor es an´alogo). La herramienta de simulaci´on configurar´a el compilador de LabComm para que ´este genere las rutinas de serializaci´on en los lenguajes elegidos. 1LabComm puede generar rutinas de serializaci´on[3] en Java, C, C# y Python. 13 Figura 2.3: Un cuadro de texto que muestra el protocolo LabComm empleado en la simulaci´on. El texto del cuadro puede ser modificado por el usuario y enviado de vuelta al servidor. Mostrar las plantillas proporcionadas para el productor y el consumidor y permitir modificarlas (ver Figura 2.5 para el caso del productor, el caso del consumidor es an´alogo), de manera que puedan hacer uso de los cambios hechos en el protocolo de comunicaci´on para enviar nuevos tipos de datos o modificar la forma en que est´an procesando los datos que env´ıan o reciben. Visualizar informaci´on relativa a la ejecuci´on del productor y del consumidor a trav´es de sus ventanas de registro de mensajes. Emitir peticiones de compilaci´on al servidor. En caso de haber fallos de compilaci´on, la interfaz gr´afica mostrar´a un mensaje de error que contendr´a los errores hallados (ver Figura 2.6) mientras que si no hay errores se permitir´a continuar y ejecutar la simulaci´on (ver Figura 2.7). Ejecutar la simulaci´on. Tanto en caso de ´exito como de error, el resultado de la simulaci´on ser´a mostrado en las ventanas de registro de mensajes del productor y del consumidor. La Figura 2.8 muestra el resultado de una simulaci´on ejecutada sin contratiempos, mientras que las Figuras 2.9 y 2.10 muestran ejemplos de posibles problemas que 14 Figura 2.4: Selecci´on del lenguaje de programaci´on en que el productor est´a implementado. El compilador de LabComm tambi´en producir´a las rutinas de serializaci´on en ese lenguaje. Las mismas opciones se ofrecen para el consumidor. pueden ocurrir durante la simulaci´on, debidos a una incorrecta modificaci´on del productor y del consumidor por parte del usuario. As´ı, y dado que se dan indicaciones acerca de c´omo modificar el productor y el consumidor, se espera que el usuario aprenda a trav´es de la experimentaci´on c´omo el productor y el consumidor utilizan LabComm para comunicarse. Cuando el usuario carga el applet en su navegador, este utiliza un socket para conectarse al servidor desde el que ha sido descargado1e inicia una sesi´on para el usuario. A cada nueva sesi´on se le asigna un directorio propio que contiene una copia de todas las plantillas de c´odigo fuente empleadas en la simulaci´on. Siempre que un usuario modifica un fichero y lo env´ıa de vuelta al servidor, este fichero es depositado en el directorio que corresponde a la sesi´on del usuario, evitando as´ı interferencias con los dem´as usuarios. 1Dado que se trata de un applet no firmado, s´olo puede crear conexiones hacia el servidor desde el que ha sido descargado. Ello causar´a algunos inconvenientes, m´as informaci´on en la Secci´on 2.6. 15 Si definimos una superclase abstracta para esos mensajes que implemente la interfaz Serializable, podremos definir toda una jerarqu´ıa de clases de mensajes con diferentes prop´ositos y todas ellas ser´an serializables. Como los Arrays[18] de Java son tambi´en objetos, esta aplicaci´on implementa la transferencia de ficheros a partir de la transmisi´on de Arrays de bytes. Es as´ı como se transmiten los ficheros de c´odigo entre el cliente y el servidor. La clase utilizada para enviar y recibir ficheros se llama FileTransfer y su c´odigo est´a disponible en el Ap´endice A.1. Es muy conveniente implementar los mensajes como clases, pues ello permite enviar mensajes muy complejos sin necesidad de dise˜nar un intrincado protocolo de comunicaci´on. Adem´as, reconocer los mensajes entrantes es muy sencillo, pues el operador instanceof[19] de Java puede ser usado para determinar la clase del mensaje que hemos recibido y as´ı interpretarlo correctamente. 2.5. Ejecuci´on de simulaciones 2.5.1. Aislamiento de otros usuarios En circunstancias normales, se espera que m´ultiples usuarios interaccionen con la aplicaci´on, ya sea enviando ficheros modificados al servidor, solicitando compilaciones y ejecuciones de simulaciones, etc. Hemos de garantizar que las acciones de cada usuario no afecten en modo alguno a los dem´as usuarios, pues si no estos experimentar´an un comportamiento inesperado y err´atico por parte de la aplicaci´on. En otras palabras, cada usuario deber´ıa tener la impresi´on de que es el ´unico que est´a trabajando con el simulador. Para ello, impondremos acceso en exclusi´on mutua a todos los recursos compartidos (e.g. los compiladores mencionados en la Secci´on 2.3 est´an implementados como monitores[14]) y nos aseguraremos de que cada usuario pueda acceder ´unicamente a los ficheros pertenecientes a su sesi´on. Al inicio de cada sesi´on, se crear´a un subdirectorio para cada usuario bajo los directorios de ficheros de c´odigo fuente y binarios localizados en el directorio ra´ız del servidor web. Cada uno de estos nuevos directorios recibir´a un nombre basado en un identificador de sesi´on ´unico, para garantizar la segura identificaci´on de los ficheros de cada usuario. El directorio de ficheros de c´odigo fuente ser´a inicialmente provisto de una copia de todos los ficheros de c´odigo usados en la simulaci´on (protocolo, productor y consumidor). Siempre que el usuario modifique uno de estos, la versi´on enviada al servidor 22 ser´a emplazada en su directorio de ficheros fuente y siempre que el usuario solicite una compilaci´on, los ficheros resultantes se colocar´an en su directorio de binarios. Naturalmente, estos directorios son debidamente borrados al t´ermino de cada sesi´on. La Figura 2.12 muestra una estructura de directorios simplificada para ilustrar d´onde se colocar´ıan los ficheros en una sesi´on con identificador 2. Figura 2.12: Estructura de directorios simplificada para mostrar d´onde colocar los ficheros de una sesi´on con identificador 2. Todos los elementos del directorio de ficheros fuente tienen su equivalente en el directorio de binarios como resultado del proceso de compilaci´on. La ´unica excepci´on es el protocolo LabComm (.lc), pero esto es as´ı porque la definici´on del protocolo no es de ninguna utilidad en el directorio de binarios. Lo que necesitamos son las rutinas de serializaci´on obtenidas al compilar el protocolo, que ser´an colocadas en el directorio “./bin/clientFiles/dir2/labCommRoutines”. 23 2.5.2. Un marco para la ejecuci´on de simulaciones Idealmente, un marco para la ejecuci´on de simulaciones deber´ıa simplificar las tareas de inicializar el productor y el consumidor, configurarlos para que se comuniquen con independencia del equipo en que se est´en ejecutando (ya sea el cliente o el servidor), llevar a cabo la simulaci´on y devolver sus resultados (en forma de uno o dos ExecutionLogs). Para esto, la clase ExecutionHandler ha sido escrita. El uso de esta clase es realmente sencillo, dado que s´olo requiere de cierta informaci´on de inicializaci´on que le permita autoconfigurarse y, hecho esto, se le puede pedir que ejecute simulaciones. La Figura 2.13 muestra c´omo se usa esta clase. Figura 2.13: Un ClientReqProcessor o un ServerReqProcessor configura un Execution- Handler, lo utiliza para ejecutar una simulaci´on y obtiene un ExecutionLog (o dos) como resultado. Un ExecutionHandler requiere de la siguiente informaci´on para autoconfigurarse: Configuraci´on de la simulaci´on: computadoras (cliente o servidor) en que se van a ejecutar el productor y el consumidor. Informaci´on para establecer la conexi´on: nombre del servidor y puerto en que la aplicaci´on en el servidor (ya sea una aplicaci´on productora o una consumidora) estar´a escuchando. Como un ExecutionHandler puede ser creado por un ClientReqProcessor o por un ServerReqProcessor, es necesario informar al ExecutionHandler de si ha sido creado en el cliente o en el servidor. Esta informaci´on es necesaria a la hora de conectar el productor y el consumidor para ejecutar la simulaci´on y para devolver los ExecutionLogs al cliente. 24 Informaci´on para cargar las clases (i.e. URLs[20]) de las clases que implementan el productor, el consumidor o ambos). Una vez inicializado, el ExecutionHandler determinar´a qu´e clases (productor y/o consumidor m´as las rutinas de serializaci´on necesarias) deben ser cargadas y si esta carga ha de hacerse de forma local o remota (desde el servidor). En cualquier caso, un cargador de clases Java (Java ClassLoader[21]) ser´a usado para esta tarea. Una vez el productor, el consumidor o ambos hayan sido cargados, el ExecutionHandler los configurar´a para que sepan c´omo conectarse el uno al otro en funci´on del equipo en que se vaya a ejecutar cada uno de ellos. Por supuesto, tanto el productor como el consumidor son ejecutados en threads independientes durante la simulaci´on. Al t´ermino de la simulaci´on el resultado de la misma, en forma de uno o dos ExecutionLogs, es devuelto a la clase que configur´o al ExecutionHandler para ejecutar la simulaci´on. Las Figuras 2.14, 2.15 y 2.16 ilustran c´omo proceden los ExecutionHandlers ante cada posible escenario de simulaci´on. En todos ellos, las simulaciones comienzan bajo demanda del usuario y terminan al entregar los resultados de la simulaci´on al applet para que los muestre al usuario. Los pasos intermedios var´ıan en todos los casos, como puede verse en las Figuras. Figura 2.14: Un ExecutionHandler lleva a cabo una simulaci´on en el cliente. Ello requiere cargar el productor y el consumidor desde el servidor. Los resultados son entregados directamente al ClientReqProcessor, que los remitir´a al applet. 25 Figura 2.15: Un ExecutionHandler ejecuta toda la simulaci´on en el servidor. La carga de clases se efect´ua de forma local. Cuando la simulaci´on termina y el ServerReqProcessor obtiene los ExecutionLogs, utiliza el ServerCommProtocol para enviarlos al cliente. Una vez recibidos en el cliente, se le entregan al applet para que los muestre en la interfaz gr´afica de usuario. Figura 2.16: Dos ExecutionHandlers, uno sito en el cliente y otro en el servidor, colaboran para llevar a cabo la simulaci´on. En este caso, parte de los resultados ser´a generada en el cliente y parte vendr´a del servidor. 26 2.5.3. Comunicaci´on productor-consumidor El productor y el consumidor utilizan un flujo de datos para transmitir los mensajes codificados con LabComm. Como el productor y el consumidor pueden ejecutarse en la misma m´aquina o en m´aquinas distintas, los comunicaremos usando un socket, pues estos son v´alidos en ambas situaciones. Sin embargo, establecer conexiones empleando sockets puede ser complicado a causa de todas las posibles localizaciones del productor y del consumidor y de que la ´unica direcci´on fija y conocida es la del servidor. Si empleamos una aproximaci´on cliente-servidor para conectar al productor y al consumidor, podemos forzar a que la aplicaci´on que se ejecute en el servidor act´ue como un servidor y a que la aplicaci´on que se ejecute fuera del servidor haga el papel de cliente. Si tanto el productor como el consumidor se ejecutan en el servidor, la decisi´on de cu´al toma el papel de servidor ser´ıa arbitraria. Como ser´a explicado m´as a fondo en la Secci´on 2.6.1, no se pueden llevar a cabo simulaciones en modo cliente-cliente en esta versi´on del simulador, por lo que este esquema solucionar´ıa el problema de conectar al productor y al consumidor. La implementaci´on es la siguiente: el productor y el consumidor desconocen su papel en la comunicaci´on (cliente/servidor) y se conectan el uno al otro a trav´es de una interfaz Java llamada CommunicationSupport. Esta interfaz define dos m´etodos: connect() ygetSocket(). Dos clases implementan esta interfaz: ClientSupport toma el papel de cliente en la conexi´on. Cuando su m´etodo connect() es invocado, este intenta conectar a un puerto espec´ıfico de un computador determinado. ServerSupport act´ua como un servidor. Cuando su m´etodo connect() es invocado, comienza a escuchar en un puerto, esperando peticiones de conexi´on. Cuando un ExecutionHandler va a inicializar a un productor/consumidor, eval´ua si ese agente tomar´a el papel de cliente o de servidor. Dependiendo de esto, crear´a un objeto ClientSupport o un objeto ServerSupport y se lo pasar´a al constructor del agente. Tanto productores como consumidores acceden al objeto ClientSupport/ServerSupport a trav´es de la interfaz CommunicationSupport y por tanto no son conscientes de c´omo se establece la conexi´on. Estos simplemente invocan el m´etodo connect() de la interfaz y esperan a que termine. Cuando eso ocurre, una conexi´on ha sido establecida entre el productor y el consumidor. Entonces, utilizar´an el m´etodo getSocket() de 27 CommunicationSupport para obtener el socket que los conecta y lo utilizar´an para enviar/recibir los datos codificados con LabComm. 2.6. Detalles t´ecnicos de la soluci´on Determinadas caracter´ısticas de dise˜no del lenguaje Java han impuesto restricciones que han tenido un impacto en la implementaci´on y funcionalidad del simulador. Estas son: 2.6.1. Applets no firmados Uno de los motivos de que los applets sean tan usados es que no entra˜nan riesgos de seguridad. Ello es as´ı porque, o bien el applet ha sido revisado y se˜nalado como inocuo por una autoridad certificadora (applets firmados/fiables) o bien el applet es ejecutado bajo severas restricciones de seguridad (applets no firmados/no fiables). El applet usado por el simulador no est´a firmado y por tanto est´a afectado por estas restricciones de seguridad. Una de dichas restricciones impide al applet establecer conexiones a otros ordenadores que no sean aquel del que el applet se ha descargado. Ello imposibilita ejecutar el simulador en modo cliente-cliente, pues no es posible establecer conexiones dentro del cliente. Sin embargo, esto no supone un gran inconveniente, porque el motivo de querer usar todos estos modos (cliente-cliente, servidor-cliente, etc) era demostrar que LabComm puede trabajar sobre cualquier tipo de flujo, al margen de si este flujo es local a la computadora o entre diferentes computadoras, y ello ha quedado demostrado al no ocasionar LabComm fallo alguno en ninguno de los otros modos de trabajo del simulador. 2.6.2. Recarga din´amica de clases Para poder ejecutar simulaciones cuando el usuario lo solicite, las clases que intervienen en la simulaci´on han de ser cargadas de forma din´amica. Los ClassLoaders[21] de Java facilitan esta tarea, pero desconocer c´omo un ClassLoader funciona puede traer consigo efectos inesperados; cuando un ClassLoader recibe una petici´on de carga de una clase, primero comprueba si la clase ha sido previamente cargada o si alguno de los padres del ClassLoader (hay una jerarqu´ıa parental) puede cargarla. S´olo si ninguna de estas opciones es posible intentar´a el ClassLoader cargar la clase. Esto es problem´atico, pues se espera que los usuarios del simulador sigan un proceso iterativo de 28 modificar, compilar y ejecutar el c´odigo de la simulaci´on hasta que obtengan los resultados deseados. Si ignor´aramos c´omo trabajan los ClassLoaders, cuando un usuario intentara compilar y ejecutar una simulaci´on m´as de una vez se encontrar´ıa con que el agente modificado (ya sea el productor o el consumidor) seguir´ıa mostrando el mismo comportamiento que antes, pues la ´ultima versi´on de esa clase no habr´ıa sido cargada por la M´aquina Virtual de Java (JVM). La soluci´on inmediata a este problema ser´ıa Java Reflection[22][23], pero esta no es posible a causa de las restricciones de seguridad aplicadas a los applets no firmados. Estos deben siempre utilizar su propio AppletClass- Loader (una subclase de ClassLoader) y no pueden instanciar ning´un otro ClassLoader, lo que hace que esta soluci´on no sea aplicable a nuestro problema. Sin embargo, podemos evitar el problema si versionamos el productor y el consumidor; cada vez que el usuario modifique uno de estos, su n´umero de versi´on ser´a actualizado por el servidor. Si concatenamos este n´umero al nombre de la clase, conseguiremos que la JVM cargue cualquier versi´on modificada del productor y del consumidor y conseguiremos que la simulaci´on transcurra de acuerdo con las expectativas del usuario. 29 30 Cap´ıtulo 3 ThrottleNet: comunicaci´on en red en tiempo real Como se ha explicado anteriormente, la fiabilidad de una red Throttle- Net depende de un uso controlado del ancho de banda disponible en la red. Es por ello que los nodos ThrottleNet no emplean directamente su enlace f´ısico al conmutador para comunicarse con los dem´as nodos. En su lugar, establecen conexiones l´ogicas1unidireccionales hacia aquellos nodos a los que desean enviar datos. Estas conexiones l´ogicas transmiten datos a trav´es de los enlaces f´ısicos que conectan los nodos al conmutador (ver Figura 3.1) y tienen un ancho de banda asignado. Por tanto, la carga de tr´afico en cada enlace f´ısico puede ser calculada como la suma de todos los anchos de banda asignados a las conexiones que transmiten datos a trav´es del enlace. La red evita situaciones que conduzcan a un uso excesivo del ancho de banda denegando arbitrariamente aquellas peticiones de establecimiento de conexiones que requieran m´as ancho de banda que el que hay disponible en el enlace f´ısico sobre el que pretenden transmitir (ver Secci´on 3.3.2). Ahora es momento de considerar c´omo se transmiten datos a trav´es de las conexiones l´ogicas. Si utiliz´asemos TCP/UDP y/o IP, el protocolo ARP[24] ser´ıa empleado para determinar la direcci´on f´ısica de los receptores de los mensajes. Esto puede plantear problemas, dado que ARP genera mensajes multidifusi´on (broadcast) que consumen grandes cantidades de ancho de banda y pueden incrementar notablemente el tiempo de entrega de los mensajes. Dada la importancia de cumplir con los plazos de entrega, ThrottleNet no utilizar´a TCP/UDP ni IP para transmitir datos, sino que trabajar´a directa- 1Utilizamos el t´ermino “conexi´on l´ogica” en vez de “servicio” para reflejar que estas conexiones no transmiten datos codificados utilizando LabComm. 31 empleando las llamadas al sistema read ywrite. En este caso, “leer” significa obtener mensajes de la red, mientras que “escribir” significa enviar mensajes a trav´es de la red. La Figura 3.3 ilustra c´omo se hace esto. Figura 3.3: Conexi´on de GlobeThrottle a la red ThrottleNet. Aunque un driver Throttle- Net tambi´en podr´ıa ser acoplado al puente Linux, esto no se muestra en esta imagen por motivos de claridad. 3.3.2. Gesti´on del ancho de banda Una conexi´on l´ogica en una red ThrottleNet implica a un emisor y a dos o m´as receptores. Estas conexiones no deben usar en exceso los tres elementos f´ısicos de la red que los sustentan, que son los enlaces f´ısicos de los nodos emisor y receptores al conmutador y los bufferes de los puertos de salida del conmutador que dirigen tr´afico a los receptores. Es decir, validar una nueva conexi´on supone comprobar que: Hay suficiente ancho de banda disponible para enviar tr´afico sobre los enlaces f´ısicos afectados. Ninguno de los bufferes de salida del conmutador asignados a los nodos receptores se ver´a sobrecargado en caso de que llegaran, simult´aneamente, mensajes de todas las conexiones a las que esos nodos est´an suscritos como receptores de datos. Ejemplos: Aquellas conexiones que env´ıen datos con elevada frecuencia ser´an probablemente rechazadas, puesto que sus requisitos de ancho de banda ser´an altos incluso si env´ıan mensajes de peque˜no tama˜no. 38 Las conexiones que env´ıen mensajes grandes a baja frecuencia probablemente pasar´an el control de ancho de banda. Sin embargo, si un nodo est´a suscrito a varias de estas conexiones como receptor de datos y una nueva petici´on de conexi´on llega, deber´a verificarse que una llegada simult´anea de mensajes de todas esas conexiones no sobrecargar´a el buffer de salida del conmutador dedicado al nodo en cuesti´on. Cuando un nodo ThrottleNet desea establecer una conexi´on ha de solicitar permiso a GlobeThrottle. GlobeThrottle mantiene un registro de todas las conexiones l´ogicas establecidas y efect´ua c´alculos para determinar si aceptar la nueva conexi´on conducir´ıa a un uso abusivo de los segmentos f´ısicos de la red mencionados. Una conexi´on l´ogica en una red ThrottleNet ´unicamente genera tr´afico cuando los dos extremos de la comunicaci´on est´an definidos: en ausencia de emisor, el receptor nunca recibir´a datos de la conexi´on, mientras que si s´olo hay emisor, este nunca enviar´a datos a trav´es de la conexi´on, pues no habr´a nadie esperando a consumirlos (m´as detalles en la Secci´on 3.4.1). Como una conexi´on puede tener m´as de un sumidero1, es necesario verificar que ninguno de los segmentos f´ısicos de red utilizados por los nodos que participan en la conexi´on ser´a utilizado en exceso como consecuencia de la aceptaci´on de dicha conexi´on. El proceso seguido por GlobeThrottle para decidir si las conexiones son aceptadas o no est´a ilustrado en el Ap´endice B.2. Si GlobeThrottle estima que aceptar la conexi´on no har´a peligrar la integridad de la red, la conexi´on ser´a aceptada y GlobeThrottle actualizar´a su base de datos de conexiones. Cuando esto ocurre, se muestra un informe en pantalla (ver Figura 3.4). Figura 3.4: Posible informe mostrado por GlobeThrottle, detallando los servicios activos y un listado de los nodos en la red. 1Un sumidero es un nodo que consume datos de una conexi´on mientras que una fuente es un nodo que produce datos y los env´ıa a trav´es de una conexi´on. 39 3.3.3. Organizador de tr´afico NRT Adem´as de tr´afico de tiempo real, ser´ıa deseable permitir a los usuarios utilizar la red con fines que no requieran de una transmisi´on de datos “urgente” (e.g. establecer una conexi´on SSH a un servidor, emplear la utilidad ping para comprobar si se puede alcanzar a un computador, etc). Todos los nodos ThrottleNet cuentan con una conexi´on l´ogica por defecto, la conexi´on NRT. Esta conexi´on dispone de ranuras de transmisi´on de 60 bytes de longitud cada milisegundo que se utilizan para enviar el tr´afico que no tiene necesidades de tiempo real (NRT) del nodo. Encapsularemos el tr´afico de las capas superiores de la pila de protocolos como tr´afico NRT y lo transmitiremos a trav´es de esta conexi´on. As´ı, podremos utilizar TCP/UDP y/o IP (entre otros) en redes ThrottleNet. La Figura 3.5 muestra un escenario en que un nodo est´a suscrito a tres conexiones l´ogicas de tiempo real y env´ıa el tr´afico de las capas de Internet, Transporte y Aplicaci´on a trav´es de la conexi´on NRT. Figura 3.5: ThrottleNet permite tr´afico RT y NRT. El tr´afico de cada una de las redes se encapsula empleando, o bien la conexi´on l´ogica de tiempo real correspondiente, o bien la conexi´on NRT. Puede apreciarse como todas estas conexiones tienen diferentes tama˜nos de paquete y per´ıodos de espera entre transmisiones si se examinan los fragmentos enviados a trav´es del enlace f´ısico. 40 Ahora que las capas superiores de la pila de protocolos pueden trabajar en la red, hemos de controlar de alguna manera los mensajes de multidifusi´on de ARP para mantener el consumo de ancho de banda de la conexi´on NRT en un nivel razonable. Lo haremos utilizando un mecanismo de entrega de mensajes en dos pasos: los nodos ThrottleNet no intercambiar´an mensajes NRT directamente entre ellos, sino que los enviar´an a GlobeThrottle y este los reenviar´a a sus destinatarios. Ello requerir´a encapsular el mensaje original dentro de un mensaje intermedio, que ser´a dividido y sus fragmentos enviados a GlobeThrottle. GlobeThrottle reconstruir´a el mensaje intermedio a partir de sus fragmentos y obtendr´a de ah´ı el mensaje original. Entonces, GlobeThrottle fragmentar´a el mensaje original y enviar´a los fragmentos al destinatario original de ese mensaje. Este proceso est´a mostrado en la Figura 3.6. Figura 3.6: El mecanismo de entrega en dos pasos requiere encapsular el mensaje original dentro de otro y remitir este ´ultimo a GlobeThrottle. 41 Como GlobeThrottle recibe y env´ıa mensajes NRT de/hacia los nodos ThrottleNet, podemos afirmar que existen dos conexiones l´ogicas entre cada nodo ThrottleNet y GlobeThrottle: una que env´ıa datos del nodo a Globe- Throttle y otra que funciona en sentido contrario. GlobeThrottle utiliza una cola de entrada para procesar los mensajes que llegan a trav´es de la conexi´on que remite datos desde el nodo y una cola de salida para enviar mensajes a trav´es de la conexi´on que transmite datos hacia el nodo. Cuando un mensaje es reensamblado en una cola de entrada, su direcci´on de destino es examinada. Si es una direcci´on unicast, el mensaje es puesto en la cola de salida del nodo con dicha direcci´on. En cambio, si la direcci´on es multidifusi´on (broadcast) (e.g. ARP) GlobeThrottle env´ıa una copia unicast de ese mensaje al resto de nodos en la red (pero no al emisor) y descarta el mensaje de multidifusi´on. De esta manera los mensajes multidifusi´on NRT no ocasionan problemas y ARP puede ser usado para resolver direcciones de tr´afico NRT. Imaginemos el siguiente escenario: hay una red de ´area local en la que utilizamos IP sobre ThrottleNet. Hay cuatro computadoras en la red, cada una de las cuales se conecta a la red utilizando el protocolo ThrottleNet. Denominaremos a estos equipos nodos A,B,CyD, respectivamente. Un usuario trata de utilizar el nodo Apara enviar una solicitud ping al nodo B. El proceso desencadenado es el siguiente: 1. Las capas superiores de la pila de protocolos en el nodo Ageneran un mensaje ARP para hallar la direcci´on MAC del nodo B. Como ya se ha explicado, el mensaje ARP se encapsula dentro de otro que va dirigido a GlobeThrottle. Este ´ultimo mensaje es fragmentado y sus fragmentos colocados en una cola de transmisi´on a la espera de ser enviados. El proceso es ilustrado en la Figura 3.6 (con la salvedad de que el mensaje enviado no ser´a un mensaje ping sino un mensaje ARP). 2. Una vez haya recibido todos los fragmentos, GlobeThrottle los ensamblar´a para formar el mensaje que act´ua como envoltorio y de su interior recuperar´a el mensaje original. GlobeThrottle ver´a que su direcci´on destino es de tipo broadcast y entonces crear´a copias unicast del mensaje y las enviar´a a las colas de salida asociadas a los nodos B,CyD(ver Figura 3.7). El mensaje ARP original ser´a descartado. 3. Las versiones unicast del mensaje ARP son fragmentadas y sus fragmentos transmitidos a B,CyD. Cada nodo reensamblar´a el ARP y lo enviar´a a las capas superiores de la pila de protocolos, las cuales lo entregar´an a la aplicaci´on que corresponda. Las capas superiores del nodo 42 Bver´an que la direcci´on IP contenida en el ARP es la del nodo By generar´an un mensaje ARP de respuesta identificando al nodo Bcomo receptor y dando a conocer su direcci´on MAC. Este mensaje tambi´en ser´a envuelto en otro que ser´a enviado al GlobeThrottle, a causa del mecanismo de entrega en dos pasos. 4. Tanto la respuesta de BaAcomo los mensajes ping que AyBintercambiar´an ser´an de tipo unicast. El proceso para transmitir estos mensajes es mostrado en la Figura 3.8. Figura 3.7: Tratamiento de mensajes multidifusi´on dentro de GlobeThrottle. Estos mensajes suelen ser mensajes de descubrimiento/exploraci´on de ARP. 43 Figura 3.8: Tratamiento de tr´afico NRT dentro de GlobeThrottle y comunicaci´on con las capas superiores de la pila de protocolos. 3.4. Tipos de tr´afico en una red ThrottleNet Todos los mensajes transmitidos a trav´es de una red ThrottleNet son tramas Ethernet provistas de cabeceras ThrottleNet (ver Figura 3.9). Los campos de estas cabeceras contienen la siguiente informaci´on: Direcciones MAC fuente ydestino. Identificador de fragmento yn´umero de fragmentos en que ha sido dividido el mensaje original. Esta informaci´on es requerida por la l´ogica que controla la recepci´on de mensajes (ver Secci´on 3.2.2). Longitud del mensaje. Por ´ultimo, y de especial inter´es para esta secci´on, el tipo de mensaje que se obtendr´a del ensamblaje de los fragmentos. Los diferentes tipos de mensajes ThrottleNet pueden clasificarse atendiendo a diferentes criterios, de los que el m´as importante es la necesidad de 44 respetar los plazos de entrega de los mensajes (i.e. clasificaci´on como tr´afico RT o NRT). Figura 3.9: Cabeceras utilizadas por los mensajes enviados en una red ThrottleNet. El campo identificador de servicio se utiliza para identificar la conexi´on l´ogica utilizada (si el mensaje no se env´ıa a trav´es de la conexi´on NRT). 3.4.1. Tr´afico RT Por definici´on, el tr´afico de tiempo real es aquel que requiere que sus mensajes se entreguen a tiempo. Este tr´afico fluye a trav´es de conexiones l´ogicas que pueden ser configuradas para asegurar la entrega a tiempo de los mensajes. Los parametros que admiten ser configurados son: El m´ınimo per´ıodo de espera entre dos transmisiones de mensajes consecutivas. Puede tomar cualquier valor entre 1 microsegundo y un segundo. El m´aximo tama˜no de los mensajes enviados a trav´es de la conexi´on. Este par´ametro determina el tama˜no de los fragmentos en que ser´an divididos los mensajes para poder ser transmitidos. Toma valores entre 64 y 1500 bytes, que son las longitudes m´ınimas y m´aximas permitidas para tramas Ethernet. Los nodos se suscriben a las conexiones l´ogicas como fuentes o sumideros de datos. GlobeThrottle mantiene informadas a las fuentes de cada servicio acerca de quienes son los sumideros del servicio (as´ı es como se resuelven direcciones para tr´afico RT). Por tanto, cuando una fuente quiere enviar un mensaje a trav´es de una conexi´on, crea una copia unicast del mensaje para cada sumidero conocido y env´ıa las copias. Es interesante observar que: 1. Las fuentes de conexiones l´ogicas para las que no hay sumideros registrados siempre descartan los mensajes que se les pide que env´ıen, por lo que no consumen ancho de banda. 45 2. El ancho de banda consumido por una fuente es directamente proporcional al n´umero de sumideros del servicio, pues todos los mensajes unicast a los sumideros se env´ıan en paralelo. Esto debe ser considerado cuando se eval´uan los riesgos de aceptar una petici´on de establecimiento de conexi´on (ver Secci´on 3.3.2). 3.4.2. Tr´afico NRT El tr´afico NRT engloba a aquellos mensajes que no han de cumplir con plazos de entrega, pero que se desea sean entregados lo m´as r´apido posible. El tr´afico NRT puede ser generado por el usuario o por la propia red Throttle- Net para efectuar tareas de mantenimiento. Este ´ultimo se usa para conocer el estado de la red (e.g. nodos que hay en la red) y el usuario desconoce su existencia. Independientemente de su origen, todos los mensajes NRT se env´ıan a trav´es de la conexi´on NRT. Los diferentes tipos de mensajes son: Mensajes multidifusi´on para encontrar a GlobeThrottle. Los nodos ThrottleNet necesitan poder comunicarse con GlobeThrottle, pues es este quien proporciona los mecanismos de resoluci´on de direcciones en una red ThrottleNet. Si un nodo no puede comunicarse con GlobeThrottle, tampoco podr´a enviar mensajes de ning´un tipo, pues no ser´a capaz de hallar las direcciones f´ısicas de los destinatarios de los mensajes. Por esto, cada vez que un nodo se conecta a la red Throttle- Net, su primera tarea es enviar estos mensajes de manera peri´odica hasta obtener respuesta. Cuando GlobeThrottle recibe un mensaje de este tipo de un nodo que a´un no consta como registrado en la red, GlobeThrottle crea las estructuras necesarias para sustentar la comunicaci´on del nodo en la red (i.e. procesar sus mensajes NRT, atender peticiones de establecimiento de conexiones, etc) y comienza a enviar keepalives al nodo en cuesti´on, para hacerle saber que se ha conectado con ´exito a la red. Los nodos recuperan la direcci´on de GlobeThrottle de los keepalives y pueden entonces empezar a operar normalmente (i.e. enviar sus mensajes). Mensajes para solicitar el establecimiento o desvinculaci´on de una conexi´on l´ogica. Los primeros sirven para pedir permiso a Globe- Throttle para crear una nueva conexi´on l´ogica de tiempo real, mientras que los segundos indican a GlobeThrottle que un nodo no desea enviar/recibir m´as datos a trav´es de una determinada conexi´on y que, por tanto, el ancho de banda que dicha conexi´on utilizaba vuelve a estar disponible. 46 Keepalives de los nodos ThrottleNet hacia GlobeThrottle. Gracias a estos mensajes, GlobeThrottle est´a siempre al corriente de las conexiones l´ogicas a las que cada nodo est´a suscrito. Adem´as, si GlobeThrottle dejase de recibir estos mensajes, podr´ıa inferir que el nodo ThrottleNet que los emit´ıa ya no est´a conectado a la red (debido a un problema de conexi´on o similar). En ese caso, GlobeThrottle proceder´ıa a eliminar tanto el nodo como sus suscripciones a servicios de su base de datos. Naturalmente, tampoco enviar´ıa m´as keepalives hacia ese nodo. Keepalives de GlobeThrottle hacia los nodos ThrottleNet. Estos mensajes satisfacen las siguientes necesidades: •Dar a conocer a los nodos ThrottleNet la direcci´on de GlobeThrottle, permitiendo as´ı localizarlo. •Proporcionar mecanismos de resoluci´on de direcciones para tr´afico de tiempo real. Esto es as´ı porque los keepalives que GlobeThrottle env´ıa a cada nodo contienen, para cada conexi´on l´ogica de la que el nodo es una fuente, las direcciones MAC de los sumideros de la conexi´on. •Permitir a los nodos detectar si GlobeThrottle est´a conectado a la red o no. Si los nodos dejan de recibir keepalives de GlobeThrottle, deducir´an que GlobeThrottle ha dejado de funcionar, no puede acceder a la red o ha cambiado de direcci´on MAC. Entonces, los nodos volver´an a empezar a enviar peri´odicamente mensajes para encontrar a GlobeThrottle de nuevo. Mensajes encapsulando tr´afico de las capas superiores de la pila de protocolos como tr´afico NRT, para permitir as´ı a los usuarios emplear programas o utilidades de dichas capas (e.g. ping). Ambos tipos de keepalives sirven otro prop´osito: como se los env´ıa con la suficiente frecuencia, evitan que el conmutador olvide a qu´e nodo ThrottleNet conduce cada uno de sus puertos de salida. 47 Figura 4.2: Relaci´on de tiempos para cada una de las principales tareas del desarrollo del simulador. Figura 4.3: Relaci´on de tiempos para cada una de las principales tareas del desarrollo de ThrottleNet. 54 4.2. Tama˜no del proyecto Esgrimir cifas como el n´umero de l´ıneas de c´odigo de que constan las implementaciones del simulador y de ThrottleNet (13260 y 8691 l´ıneas de c´odigo1, respectivamente) carece de sentido, pues estas no ofrecen una clara indicaci´on de los esfuerzos realizados. Esto es especialmente cierto en el caso de la implementaci´on de ThrottleNet, por los siguientes motivos: La aproximaci´on incremental empleada hace dif´ıcil prever desde el principio c´omo se va a organizar todo el c´odigo. Cada paso de una iteraci´on a otra ha implicado grandes reestructuraciones de la aplicaci´on que no se ven reflejadas en la m´etrica de l´ıneas de c´odigo. Por ejemplo, la introducci´on de GlobeThrottle ha supuesto: •Cambiar parte de la l´ogica de env´ıo y recepci´on de mensajes para poder implementar la entrega en dos pasos (ver Secci´on 3.3.3). •Reescribir todas las funciones de tratamiento de mensajes (fragmentaci´on, reensamblado, planificaci´on de env´ıos, etc.) de manera que puedan funcionar de tanto en espacio de n´ucleo como en espacio de usuario. No es tan importante el n´umero de l´ıneas de c´odigo como su calidad: el dise˜no no tiene por qu´e ser extenso, pero ha de ser adecuado para cooperar con el c´odigo de gesti´on de redes (networking) del n´ucleo, para permitir utilizar puentes Linux, etc. As´ı, el an´alisis y formaci´on tecnol´ogica suponen una parte muy importante de los esfuerzos realizados (ver Figura 4.3), pero no se ven reflejados en las l´ıneas de c´odigo escritas. Por ´ultimo, desarrollar ThrottleNet en C como una aplicaci´on de espacio de n´ucleo ha permitido lograr una gran eficiencia a costa de un mayor tiempo de implementaci´on y depurado, que tampoco se ve reflejado por la cantidad de l´ıneas de c´odigo escritas. As´ı, lo m´as apropiado no es medir el software implementado en cuanto a su longitud en l´ıneas de c´odigo sino en cuanto a su complejidad y a la funcionalidad que brinda. Pese a no emplear ninguna m´etrica de punto funci´on, es f´acil apreciar la cantidad de funcionalidad implementada si se considera el amplio n´umero de posibilidades de aplicaci´on que el software ofrece; recordemos que proporciona una infraestructura de comunicaci´on en tiempo real 1Se ha empleado el contador de l´ıneas de c´odigo disponible en http://code.google. com/p/loc-calculator/. 55 para la comunicaci´on de casi cualquier sistema distribuido a t´erminos muy asequibles (en cuanto al hardware ysoftware que requiere para funcionar). En cualquier caso, para conocer el tama˜no del proyecto en s´ı y la cantidad de esfuerzos que ha supuesto, tambi´en se han de considerar factores como la carga de formaci´on necesaria para poder acometerlo, dificultades de implementaci´on y depurado, etc. 56 Cap´ıtulo 5 Conclusiones Esta ´ultima secci´on cierra la memoria presentando las contribuciones de este proyecto, evaluando el cumplimiento de los requisitos funcionales planteados al inicio del mismo (ver Secci´on 1.1), detallando las l´ıneas de trabajo futuro que deja abiertas y con un resumen de la experiencia del proyectando. 5.1. Contribuciones Como ya se dijo en la Secci´on 1, la Universidad de Lund proyecta ofrecer una soluci´on a la comunicaci´on en tiempo real utilizando LabComm y ThrottleNet. En el momento de iniciar este proyecto, la Universidad de Lund ya ofrec´ıa una implementaci´on de LabComm para su uso, pero esta no era ampliamente aceptada dado que no transmit´ıa con claridad las posibilidades de aplicaci´on que ofrec´ıa a sus potenciales usuarios. Se hab´ıa esbozado tambi´en una especificaci´on del protocolo ThrottleNet, pero todav´ıa no se dispon´ıa de una implementaci´on. Este proyecto trata de contribuir a la soluci´on planteada por la Universidad de Lund proporcionando medios para facilitar el aprendizaje del manejo de LabComm y desarrollando una implementaci´on de ThrottleNet a partir de la especificaci´on proporcionada. Dado que la mejor manera de familiarizarse con una tecnolog´ıa es experimentar con ella, este proyecto ha optado por desarrollar un simulador que permita experimentar con LabComm, para as´ı comprender qu´e puede hacer y c´omo ha de usarse. Este simulador se ha concebido como una herramienta educativa y su simplicidad de uso ha sido uno de los principales objetivos de dise˜no. Como las aplicaciones que requieren de un gran esfuerzo de configuraci´on para poder ser usadas tienden a disuadir a los usuarios de emplearlas, esta herramienta de simulaci´on ha sido dise˜nada 57 como una aplicaci´on web para liberar al usuario de cualquier esfuerzo de configuraci´on. La aplicaci´on presenta un escenario en que un productor y un consumidor intercambian datos empleando LabComm. El usuario puede modificar el c´odigo fuente de la simulaci´on (i.e. el productor, el consumidor y el protocolo que define los datos transmitidos durante la simulaci´on) a su gusto, siendo as´ı capaz de alcanzar un elevado grado de personalizaci´on para cada simulaci´on. Esta aplicaci´on puede ser accedida a trav´es de: http://vm15.cs.lth.se: 2323/labCommDemo/Client.html. Adem´as, se ha desarrollado una implementaci´on de ThrottleNet. Esta permite hacer uso de redes de comunicaci´on en tiempo real que son f´acilmente desplegables en una variedad de entornos gracias a que sus requisitos de funcionamiento son muy asequibles. En concreto, estas redes requieren del uso de un conmutador y enlaces full-duplex, utilizados para conectar a los nodos utilizando una topolog´ıa de estrella (ver Secci´on 3). La implementaci´on proporcionada (como driver Linux) permite una sencilla instalaci´on en casi cualquier distribuci´on Linux y, con ligeros cambios, en sistemas operativos del tipo UNIX, Mac OS, Windows, etc (ver Secci´on 5.3.3). 5.2. Cumplimiento de objetivos Se examinan ahora los requisitos funcionales planteados para el simulador y la implementaci´on de ThrottleNet y se explica si han sido cumplidos o no, c´omo y qu´e problemas se han encontrado en el camino. 5.2.1. El simulador Req. 1.1: cumplido, pues la aplicaci´on funciona como se deseaba: el usuario puede seguir un proceso iterativo de modificaci´on y ejecuci´on de las simulaciones hasta alcanzar los resultados deseados. Como ya se ha explicado, para conseguir esto hubo que dar soluci´on al problema de recargar las clases din´amicamente utilizando un applet no firmado (ver Secci´on 2.6.2). Req. 1.2: cumplido. La implementaci´on como aplicaci´on web libera al usuario de las tareas de configurar internamente el simulador (e.g. configurar los compiladores, cargar las clases, etc). La Secci´on 1.2.1 explica esto con mayor detalle. 58 5.2.2. La implementaci´on de ThrottleNet Req. 2: cumplido, pues ThrottleNet tan s´olo requiere de enlaces fullduplex y de un conmutador y puede ser desplegado en pr´acticamente cualquier distribuci´on Linux (ver Secciones 1 y 1.2.2). Req. 2.1: cumplido, porque se ha determinado que los nodos Throttle- Net no se comuniquen directamente a trav´es de los enlaces f´ısicos sino que deban solicitar permiso para establecer conexiones l´ogicas para poder comunicarse entre ellos (ver Secci´on 3). Req. 2.2: cumplido, gracias a que la implementaci´on fragmenta los mensajes salientes y los env´ıa respetando los per´ıodos de espera entre transmisiones. A la recepci´on de los fragmentos, estos se ensamblan para formar el mensaje original (ver Secci´on 3.2). Req. 2.3: cumplido, porque cada nodo ThrottleNet cuenta con una conexi´on NRT por defecto y utiliza GlobeThrottle para resolver las direcciones de sus mensajes (ver Secci´on 3.3.3). Req. 2.4: cumplido, pues los nodos ThrottleNet pueden establecer conexiones l´ogicas de tiempo real a medida. GlobeThrottle proporciona resoluci´on de direcciones para tr´afico RT (ver Secci´on 3.4.1). Req. 2.5: cumplido, gracias a los keepalives que GlobeThrottle y el resto de nodos intercambian. Estos mensajes contienen toda la informaci´on necesaria para comunicar con GlobeThrottle, obtener direcciones de los destinatarios de tr´afico RT, determinar qu´e agentes de la red contin´uan conectados, etc. Todos estos mensajes se env´ıan a trav´es del canal NRT, puesto que no tienen requisitos de entrega de tiempo real (ver Secci´on 3.4.2). Req. 2.6: cumplido. Este informe es producido y mostrado por GlobeThrottle de forma peri´odica y tambi´en cada vez que se establecen/eliminan conexiones l´ogicas en la red. La Figura 3.4 muestra uno de estos informes. 59 5.3. Trabajo futuro Aqu´ı se describen posibles mejoras al simulador y a la implementaci´on de ThrottleNet realizados. Adem´as, con base en los resultados obtenidos de este proyecto, se presentan los pasos a seguir para finalmente obtener la soluci´on integrada proyectada. 5.3.1. Posibles mejoras para el simulador Aunque LabComm permite generar las rutinas de serializaci´on en varios lenguajes, esta primera versi´on del simulador tan s´olo permite ejecutar simulaciones en lenguaje Java. A˜nadir la funcionalidad necesaria para soportar m´as lenguajes queda como tarea pendiente para futuras versiones de esta herramienta de simulaci´on. 5.3.2. Posibles mejoras para ThrottleNet Esta implementaci´on de ThrottleNet emplea un GlobeThrottle para las tareas de gesti´on de ancho de banda y resoluci´on de direcciones. Una desventaja de esta aproximaci´on es que convierte a GlobeThrottle en un punto singular de fallo de la red ThrottleNet[28]: si falla o no funciona correctamente, no se podr´a transmitir tr´afico RT ni NRT en la red. Sin embargo, GlobeThrottle permite encapsular tr´afico NRT en la red, lo cual posibilita utilizar protocolos de las capas de Internet y Transporte sobre ThrottleNet. Ser´ıa deseable encontrar una soluci´on que incrementase la fiabilidad de la red (i.e. la tolerancia frente a fallos de GlobeThrottle) al tiempo que mantenemos la capacidad de soportar tr´afico NRT. Esto podr´ıa hacerse utilizando dos o m´as GlobeThrottles: uno activo y uno o varios redundantes, que se mantienen actualizados respecto del estado de la red analizando los keepalives enviados por el GlobeThrottle activo. El GlobeThrottle activo llevar´ıa a cabo las tareas propias de GlobeThrottle y, en caso de fallo (que podr´ıamos detectar en caso de dejar de recibir los keepalives de GlobeThrottle), uno de los redundantes lo sustituir´ıa. Sin embargo, esta aproximaci´on s´olo ser´a efectiva si GlobeThrottle no se comporta incorrectamente antes de fallar por completo, ya que de lo contrario los GlobeTrottle redundantes habr´an analizado keepalives con informaci´on falsa y tendr´an por tanto una idea err´onea del estado real de la red. 60 5.3.3. Pasos hacia la soluci´on integrada Finalmente, enumeraremos las ampliaciones necesarias para alcanzar la soluci´on integrada deseada: 1. Proporcionar una implementaci´on de ThrottleNet para aquellos otros sistemas operativos que podamos querer utilizar. En un laboratorio de rob´otica no es raro encontrar sistemas como Xenomai y VxWorks. Portar ThrottleNet a ´estos ser´ıa sencillo dada la actual implementaci´on como driver. Para otros sistemas, como Windows, UNIX, etc, podr´ıamos utilizar una implementaci´on en espacio de usuario similar a la utilizada con GlobeThrottle (ver Secci´on 3.3). 2. Crear una capa de abstracci´on que facilite el trabajo con las interfaces de red ofrecidas al usuario (eth rt y eth nrt). Como medida provisional, se ha proporcionado un programa para que el usuario pueda solicitar la creaci´on y eliminaci´on de conexiones l´ogicas, adem´as de enviar y recibir datos a trav´es de estas. El c´odigo de esta aplicaci´on est´a contenido en el Ap´endice B.3. 3. Asociar a las conexiones l´ogicas de tiempo real una definici´on de los tipos de datos que ser´an enviados a trav´es de ellas (el protocolo LabComm), consiguiendo as´ı la previamente mencionada abstracci´on de “servicio”. 5.4. Experiencia personal Este proyecto me ha resultado una experiencia muy grata, en cuanto a que me ha permitido poner en pr´actica los conocimientos adquiridos durante la carrera, experimentar con nuevas formas de desarrollar software y, sobre todo, aprender muchas cosas nuevas. Adem´as, me ha brindado la posibilidad de trabajar con gente realmente capaz y brillante, y de aprender de su manera de afrontar los problemas t´ecnicos, por desesperantes que puedan llegar a ser. 61 62 Ap´endice A La herramienta de simulaci´on A.1. Transferencia de ficheros mediante ObjectStreams Esta secci´on contiene el c´odigo empleado para transferir ficheros de c´odigo fuente entre el cliente y el servidor del simulador. /** * Class FileTransfer. * Provides methods for transferring text files. */ package common; import java.io.*; import java.util.Arrays; public class FileTransfer { private static final int BUFFER_SIZE = 256; /** * Returns a String containing all text in the requested file. This * file is read from the socket which connects to the server. */ public String receiveFile(ObjectInputStream in) throws IOException, ClassNotFoundException, Exception { 63 A.3. Common mistakes when using the simulator There is a series of possible common mistakes which may be made when trying to add new samples to encode and decode in an existing system. Since these mistakes are located at either the producer or the consumer source code, this quick guide has been divided into two such sections. A.3.1. Producer code Try to encode something with a non-existent encoder: Compilation fault. Example: theEncoder.encodeIt(”foo bar”); Try to encode something with an unappropriate encoder. Different possibilities follow... 1. Use an encoding routine whose parameter type does not match the type of the sample you try to encode: Compilation fault. Example: theEncoder.handleABoolean(5); 2. Use an encoding routine whose parameter type does not match the type of the sample you try to encode, but automatic conversion is allowed: Permitted and should work fine. Example: theEncoder.handleADouble(5); Example: theEncoder.handleADouble((double) 5); 3. Try to encode a vector sample using an encoding routine which accepts as a parameter a vector of the same type but has a different number of components. If the size of the vector to be encoded is •bigger than that expected, there will be no compile or runtime failures but (attention!) the latter components of the vector will not be sent. •smaller than that expected, an exception will be thrown while traversing the vector. In the providen implementation of the Producer this exception is caught and properly treated. Have encoder routines with the same name. 70 •If they are declared to encode the same type of sample, that is, accept the same type of parameter, then there will be a compilation fault, due to the fact that there are duplicated symbols. Example: public void encodeADouble(double value) {...} public void encodeADouble(double v) {...} •If they are declared to encode samples of different types, that is, accept different types of parameters then there is no problem at all; the method which has not been declared in the Handler of the class which encoder routine we want to use will be ignored. A.3.2. Consumer code First, there is no possibility to decode a sample using a decoder routine which is not suitable for such a sample. The reason is that the sample messages are self-descriptive and thus know which routine they need to use to be decoded1. Thus, either the decoder routine has not been properly registered to the decoder or there are duplicated routines or any other kind of error which may cause compilation faults. For the case in which there are decoder routines with the same name, the same described at the Producer section applies. Should there not be a routine registered, the causes for that may be any of the following: 1. Cause A: The decoder is not declared to implement the handling routine for the class which has samples communicated through the channel. That is, the implements clause is missing. Example: class MyDecoder implements aBoolean.Handler is missing. 2. Cause B: The decoder does not register the decoder routine. Example: The statement ABoolean.register(dChannel, this); is missing. 3. Cause C: The decoder routine is missing, that is, there is no implementation for it. And, depending on the combination of causes A, B and C the result may be one of those shown in table A.1 (Pwill stand for ”The sentences which would avoid cause X from happening are present.and Mfor ”the statements that would avoid cause X from happening are missing”). 1The routine which implements the Handler declared at that class. 71 A B C Consequence M M M IOException: No dispatcher for ABoolean M M P IOException: No dispatcher for ABoolean M P M Compilation fault: Can not find method handleABoolean M P P Compilation fault: Can not find method ABoolean.register() P M M Compilation fault: Method handleABoolean is not overriden P M P IOException: No dispatcher for ABoolean P P M Compilation fault: Can not find method handleABoolean P P P OK Cuadro A.1: Different outcomes depending on proper registration of the decoder routines. Finally, if the decoder would not be attached to a communication channel, an exception with the message ”size of next message expected”would be thrown. Example:dChannel = new LabCommDecoderChannel(); instead of dChannel = new LabCommDecoderChannel();. 72 Ap´endice B La implementaci´on de ThrottleNet B.1. Racionamiento de tr´afico B.1.1. Procesamiento de tr´afico saliente /* Transfer a message using the ThrottleNet protocol. * @dst_addr: receiver of the message * @msg_type: throttleNet type of the message * @service_id: id of the logical connection * @skb: Linux networking structure. Contains the message * @outgoing: structure for outgoing traffic. Its lock/semaphore * has been taken before calling this function. */ tn_tx(dst_addr, msg_type, service_id, skb, outgoing) { // Allow no traffic until GlobeThrottle has been found. if (not_known(globeThrottle_address)) { drop_message(skb); return; } /* If device is being used, enqueue the message to be * sent later on. */ if (being_used(outgoing)) { enqueue_msg(outgoing, dst_addr, skb, msg_type, service_id); return; } 73 // Else split it into fragments and schedule their transmission else { split_msg(dst_addr, skb.msg, msg_type, service_id, outgoing); mark_used(outgoing); start_tx_timer(outgoing); } } /* This function is triggered whenever a transmission timer is * activated at an outgoing structure. * It performs a delayed transmission of a fragment and schedules * transmission of the remaining fragments for that message. * Should there be no more fragments to be sent, it will split a * message pending to be sent into fragments and schedule * their transmission. * If nothing is to be sent, marks the outgoing structure as not * being used. * @timer: timer which triggered this function. */ send_delayed_fragment(timer) { var remaining = 0, successful, scheduled = false; var skb, dst_addr, msg_type, service_id; // Get outgoing with some offset calculations and lock it outgoing = container_of(timer); take_lock(outgoing); // If there are fragments to send, send one if (get_fragments_to_send(outgoing) > 0) { remaining = send_enqueued_fragment(outgoing); } /* If there are still fragments to send, restart the * transmission timer. */ if (remaining > 0) { start_tx_timer(outgoing); scheduled = true; } else { 74 /* No more fragments to send. If there are enqueued messages * split them into fragments and schedule their transmission. */ <skb, dst_addr, msg_type, service_id> = get_awaiting_msg(outgoing); if (skb != NULL) { split_msg(dst_addr, skb->msg, msg_type, service_id, outgoing); start_tx_timer(outgoing); scheduled = true; } // If there are no fragments to send... if (!scheduled) { mark_unused(outgoing); } return_lock(outgoing); } B.1.2. Procesamiento de tr´afico entrante /* Process an incoming fragment under the assumption that all * fragments arrive in order. * @msg: a newly arrived fragment * @incoming: structure in which the incoming fragments will * be stored until reassembling takes place. * @handle_packet: function to call when the packet has * been reassembled. * Different types of messages may require * different handling * All appropriate locks must be held before calling this * function. */ void handle_incoming_msg(msg, incoming, handle_message) { var latest_frag_no, latest_total_frags; var enqueue_it = false, flush_assembly_queue = false; /* Get the fragment’s fragment number and total number * of fragments in which the message is fragmented. */ latest_frag_no = get_fragment_number(msg); lastest_total_frags = get_total_frags(msg); /* Possible cases...*/ 75 // 1st fragment arrives, assembly queue is empty if (empty_assembly_queue(incoming) && is_first_fragment(msg)) { enqueue_it = true; } // 1st fragment arrives, assembly queue not empty... else if (!empty_assembly_queue(incoming) && is_first_fragment(msg)) { enqueue_it = true; flush_assembly_queue = true; } // The fragment which we expected arrives... else if (!empty_assembly_queue(incoming) && latest_frag_no == expected_frag_no(incoming) && latest_total_frags == expected_total_frags(incoming)) { enqueue_it = true; } // We don’t want the packet. else { flush_assembly_queue = true; } // Enqueue the fragment if (enqueue_it) { enqueue_fragment(incoming, msg); // Have we received all fragments? if (latest_frag_no == expected_total_frags(incoming)) { assemble_message(incoming); handle_message(incoming); clean_assembly_buffer(incoming); // Flush the assembly queue. flush_assembly_queue = true; } } // Flush the assembly queue, if needed if (flush_assembly_queue) { discard_packets_to_assemble(incoming); } } 76 B.2. Gesti´on de ancho de banda Toma de decisiones para la aceptaci´on de establecimiento de conexiones. /* Calculate no of bytes that this service will send/receive * using the link in one second. * @freq: inter-fragment gap for the connection (nsecs). * @size: max bytes per fragment sent using this connection */ double bw_link_per_service(freq, size) { var no_messages, bw; no_messages = 1E9 / freq; bw = no_messages * size; return bw; } /* Calculate no of bytes that this node sends/receives over * the link because of its connections to other ThrottleNet * nodes. * @mac: mac address of the node */ double bw_link_per_node(mac) { double bw = 0.0, service_bw; var source_for_list, sink_for_list; // Lists of services // Get all services for which the node is source/sink source_for_list = get_services_for_which_this_node_is_a_source(); sink_for_list = get_services_for_which_this_node_is_a_sink(); /* We will send unicasts to all receivers of connections for which * we are sources. The required bandwidth will depend on the * connection specifics (size and frequency) and on the number of * nodes subscribed to that connection. */ foreach (service in sink_for_list) { service_bw = bw_link_per_service(service.freq, service.size); no_sinks = service.no_sinks; 77 bw += no_sinks * service_bw; } /* We will receive messages from all connections to which we are * subscribed as receivers if there is a source defined for that * connection. */ foreach (service in source_for_list) { service_bw = bw_link_per_service(service.freq, service.size); bw += service.source_is_defined() ? service_bw : 0; } // Do not forget to account bandwidth reserved for NRT bw += NRT_RESERVED_BW; return bw; } /* Calculate how many bytes will be stored in the output * buffers of the switch in case of simultaneous reception * of messages from connections of which this node is a * receiver. */ double bw_buff_per_node(mac) { double bw = 0.0; var sink_for_list; sink_for_list = get_services_for_which_this_node_is_a_sink(); foreach (service in sink_for_list) { // Just consider it if the service source is defined if (service.source_is_defined) { bw += service.size; } } return bw; } /* Check if this service requirements can be satisfied given the * current state of the network. Particularly, this function * performs checks to avoid overloading the links between the nodes 78 * and the output buffers of the switch (in case of simultaneous * reception of messages from several services) * @mac: mac address of the node subscribing the service. * @id: service id. * @freq: service frequency. * @size: service max packet size. * @is_src: is the node registering as a source? (boolean) * @used_link_bw: already allocated link bandwidth for this * node. * @used_buff_bw: already allocated space at the switch output * buffer. * * Returns true if registering the service is feasible in the * current situation and false otherwise. */ boolean bw_check_viability(mac, id, freq, size, is_src, used_link_bw, used_buff_bw) { boolean abort = false; var service, service_sinks, service_source; var extra_link_bw = 0, extra_buff_bw = 0, new_link_load, sink_link_bw, sink_buff_bw; /* If the service already exists in GlobeThrottle’s database, * check how accepting this request affects the nodes related * to the service. */ if ((service = find_service(database, id)) != NULL) { new_link_load = bw_link_per_service(freq, size); /* Check: 1) Is there any node which has no available bandwidth * to meet this service requirements? * 2) Can we (along with the other sources of services * to which the node is subscribed) overload the output * buffer for any of the sinks of this service in case * all the service messages would arrive simultaneously? */ if (is_src) { service_sinks = get_sinks_for_this_service(service); foreach (sink in service_sinks) { sink_link_bw = bw_link_per_node(sink.mac); sink_buff_bw = bw_buff_per_node(sink.mac); // Check 1) 79 default: error_found = 1; break; } } break; // Help case ’h’: printf("Provide a command and all necessary arguments.\n"); printf("Commands:\n"); printf("\tInstall a service (i). Requires id, freq, size and " "description\n"); printf("\tDelete a service (d). Requires id.\n"); printf("\tRead from a service (r). Requires id.\n"); printf("\tWrite to a service (w). Requires id and msg.\n"); printf("Possible arguments (comma separated):\n"); printf("\tService id (id=)\n"); printf("\tService frequency (freq=)\n"); printf("\tService message size (size=)\n"); printf("\tService description (desc=)\n"); printf("\tMessage to write (msg=)\n"); break; // Wrong argument default: fprintf(stderr, "Unvalid argument %s\n", optarg); break; } } if (error_found || argc == 1) { fprintf(stderr, "\nType ioctler -h for help\n"); return -1; } switch (mode) { case ’c’: if (cid < 0 || cfreq < 0 || csize < 0 || desc == NULL) { fprintf(stderr, "Invalid arguments\n"); 86 return -1; } else { id = (__u16) cid; freq = (__u32) cfreq; size = (__u16) csize; } break; case ’d’: if (cid < 0) { fprintf(stderr, "Invalid arguments\n"); return -1; } else { id = (__u16) cid; } break; case ’w’: if (cid < 0 || msg == NULL) { fprintf(stderr, "Invalid arguments\n"); return -1; } else { id = (__u16) cid; } break; case ’r’: if (cid < 0) { fprintf(stderr, "Invalid arguments\n"); return -1; } else { id = (__u16) cid; } break; } // Set up everything and link to the device 87 if (!init()) exit(1); switch (mode) { case ’c’: printf("start service (id = %u, freq = %u, size = %u," "desc = %s, desc_len= %d, is_src = %d)\n", id, freq, size, desc, strlen(desc), is_src); start_service(id, freq, size, desc, strlen(desc), is_src); break; case ’d’: printf("stop service (id = %u)\n", id); stop_service(id); break; case ’w’: printf("write msg (id = %u, msg = %s, len = %d)\n", id, msg, strlen(msg)); write_msg(id, msg, strlen(msg), eager); break; case ’r’: printf("read msg (id = %u, eager = %s)\n", id, eager ? "true" : "false"); read_msg(id, eager); break; } close(fd); } int init(void) { int result; struct ifreq ifr; struct sockaddr_ll sa; fd = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (fd < 0) { 88 perror("socket"); goto socket_failed; } strncpy(ifr.ifr_name, name, IFNAMSIZ); if (ioctl(fd, SIOCGIFINDEX, &ifr) < 0) { perror("ioctl"); goto get_index_failed; } sa.sll_family = AF_PACKET; sa.sll_ifindex = ifr.ifr_ifindex; sa.sll_protocol = htons(ETH_P_ALL); if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { perror("bind"); goto bind_failed; } result = 1; goto out; bind_failed: get_index_failed: close(fd); socket_failed: result = 0; out: return result; } int start_service(__u16 s_id, __u32 s_freq, __u16 s_data, char *s_desc, int desc_len, int s_is_src) { struct ifreq ifr; struct ioctl_service_desc *my_desc; my_desc = (struct ioctl_service_desc *) malloc(sizeof (struct ioctl_service_desc)); if (my_desc == NULL) { perror("service registration"); 89 return 0; } // Fill in the service descripton. my_desc->id = s_id; my_desc->is_src = s_is_src; my_desc->freq = s_freq; my_desc->size = s_data; my_desc->desc_len = desc_len; printf("max_desc_len = %d\n", MAX_DESC_LEN); memcpy(my_desc->desc, s_desc, (desc_len < MAX_DESC_LEN) ? desc_len : MAX_DESC_LEN); // Pass it to the device strncpy(ifr.ifr_name, name, IFNAMSIZ); ifr.ifr_data = (void *) my_desc; if (ioctl(fd, SERVICE_REGISTRATION, &ifr)) { perror("ioctl"); return 0; } else printf("Successful registration of service %u!\n", my_desc->id); free(my_desc); return 1; } int stop_service(__u16 s_id) { struct ifreq ifr; struct ioctl_service_desc *my_desc; my_desc = (struct ioctl_service_desc *) malloc(sizeof (struct ioctl_service_desc)); if (my_desc == NULL) { perror("service registration"); return 0; } // Fill in the service descripton. 90 memset(my_desc, 0, sizeof(struct ioctl_service_desc)); my_desc->id = s_id; // Pass it to the device strncpy(ifr.ifr_name, name, IFNAMSIZ); ifr.ifr_data = (void *) my_desc; if (ioctl(fd, SERVICE_DELETION, &ifr)) { perror("ioctl"); return 0; } else printf("Successful removal of service %u!\n", my_desc->id); free(my_desc); return 1; } void write_msg(__u16 service_id, __u8 *msg, unsigned int msg_len, int eager) { int copied, i, messages = 10; struct ifreq ifr; struct ioctl_rt_msg *irm; irm = (struct ioctl_rt_msg *) malloc(sizeof (struct ioctl_rt_msg)); if (irm == NULL) { perror("write message"); return; } // Prepare a write request do { memset(irm->msg, 0, ETH_MTU); if (eager) { for (i = 0; i < 10; i++) { irm->msg[i] = i; } irm->msg_len = 10; messages--; } 91 else { memcpy(irm->msg, msg, msg_len); irm->msg_len = msg_len; } irm->id = service_id; // Pass it to the device strncpy(ifr.ifr_name, name, IFNAMSIZ); ifr.ifr_data = (void *) irm; ioctl(fd, RT_WRITE, &ifr); } while (eager && messages > 0); free(irm); } void read_msg(__u16 service_id, int eager) { int i; struct ifreq ifr; struct ioctl_rt_msg *irm; struct timespec tp, tp2; irm = (struct ioctl_rt_msg *) malloc(sizeof (struct ioctl_rt_msg)); if (irm == NULL) { perror("read msg"); return; } // Prepare a read petition memset(irm->msg, 0, ETH_MTU); irm->msg_len = 0; irm->id = service_id; // Pass it to the device strncpy(ifr.ifr_name, name, IFNAMSIZ); ifr.ifr_data = (void *) irm; i = 0; while (eager) { clock_gettime(CLOCK_MONOTONIC, &tp); 92 ioctl(fd, RT_READ, &ifr); if (irm->successful) { clock_gettime(CLOCK_MONOTONIC, &tp2); printf("-------------------------\n"); printf("[%lf]Received a message from service %u\n", ((double) tp2.tv_nsec) - ((double) tp.tv_nsec), irm->id); for (i = 0; i < irm->msg_len; i++) printf("%u", irm->msg[i]); printf(".\n-------------------------"); i++; eager = (i == 10) ? 0 : 1; } } free(irm); } 93 94 Ap´endice C El sistema LabComm C.1. Componentes del sistema LabComm El sistema LabComm se compone de tres elementos: Un lenguaje de declaraci´on de datos, que permite declarar tipos de datos simples y compuestos de la misma manera en que lo hace un lenguaje de programaci´on (ver Secci´on C.2). Este lenguaje se emplea para especificar los tipos de datos que se van a intercambiar durante la comunicaci´on. A la especificaci´on de los tipos de datos que se van a transmitir se la denomina “protocolo”. Un protocolo binario, que especifica c´omo han de traducirse los tipos de datos simples a secuencias de bytes y c´omo recuperar los datos originales a partir de estas secuencias de bytes (serializaci´on). Puesto que los tipos de datos compuestos se obtienen a partir de la agregaci´on de tipos de datos simples, esta especificaci´on tambi´en permite serializar estructuras de datos compuestas. Un compilador que genera rutinas de serializaci´on en diversos lenguajes de programaci´on1a partir de un protocolo. Estas rutinas realizan la conversi´on de los tipos de datos (tal y como est´an representados en esos lenguajes de programaci´on) a secuencias de bytes y viceversa. C.2. El lenguaje LabComm Esta secci´on detalla los tipos de datos que ofrece el lenguaje LabComm para especificar un protocolo de comunicaci´on. 1Java, C, C# y Python est´an soportados 95 This will generate the following classes1in the directory javaRoutines: a.java,b.java,SimpleSample.java and SimpleType.java. The files a.java,b.java and SimpleSample.java have practically the same content: An interface specification for their handlers. Methods to register a LabCommDecoder (to decode that type of data). Methods to register a LabCommEncoder (to encode that type of data). As classes aand bare related to primitive data types (namely booleans and bytes) they do not need any other routines to encode or decode their data than those already provided in the LabComm libraries (remember the previously defined binary protocol). Nevertheless, the file “example.lc” defines a data type which is an aggregate type (a struct). Since there are no routines already implemented to encode/decode structs, the LabComm compiler will create the class SimpleType to provide with the proper encoder/decoder routines. However, the programmer will never work directly with this class, but with the SimpleSample class. You can generate all these classes feeding the protocol provided in Appendix D.4 to the LabComm system, available at http://vm15.cs.lth.se: 2323/labCommDemo/labcomm2.zip. D.3. Step two: Use the marshalling routines If you are planning to work with Eclipse, it might be a good idea to separate the LabComm libraries and generated routines from your application classes. You could do this by creating two different packages, one for your own application classes and the other one for the automatically created routines. Then, you can import these routines from your application. The generated routines will have a number of import clauses, for example: import se.lth.control.labcomm.LabCommDecoder; import se.lth.control.labcomm.LabCommDispatcher; import se.lth.control.labcomm.LabCommEncoder; 1Please note that this is what would happen if you used the protocol definition contained in Appendix D.4. This is the file we assume to be using from now on. 102 import se.lth.control.labcomm.LabCommHandler; import se.lth.control.labcomm.LabCommSample; In order to help Eclipse find these packages, you should tell it to import the library folder as a source folder. You can do this by navigating through the folder which contains all LabComm material, entering the “lib” folder and choosing the “java” folder inside. Check Figure D.2 to see how the examples in this tutorial have been structured. There are several elements surrounded by different coloured boxes in Figure D.2. Elements in the blue box are part of the LabComm library. The red and green boxes surround not only the applications written by the programmer himself but also the routines generated by LabComm. Red box elements belong to the first example and green elements to the second one. We will now describe how elements of data type a(in Example 1) are encoded and decoded by the Sender and Receiver applications respectively. These two programs will communicate using a File as a stream. Please note that the code implementing the agents named below is available at http://vm15.cs.lth.se:2323/labCommDemo/Client.html. The Sender will first create a FileOutputStream directed to a File. Then, it will associate it to an object of the class LabCommEncoderChannel, which provides an implementation for the interface LabCommEncoder. The next step is to register this channel to any classes which we want to encode (in this case class a) and invoke the static method encode in those classes whenever we want to encode one of their objects. The Receiver will first create a FileInputStream to read from a File. Then, it will associate it to an object of the class LabCommDecoderChannel, which provides an implementation for the interface LabCommDecoder. The next step is to register this channel to any classes which we want to decode (in this case class a) together with a specific handler for each class. Those handlers have to implement the interface specified in each class that we want to decode. Finally, whenever we want to decode the data on the stream handled by the LabCommDecoderChannel we will simply call the run method in the class LabCommDecoderChannel and it will decode any data in the stream. Since we already registered to this channel all handlers needed to decode the data which is in the stream, this process is performed automatically. 103 Figura D.2: Organization of the packages in the tutorial. Another example has been written to illustrate how LabComm ought to be used in a client-server architecture (i.e. the stream would now be a socket). For that example, the encoder/decoder routines would be generated in the same way we did before: create a directory to host the files, run the Lab- Comm compiler on the protocol file and writing an application which uses the routines as explained before. The applications for this new example have been written in a different way; wrapping all registrations of the classes to encode/decode (and their hand- 104 lers) to the communication channel into inner classes, namely MyEncoder and MyDecoder. Final observation: whenever the method run of the LabCommDecoderChannel is called, a careful exception handling is required. The reason is that this method will read from a stream until the end of that stream is reached, which will cause a EOFException (End Of File Exception) to be thrown. A catch clause which does nothing is required for this exception, since it will always be thrown at the end of a succesful communication. D.4. Protocol definition for Example 1 typedef struct { int x; int y; string name; float data[2][_,_]; } SimpleType; sample SimpleType SimpleSample; sample boolean a; sample byte b; 105 Bibliograf´ıa [1] Lund Institute of Technology, Lund University, A LabComm wiki.http: //torvalds.cs.lth.se/moin/LabComm. [2] A. Blomdell, K.-E. Arzen, and A. Martinsson, ThrottleNet: Hard realtime communication over switched Ethernet. [3] “Serializaci´on.” Wikipedia. http://es.wikipedia.org/wiki/ Serializaci%C3%B3n. [4] “Java applets.” Sun Developer Network. http://java.sun.com/ applets/. [5] “The java tutorials: Applets.” http://download.oracle.com/javase/ tutorial/deployment/applet/. [6] “What applets can and cannot do.” http://download.oracle.com/ javase/tutorial/deployment/applet/security.html. [7] “The java tutorials: Creating a gui using swing.” http://download. oracle.com/javase/tutorial/uiswing/. [8] “Rtnet - hard real-time networking for real-time linux.” RTnet Development Team. www.rtnet.org/. [9] “Xenomai - real-time framework for linux.” http://www.xenomai.org/ index.php/Main_Page. [10] “Real-time application interface for linux.” https://www.rtai.org/. [11] “Flow control.” Wikipedia, the free Encyclopedia. http://en. wikipedia.org/wiki/Flow_control. [12] “Vxworks: real-time operating system for embedded systems.” http: //en.wikipedia.org/wiki/VxWorks. 107 [13] A. Rubini, J. Corbet, and G. Kroah-Hartman, Linux Device Drivers. O’Reilly, 2005. [14] “Monitors (synchronization).” http://en.wikipedia.org/wiki/ Monitor_%28synchronization%29. [15] “The java tutorials: Objectstreams.” http://download.oracle.com/ javase/tutorial/essential/io/objectstreams.html. [16] “Serializable (java 2 platform se v1.4.2).” Oracle. http://download. oracle.com/javase/1.4.2/docs/api/java/io/Serializable.html. [17] T. Greanier, Java Serialization. Sun Developer Network. http: //java.sun.com/developer/technicalArticles/Programming/ serialization/. [18] “The java tutorials: Arrays.” http://download.oracle.com/javase/ tutorial/java/nutsandbolts/arrays.html. [19] “The instanceof keyword (java).” http://www.java2s.com/Tutorial/ Java/0060__Operators/TheinstanceofKeyword.htm. [20] “Localizador uniforme de recursos (url).” Wikipedia, la enciclopedia libre. http://es.wikipedia.org/wiki/Localizador_uniforme_de_ recursos. [21] “Java classloaders.” Oracle. http://download.oracle.com/javase/1. 4.2/docs/api/java/lang/ClassLoader.html. [22] J. Jenkov, “Java reflection: Dynamic class loading and reloading.” http://tutorials.jenkov.com/java-reflection/ dynamic-class-loading-reloading.html. [23] “Java reflection.” Sun Developer Network. http://java.sun.com/ developer/technicalArticles/ALT/Reflection/. [24] “Address resolution protocol (arp).” Wikipedia, the free Encyclopedia. http://en.wikipedia.org/wiki/Address_Resolution_Protocol. [25] The Linux Foundation, Linux bridges.www.linuxfoundation.org/ collaborate/workgroups/networking/bridge. [26] “Semaphores (synchronization).” Wikipedia, the free encyclopedia. http://en.wikipedia.org/wiki/Semaphore_%28programming%29. 108 [27] “Spinlocks (synchronization).” Wikipedia, the free encyclopedia. http: //en.wikipedia.org/wiki/Spinlock. [28] G. Mathiason and M. Amirijoo, “Real-time communication through a distributed resource reservation approach,” Master’s thesis, University of Skoevde, 2004. [29] “Universal tun/tap driver.” http://en.wikipedia.org/wiki/TUN/ TAP. [30] “Universal tun/tap driver: Faq.” http://vtun.sourceforge.net/tun/ faq.html. [31] “Ioctl: Input output control.” Wikipedia, the free encyclopedia. http: //en.wikipedia.org/wiki/Ioctl. [32] “Ethertype.” Wikipedia, the free encyclopedia. http://en.wikipedia. org/wiki/EtherType. [33] “Java web start.” Oracle. http://www.oracle.com/technetwork/ java/javase/tech/index-jsp-136112.html. 109