Desarrollo protocolos

79
Joaquín Luque Rodríguez Ana Verónica Medina Rodríguez DESARROLLO DE PROTOCOLOS UNIVERSIDAD DE SEVILLA DEPARTAMENTO DE TECNOLOGÍA ELECTRÓNICA

description

Los procesos que se tienen en cuenta para la creacion de un protocolo

Transcript of Desarrollo protocolos

Page 1: Desarrollo protocolos

Joaquín Luque RodríguezAna Verónica Medina Rodríguez

DESARROLLO DEPROTOCOLOS

UNIVERSIDAD DE SEVILLADEPARTAMENTO DE TECNOLOGÍA ELECTRÓNICA

Page 2: Desarrollo protocolos

Joaquín Luque RodríguezAna Verónica Medina Rodríguez

DESARROLLO DEPROTOCOLOS

Universidad de SevillaDepartamento de Tecnología Electrónica

Servicio de PublicacionesSevilla, 1994

* Facultad de Informática y EstadísticaAvenida Reina Mercedes s/n

41012-Sevilla. SPAIN.( 455 27 86

Page 3: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 1

1.- Conceptos de Ingeniería de Protocolos.

Se denomina Ingeniería de Protocolos al conjunto de

actividades que, partiendo de unos requisitos de

comunicación de un sistema informático, es capaz de generar

un protocolo de comunicaciones ejecutable que cumple los

requisitos establecidos de una manera fiable y eficiente

(fig. 1). Las actividades más significativas que tienen

lugar dentro de la Ingeniería de Protocolos son las

siguientes (fig. 2):

a) Síntesis. Partiendo de la descripción informal de

los requisitos de comunicaciones genera una

especificación del protocolo. La especificación de un

protocolo comprende 5 partes distintas:

- Los servicios proporcionados por el protocolo;

- Las suposiciones sobre el entorno en el cual se

va a ejecutar el protocolo;

Figura 1.- Contexto de la Ingeniería de Protocolos

Page 4: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 2

- El vocabulario de mensajes usados por el

protocolo;

- El formato de cada uno de los mensajes del

vocabulario;

- Las reglas de procedimiento que garantizan la

consistencia del intercambio de mensajes; y

Figura 2.- La Ingeniería de Protocolos

Page 5: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 3

Cada una de estas partes de la especificación debe ser

descrita con la menor ambigüedad posible, recurriendo,

cuando sea posible, a técnicas de descripción formal de

protocolos (FDTs: Formal Description Techniques), entre

las que se pueden destacar:

- Máquinas de estados finitos (FSM: Finite State

Machines).

- Máquinas de estados finitos extendidas (EFSM:

Extended Finite State Machines).

- Redes de Petri.

- Lógica temporal.

- Lenguajes de programación.

- Lenguajes de especificación. Entre estos

destacan los siguientes lenguajes normalizados:

- Estelle (ISO), basado en EFSM.

- LOTOS (ISO), basado en lógica temporal.

- SDL (CCITT), basado en EFSM.

- ASN.1 (ISO y CCITT) para descripción de

PDUs.

b) Validación. Esta actividad se propone descubrir los

errores de autoconsistencia lógica que pudiese tener la

especificación, en un esfuerzo por encontrar aquellos

errores que pueden existir en cualquier protocolo, con

independencia de la función específica que realice.

Entre estos errores destacan:

- Bloqueos (deadlocks): el protocolo llega a un

estado del que no puede salir.

Page 6: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 4

- Círculos viciosos (livelocks): el protocolo

ejecuta indefinidamente una secuencia de pasos

repetitiva sin realizar ningún progreso efectivo.

- Diseño incompleto: no se especifican respuestas

para todos los sucesos posibles en cada estado.

c) Verificación. Es la tarea de comprobar que el

protocolo especificado realiza correctamente las

funciones para las que fue diseñado, y esto no sólo en

condiciones normales, sino también ante la presencia de

cualquier combinación de fallos. En la práctica la

diferencia entre la validación y la verificación no es

siempre clara, por lo que a veces se engloban como una

sola actividad.

d) Análisis de prestaciones. El objetivo de la

Ingeniería de Protocolos no es sólo obtener protocolos,

sino hacer que éstos sean los más eficientes que sea

posible. Para ello la especificación es analizada desde

este punto de vista, midiendo parámetros tales como

eficiencia, retardo, longitud de colas y otros, para

sugerir modificaciones en la especificación que

optimicen las prestaciones.

e) Implementación. Una vez obtenida una especificación

fiable y eficaz del protocolo, y a partir de ella, se

procede a la construcción de un protocolo ejecutable.

Son numerosas las cuestiones y criterios que habrá que

tener en cuenta en esta fase, las cuales serán objeto

de desarrollo en posteriores apartados.

Page 7: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 5

f) Generación de pruebas. Aunque la especificación haya

sido depurada y sea fiable y eficiente, el complejo

problema de la implementación puede introducir de nuevo

errores o ineficacias en el producto final. Esta

actividad se encarga de generar la batería de pruebas a

las que deberá someterse el protocolo ejecutable.

Existen diversas técnicas para ello e incluso un

lenguaje de especificación de las pruebas normalizado

por ISO con el nombre de TTCN (Tree and Tabular

Combined Notation).

g) Pruebas. El protocolo ejecutable, antes de su

aprobación definitiva, debe ser probado con respecto a

dos criterios:

- Pruebas de homologación (conformance testing):

el protocolo ejecutable es una implementación fiel

de la especificación y cumple las funciones que en

ella se expresan.

- Pruebas de prestaciones: el protocolo ejecutable

tiene unas prestaciones adecuadas (tiempos de

respuesta, capacidad de tráfico, etc.).

Los principios discutidos anteriormente pueden

formularse en las siguientes diez reglas básicas para el

desarrollo de protocolos [HOL 91]:

1.- Asegúrese de que el problema está bien definido. Todos

los criterios de diseño, requisitos y restricciones,

deben ser formulados antes de comenzar el desarrollo.

Page 8: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 6

2.- Defina los servicios que deben realizarse en cada nivel

o módulo de abstracción antes de decidir que

estructuras se usarán para llevar a cabo estos

servicios (el qué viene antes que el cómo).

3.- Diseñe la funcionalidad externa de cada nivel o módulo

antes que el comportamiento interno. Considere primero

la solución cómo una caja negra y decida como debe

interaccionar con su entorno. Una vez hecho esto puede

ya decidir como se organiza internamente cada caja

negra. Probablemente consistirá en cajas negras más

pequeñas que pueden ser tratadas de forma similar.

4.- Mantenga la simplicidad. Los protocolos rebuscados son

más propensos a contener errores que los simples; son

más difíciles de implementar, más difíciles de

verificar, y normalmente menos eficientes. Existen muy

pocos problemas realmente complejos en el desarrollo de

protocolos. Los problemas que parecen complejos son con

frecuencia problemas simples entrelazados. La tarea del

ingeniero de protocolos consiste en identificar los

problemas más simples, separarlos, y resolverlos

individualmente.

5.- No mezcle cuestiones que son independientes.

6.- No introduzca restricciones innecesarias. Un buen

desarrollo debe ser fácilmente extensible. Un buen

desarrollo resuelve una clase de problemas y no tan

sólo un problema aislado.

7.- Antes de implementar el protocolo, valide y verifique

Page 9: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 7

el protocolo, y analice sus prestaciones, comprobando

que se cumplen los requisitos impuestos.

8.- Implemente el protocolo.

9.- Realice las pruebas de homologación y prestaciones de

la implementación obtenida.

10.- No se salte las reglas 1 a 7.

La regla que se incumple con más frecuencia es claramente la

regla número 10.

2.- El problema de la implementación.

El problema de la implementación de un protocolo,

superada ya la fase de especificación del mismo, presenta

interesantes cuestiones que conviene abordar. Existen

principalmente tres vías para obtener un protocolo

ejecutable a partir de una especificación: automática,

semiautomática o manual (fig. 3). Por la vía automática la

especificación, que debe haber sido descrita completamente

mediante FDTs, se somete a un compilador del lenguaje en el

que está descrita la especificación, el cual genera el

protocolo ejecutable requerido. En el procedimiento manual

existen tres fases:

a) En primer lugar se establecen los criterios o

estrategias para implementar correctamente los

protocolos.

b) A continuación se realiza un diseño informático

fruto del cual se obtiene un documento de diseño.

Page 10: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 8

c) Por último se procede a codificar el diseño anterior

en algún lenguaje de programación convencional del que

se deriva el protocolo ejecutable.

El procedimiento semiautomático es una mezcla de los

dos anteriores. En efecto, parte de una especificación del

protocolo descrita mediante una FDT para la cual se dispone

de un compilador adecuado. Sin embargo, si la especificación

original no es completa (por ejemplo no incluye la gestión

de memoria, la interfaz con módulos externos, etc.) será

necesario completarla de forma manual, siguiendo para ello

los tres pasos anteriores: definición de criterios, diseño

Figura 3.- La implementación de protocolos

Page 11: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 9

informático y codificación. El resultado puede ser bien una

modificación de la especificación original para completarla,

o bien la elaboración de módulos ejecutables que en unión de

los obtenidos automáticamente forman el conjunto del

protocolo ejecutable.

Cualquiera que sea el método empleado la construcción

de un protocolo pasa necesariamente por una fase de

definición de criterios de implementación, aunque según el

grado de automatización del proceso, estos criterios estarán

subsumidos en mayor o menor medida dentro de la herramienta

de generación. Conviene pues centrarse en tales criterios,

entre los cuales destacan los siguientes (fig. 4):

- Representación de arquitecturas multicapa.

- Representación de máquinas de estados.

- Representación de multiplicidad de máquinas iguales

del mismo nivel.

- Tratamiento de cabeceras y colas.

- Relación entre SDUs y PDUs.

- Gestión de memoria.

- Manejo del hardware de comunicaciones.

- Interfaz con módulos externos.

Cada uno de estos aspectos merece un estudio detenido

que se aborda en los próximos apartados.

Page 12: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 10

Figura 4.- Definición de criterios de implementación

Page 13: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 11

Representación decapas

Una capa porproceso

Claridad conceptual

Fácil mantenimiento/sustitución

Facilidad de pruebas

Varias capasen un procesoúnico

Utilización de código común

Ocupa menos espacio

Más rápido

Mayor transparencia

Representación deprocesos

Tareas Mayor claridad conceptual

Fácil mantenimiento/sustitución

Facilidad de prueba

Inconvenientes Sistema Operativo multitarea obligatorio

Mayor espacio

Elevado número de cambios de contexto

Pérdida de control de ejecución

Módulos Obligatoria en Sistemas Operativos monotarea

Necesita sistema de gestión de módulos

Mayor rapidez (no hay cambios de contexto)

Menor espacio

Mejor control de ejecución

Comunicación entrecapas

Asíncrona Monocola Ahorra espacio

Centraliza funciones de gestión de colas

Lógica de gestión más compleja

Multicola

Dificultades Control de flujo.Soluciones:

Colas de tamaño fijo

Primitivas de control deflujo

Asig. de memor.controlada

Ejec. atómica.Solución:

Asig. de prioridades alos eventos

SíncronaTransferencia de control con el IDU

Ventajas Elimina el problema del control de flujo

Elimina el problema de la ejecución atómica

Inconvenientes Dificultades de coordinación

Limita el paralelismo

Eleva el número de cambios de contexto.

Tabla 1.- Representación de arquitecturas multicapa

Page 14: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 12

2.1.- Representación de arquitecturas multicapa.

Cuando se aborda el problema de implementar protocolos

que comprenden más de una capa del modelo OSI hay que

fijarse principalmente en 3 aspectos (tabla 1): a) forma de

representación de las capas; b) forma de representación de

los procesos; y c) forma de comunicación entre capas.

a) Representación de las capas. A este respecto caben dos

soluciones. La primera de ellas propugna que cada capa sea

representada mediante un proceso (fig. 5a), tenga éste la

forma informática que tenga (módulo, tarea). Este enfoque

proporciona una gran claridad conceptual, facilita el

mantenimiento y/o la sustitución de la capa y se adapta

perfectamente a las técnicas de prueba de la capa. Por otra

parte, cabe que varias capas sean agrupadas en un sólo

proceso (fig. 5b), optimizando espacio, tiempo de ejecución,

posibilitando la utilización de código común, y haciendo más

transparente al usuario la constitución interna del

protocolo. Sin embargo con esta solución es más difícil la

sustitución y la prueba de una capa aislada.

Page 15: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 13

b) Representación de los procesos. El término proceso

utilizado hasta ahora para representar una o varias capas es

deliberadamente ambiguo. Su concreción informática puede

tomar dos formas: tarea o módulo. Entendemos por tarea una

unidad de programación que el sistema operativo considera

como un ente autónomo para la ejecución y que, por tanto,

constituye la unidad elemental en la planificación de

actividades del sistema. Un módulo es un subconjunto de una

tarea y no puede ser ejecutado autónomamente por el sistema

operativo. Si se opta por representar cada proceso mediante

una tarea (fig. 6a) se obtienen unos límites claros y

definidos, con lo que ello repercute en cuanto a claridad

conceptual, facilidad de mantenimiento y/o sustitución, y

posibilidades de prueba. Sin embargo esta solución presenta

Figura 5.a.- Una capa por proceso Figura 5.b.- Varias capas porproceso

Page 16: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 14

algunos inconvenientes entre los que cabe destacar:

Figura 6.a.- Un proceso por tarea

Figura 6.b.- Varios procesos por tarea

Page 17: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 15

- Sólo puede utilizarse cuando el sistema operativo es

multitarea.

- Mayor ocupación en memoria debido a que normalmente

cada tarea incorpora una determinada cantidad de código

de tamaño más o menos fijo para su interfaz con el

exterior.

- Fuerte sobrecarga del sistema ya que en un protocolo

es corriente que el control de la ejecución tenga que

estar contínuamente transfiriéndose de un proceso a

otro, por lo que, en esta solución, se transferiría el

control de una tarea a otra. El proceso de cambio de

tarea, denominado "context switching" (cambio de

contexto), involucra una serie de operaciones del

sistema operativo que consumen considerables recursos

del sistema si se realizan con frecuencia.

- Hay una cierta pérdida de control de la ejecución de

los procesos. En efecto, los criterios por los cuales

el sistema operativo transfiere control a una tarea

(proceso) está normalmente marcado por rígidas normas

que, en el mejor de los casos, permiten algún tipo de

parametrización (prioridad, porción de tiempo de CPU,

etc.). Sin embargo, la elección de un adecuado conjunto

de parámetros de ejecución es una tarea delicada que

además sólo permite un control muy reducido sobre los

procesos.

Page 18: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 16

La otra posibilidad consiste en identificar cada uno de

los procesos con un módulo, constituyendo todos ellos una

tarea única (fig. 6b). Esta solución es obligatoria cuando

no se dispone de un sistema operativo multitarea por lo que

el diseñador debe proporcionar un sistema de gestión de

módulos de acuerdo con las facilidades que le proporcione el

sistema operativo y el lenguaje de programación que utilice

(algunos lenguajes como el ADA facilitan en gran medida la

gestión de módulos). En general esta solución ocupa menos

espacio, es más rápida (al eliminar los "context

switchings") y permite un mejor control de la ejecución de

los procesos.

c) Comunicación entre capas. La comunicación de información

entre dos capas adyacentes puede ser de dos tipos: síncrona

o asíncrona. La comunicación asíncrona se basa en la

utilización de una o varias colas donde se introducen las

IDUs (Interface Data Units: Unidades de Datos de la

Interfaz). Dependiendo de los recursos de comunicación entre

procesos (tareas/módulos) que proporcionen el sistema

operativo y el lenguaje de programación, puede implementarse

con una cola (fig. 7a) o par de colas (fig. 7b) para el

conjunto de todas las conexiones entre las dos capas

(sistema monocola) o como un par de colas (fig. 7c) para

cada una de dichas conexiones (sistema multicola). El

sistema monocola en general tiende a ahorrar espacio de

almacenamiento y centraliza las funciones de gestión de

Page 19: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 17

colas, pero por el contrario necesita una lógica algo más

compleja que permita determinar a qué conexión entre capas

corresponde cada IDU.

La comunicación asíncrona entre capas presenta dos

dificultades. La primera de ellas es el control de flujo

entre capas, es decir, cómo hacer que una capa no genere, en

Figura 7.a.- Comunicación con una cola

Figura 7.b.- Comunicación con un par de colas

Figura 7.c.- Comunicación multicola

Page 20: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 18

promedio, más información de la que la otra capa es capaz de

digerir. Existen tres formas posible de abordar esta

cuestión:

- Si se utiliza una técnica multicola (cada conexión

emplea un par de colas), puede asignarse a estas colas

un tamaño fijo por lo que si una capa las encuentra

llenas deberá esperarse, adecuando de esta forma su

ritmo al del otro nivel.

- Si se utilizan colas ilimitadas, o bien la técnica

monocola, es necesario que existan primitivas

específicas de control de flujo entre capas deteniendo

o reanudando el flujo de información a través de la

interfaz para cada conexión concreta.

- Por último, una solución alternativa consiste en

hacer que la asignación de memoria para una IDU se

realice de alguna forma controlada por la capa que lo

va a recibir. De esta forma si dicha capa no se halla

preparada para procesarlo simplemente no concede la

memoria necesaria para almacenar el IDU, con lo que el

proceso en la capa de origen se detiene.

La segunda de las dificultades que surgen como

consecuencia de la comunicación asíncrona entre capas es el

denominado "ejecución atómica de eventos". Consiste en el

hecho de que una petición de servicios de una capa a otra,

realizada mediante la intercomunicación de una IDU, no se

realiza en el mismo momento que se introduce en la cola

Page 21: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 19

correspondiente, pues puede haber otras IDU en la cola que

se procesen con anterioridad. Para determinados servicios de

una capa este hecho puede ser significativo. Por ejemplo, de

acuerdo con muchas especificaciones, cuando una capa lanza

una solicitud de desconexión, la conexión deja de existir

inmediatamente, no enviándose ninguna PDU más. Sin embargo,

si la cola contiene alguna solicitud previa de envío de

PDUs, la solicitud de desconexión no se tendrá en cuenta, en

general, hasta después de haber enviado todas las PDUs de la

cola. La implementación no concuerda con la especificación.

Una posible solución a este problema está en la asignación

de prioridades en la cola de IDUs, con lo cual una solicitud

de desconexión de alta prioridad se atenderá antes que una

solicitud de envío de PDUs.

La comunicación síncrona entre capas es aquella que se

realiza sin el uso de colas de almacenamiento. Cuando una

capa desea pasar una información a otra capa detiene su

ejecución y le pasa el control junto con la IDU. El control

sólo le es devuelto una vez procesada completamente la

información. Aunque este enfoque evita los problemas de

control de flujo y ejecución atómica de eventos, que

existían en la comunicación asíncrona, introduce serias

dificultades de coordinación entre procesos, limitando el

grado de paralelismo en la ejecución de los mismos y

elevando el número de "context switchings" cuando los

procesos se representan mediante tareas.

Page 22: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 20

Tratamientode eventos

Orden de tratamiento FIFO Simple

Con prioridades

Aleatorio Simple

Con prioridades

Efecto de la lectura Permanece en la cola.Requiere:

Orden explícita de extracción

Lectura más compleja

Especificación de eventos adescartar

Desaparece de la cola.Requiere:

Almacenamiento interno deeventos

Especificación de eventosalmacenados

Representación deesperas

En tareas independientes: utiliza recursos del Sistema Operativo

En rutinas: necesitagestor de módulos

Simple

Transferencia cuando debe actuar

Transferencia de orden aleatorio

En corrutinas Necesita gestor de módulos Simple

Transferencia cuando debe actuar

Transferencia en orden aleatorio

No soportado en todos los lenguajes

Descripcióndetransiciones

En tablas (estado,evento, condición)

Más lento

Fácil generación automática

En código Selecciones (estado, evento, condición)

Selecciones (evento, estado, condición)

Representación detemporizadores

En proceso externo Con comprobación periódica

Con llamada al Sistema Operativo

En proceso interno

Tabla 2.- Representación de máquinas de estados

Page 23: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 21

2.2.- Representación de máquinas de estados.

Una vez analizados los problemas de la representación

de arquitecturas multicapas, donde pueden existir múltiples

tipos de máquinas de estados, es el momento de estudiar la

forma de representación de una máquina de estados. A este

respecto son 4 las cuestiones en las que conviene fijarse

(tabla 2): a) tratamiento de los eventos; b) representación

de las esperas; c) descripción de las transiciones; y d)

representación de los temporizadores (timers).

a) Tratamiento de los eventos. Los eventos de entrada a una

máquina provienen de otras máquinas o de la interfaz con el

exterior. En general estos eventos son almacenados en una

cola con disciplina de FIFO (First-In, First-Out: el primer

evento que entra es el primer evento que sale). De esta

forma al acceder a la cola se extrae siempre el más antiguo

(fig. 8a). Este criterio puede ser matizado, si el protocolo

lo requiere, por la existencia de un parámetro de prioridad,

de tal forma que se tome siempre en consideración el evento

más prioritario, sea o no el más antiguo (fig. 8b). A

cualquiera de los dos mecanismos anteriores puede añadirse

un factor de "no determinismo". Esto consiste en extraer de

la cola un evento por criterio aleatorio de entre los de

mayor prioridad (fig. 8c). El acceder aleatoriamente a la

cola tiene sobre todo interés en procesos de validación,

verificación, análisis de prestaciones y pruebas, ya que

Page 24: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 22

permite explorar combinaciones de eventos muy diversos.

Otro aspecto a considerar en el tratamiento de los

eventos es el efecto que tiene sobre la cola el proceso de

lectura. Una solución consiste en considerar que la lectura

de la cola da información del evento que hay que procesar

pero deja inalterada la cola (fig. 9a). De esta forma si la

máquina, por encontrarse en el estado adecuado, toma en

consideración el evento, debe realizarse una operación

explícita de extracción del evento de la cola. Si por el

Figura 8.a.- Tratamiento de eventos con FIFO

Figura 8.b.- Tratamiento de eventos con FIFO priorizado

Figura 8.c.- Tratamiento de eventos no determinista

Page 25: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 23

contrario la máquina no pudiese procesar el evento, éste

seguiría estando en la cola. La máquina debe entonces leer

de nuevo la cola para ver si existe algún otro evento que sí

pueda ser procesado. Por tanto, los eventos no tratados en

un estado son conservados. Este sistema requiere:

- una orden explícita de extracción de eventos de la

cola.

- un mecanismo más complejo de lectura de la cola.

- una especificación de los eventos que, por carecer de

sentido, deban ser descartados (borrados de la cola) en

cada uno de los estados.

Figura 9.a.- Lectura de eventos sin extracción

Figura 9.b.- Lectura de eventos con extracción

Page 26: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 24

La alternativa es que la lectura de la cola suponga

automáticamente la extracción del evento (fig. 9b). Si la

máquina no lo puede procesar en ese estado pero el evento no

debe ser rechazado es necesario que se proceda a un

almacenamiento temporal interno al módulo y a una posterior

consideración de los eventos almacenados. Por tanto, los

eventos no tratados en un estado son descartados. Este

sistema requiere:

- un mecanismo de gestión del almacenamiento interno de

eventos.

- una especificación de los eventos que, por no poder

ser tratados en un estado, deban ser almacenados

internamente.

b) Representación de las esperas. Existen principalmente

tres métodos de representar las situaciones de espera de las

máquinas de estados y el correspondiente control del ciclo

de ejecución de las mismas. La primera de estas formas es la

que se utiliza cuando cada máquina de estados se representa

mediante una tarea independiente, siendo el sistema

operativo el que controla la ejecución de las distintas

tareas (fig. 6a). En este caso la espera se ejecuta haciendo

uso de las facilidades del sistema operativo para suspender

la ejecución de tareas y reanudarlas cuando se produce algún

suceso predeterminado (expiración de un timer, activación de

un semáforo, etc.). En este caso la estructura, para un

ejemplo con dos máquinas, sería la siguiente:

main() /* Máquina A */

Page 27: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 25

{ for (;;) {

espera_a(); lee_evento_a(); procesa_evento_a(); }

}

main() /* Máquina B */{

for (;;) { espera_b(); lee_evento_b(); procesa_evento_b(); }

}

Cuando las máquinas se representan mediante rutinas de

una misma tarea (fig. 6b), y por tanto se necesita un gestor

de módulos específico, la espera de una máquina puede

representarse de dos formas. En primer lugar, puede

utilizarse una salida de la rutina que codifica el módulo y

la devolución del control al gestor de módulos. En este caso

la estructura sería esquemáticamente la siguiente:

main() /* Gestor de módulos */{ for (;;) {

maquina_a();maquina_b();

}}

maquina_a() /* Máquina A */{

lee_evento_a(); procesa_evento_a();

}

maquina_b() /* Máquina B */{

lee_evento_b(); procesa_evento_b();

Page 28: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 26

}

La versión aquí expuesta presenta un gestor de módulos

bastante simple. Algunas modificaciones comunes al gestor

incluyen:

- la transferencia de control a las máquinas únicamente

cuando tienen algún evento que tratar, y

- la activación de las máquinas en orden aleatorio,

principalmente en simulación.

Por último, y también cuando las máquinas se

representan mediante módulos de una tarea única, pueden

representarse las esperas mediante transferencias de control

entre corrutinas. Para ello, en primer lugar, el gestor de

módulos arranca todas y cada una de las máquinas de estados.

Este proceso es similar a la activación de una tarea desde

el sistema operativo. Para arrancar un módulo se reserva un

espacio en la pila (moviendo el Stack Pointer SP) y se llama

a la rutina que representa la máquina. Dicha rutina, al

igual que una tarea del sistema operativo, no debe finalizar

(devolver el control con un "return") sino que cuando quiera

realizar una espera, deberá transferir el control al gestor

de módulos mediante la técnica de corrutinas. Una vez

arrancadas todas las máquinas, el gestor de módulos les va

transfiriendo control también según la técnica de

corrutinas. La estructura genérica es la siguiente:

main() /* Gestor de módulos */{

Page 29: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 27

arranca_maquina_a(); arranca_maquina_b(); for (;;) {

corrutina_maquina_a();corrutina_maquina_b();

}}

maquina_a() /* Máquina A */{ for (;;) {

espera_a(); lee_evento_a(); procesa_evento_a();

}}

maquina_b() /* Máquina B */{ for (;;) {

espera_b(); lee_evento_b(); procesa_evento_b();

}}

arranca_maquina_a(){ reserva_espacio_pila(); valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_a();}

arranca_maquina_b(){ reserva_espacio_pila(); valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_b();}corrutina_maquina_a(){ valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_a,1);}

corrutina_maquina_b(){ valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_b,1);}

Page 30: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 28

espera_maquina_a(){ valor=setjmp(punto_salto_maquina_a); if (valor==0) longjmp(punto_salto_gestor_modulos,1);}

espera_maquina_b(){ valor=setjmp(punto_salto_maquina_b); if (valor==0) longjmp(punto_salto_gestor_modulos,1);}

La reserva de espacio en la pila es una condición

intrínseca al uso de corrutinas. En efecto, en la figura 10

puede observarse la distribución de la pila sin reserva de

espacio (fig. 10a), con los conflictos que acarrea, y con

reserva de espacio (fig. 10b). Es de notar también que las

funciones "setjmp" y "longjmp" con las que se han

implementado en C la técnica de corrutinas son estándares en

ANSI C y en UNIX, por los que pueden encontrarse en gran

número de plataformas y sistemas operativos. Otros lenguajes

proporcionan mecanismos similares. En algunos, sin embargo,

no es posible utilizar este recurso.

Figura 10.a.- Pila con corutinas si reserva de espacio

Page 31: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 29

c) Descripción de las transiciones. Las transiciones pueden

ser expresadas de dos formas diferentes: mediante tablas o

mediante código. En muchos documentos de especificación de

protocolos se expresan las máquinas de estados mediante

tablas (estado, evento, condición). Para cada posible

combinación de estado y evento, y para cada condición

adicional que se imponga, se expresan tabularmente las

acciones a realizar y el próximo estado. La otra forma de

representar las transiciones es expresarlas directamente en

el código usando sentencias anidadas de selección ("switch"

en C o similares). Estas sentencias pueden tomar una

estructura de (estado, evento, condición), o bien de

(evento, estado, condición). Con este sistema el protocolo

se ejecuta a mayor velocidad, ya que no hay que ir

interpretando las tablas de transiciones. Por el contrario

el método de tablas es muy adecuado cuando se realiza una

generación automática de código.

Figura 10.b.- Pila con corrutina y reserva de espacio

Page 32: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 30

Una cuestión que debe tenerse en cuenta, sea cual sea el

método de representación de transiciones elegido, es la

necesidad de representar también el proceso o la transición

de inicialización de la máquina de estados.

La mejor forma de entender cada una de estas opciones

es mediante un ejemplo simple. Supongamos para ello que se

desea construir un juego que dispone de dos luces, roja y

verde, y dos pulsadores, también rojo y verde. Cada segundo,

se enciende aleatoriamente una de las lámparas. Si el

jugador pulsa el botón del mismo color que la lámpara, ésta

se apaga. Transcurrido un segundo se vuelve a encender

aleatoriamente otra (o la misma) lámpara. El juego termina

cuando, por descuido del jugador, se encienden las dos

lámparas, momento en el cual una nueva pulsación no apaga

ninguna lámpara. Este sencillo juego puede modelarse de

acuerdo con la máquina de estados de la figura 11. La misma

máquina se expresa de forma tabular en la figura 12. Los

programas anexos "maqsimp1", "maqsimp2" y "maqsimp3"

presentan las tres alternativas de representación de

transiciones aplicados a ese ejemplo. Por último el programa

anexo "maqsimp" es una simulación ejecutable del mencionado

juego.

Page 33: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 31

Figura 11.- Máquina de estados de un juego simple

Page 34: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 32

d) Representación de los temporizadores (timers). Uno de los

elementos presentes con gran frecuencia en las máquinas de

estados que describen protocolos de comunicaciones es el de

los temporizadores o timers. Si bien son similares a otros

eventos, su tratamiento presenta algunas singularidades que

conviene señalar. Fundamentalmente son dos las formas de

abordar la representación de los temporizadores:

- mediante proceso externo al módulo, o

- mediante proceso interno al módulo.

En el primero de estos enfoques (fig. 13a) la

activación o cancelación de un timer se traducen en una

actuación de la máquina de estados hacia un proceso externo.

En este proceso externo el temporizador puede ser tratado de

dos formas:

Figura 12.- Tablas de estado de un juego simple

Page 35: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 33

1) Una posibilidad consiste en almacenar el instante en

el que expira el timer e irlo comparando periódicamente

(en cada activación del proceso de gestión de timers)

con el valor del reloj (real o simulado). Una vez

superado el plazo impuesto por el timer se genera el

evento correspondiente en la máquina original.

2) La segunda posibilidad de tratamiento del timer en

el proceso externo es solicitar al sistema operativo

que avise (active el proceso de gestión de timers)

cuando expire el evento. Una vez que esto ocurre se

genera el evento correspondiente en la máquina

original.

Figura 13.a.- Gestión externa de timers

Page 36: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 34

La representación de los temporizadores mediante

proceso interno al módulo (fig. 13b) supone que la cola (o

colas) de entrada de eventos a la máquina tiene un parámetro

que indica la "hora de entrada en vigor" de cada evento. De

esta forma un evento normal tendrá hora de entrada en vigor

igual a cero o igual a la hora en la que se generó, con lo

cual puede ser procesado inmediatamente. Por el contrario la

activación de un timer supone la inclusión en la cola de un

evento de expiración del timer con una hora de entrada en

vigor igual a la hora actual más la duración del timer.

Hasta que el reloj (real o simulado) no supera la hora de

entrada en vigor de un evento, éste no es tomado en

consideración. El proceso de la cancelación de un timer

supone la extracción del evento de la cola.

Figura 13.b.- Gestión interna de timers

Page 37: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 35

2.3.- Representación de máquinas múltiples.

Una cuestión que se plantea frecuentemente en el

desarrollo de protocolos es que, si bien el funcionamiento

de una determinada capa es descrita mediante una única

máquina de estados, dicha máquina se halla múltiples veces,

en estados y con valores de variables distintas,

correspondiendo con la existencia de múltiples usuarios. Por

ejemplo un protocolo de transferencia de ficheros puede ser

descrito por una máquina única, también denominada máquina-

tipo. Pero si varios programas de un sistema multitarea

intentan utilizar simultáneamente las facilidades

proporcionadas por el protocolo, cada uno de ellos necesita

para sí una copia de la máquina de estado, la máquina-

individual, que se encuentra en un estado absolutamente

independiente del estado de las demás máquinas-individuales.

La representación de estas máquinas múltiples puede

realizarse recurriendo, principalmente, a dos métodos (tabla

3):

- mediante procesos independientes.

- mediante un proceso único.

En el primero de estos enfoques (fig. 14) cada una de

las máquinas individuales se plasma mediante un proceso

(tarea independiente o módulo de un programa). La variable

que representa el estado de la máquina, así como el resto de

Page 38: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 36

las variables usadas por la maquina deben ser locales al

proceso, mientras que el código es repetido tantas veces

como máquinas-individuales existan. Esta solución implica

una ocupación de espacio relativamente alta.

Procesos independientes Ocupación de memoria alta

Fácil codificación (no hay índices)

Alta velocidad (no hay índices)

Fácil generación automática

Proceso único Variables indexadas Ocupación baja de memoria

Menor velocidad (manejo de índices)

Codificación engorrosa (índices)

Corrutinas Ocupación baja de memoria

Fácil codificación (no hay índices)

Alta velocidad (no hay índices)

Fácil generación automática

Tabla 3.- Representación máquinas múltiples

Si se opta por representar las múltiples máquinas

iguales mediante un único proceso (fig. 15) todas las

máquinas individuales comparten el mismo código, aunque sus

variables son independientes. Esto se traduce en una

importante reducción de espacio. El aspecto más conflictivo

radica en que un código único maneje variables diferentes.

Para ello caben fundamentalmente dos soluciones:

- el uso de variables indexadas, o

- el uso de corrutinas.

La primera de estas alternativas implica que todas las

variables de la máquina sean indexadas, tanto si se definen

como variables globales o como variables locales al proceso.

El índice de las variables hace referencia a la máquina

individual a la que corresponde. Cuando se transfiere

Page 39: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 37

control a la máquina-tipo se hace con la indicación de la

máquina individual que se desea ejecutar, lo que se traduce

en el uso de un determinado índice para las variables. Esta

técnica hace la codificación de la máquina más engorrosa y

su ejecución algo más lenta.

Figura 14.a.- Una máquina individual por tarea

Figura 14.b.- Una máquina individual por módulo

Page 40: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 38

La segunda alternativa implica el uso de corrutinas

para la codificación de las máquinas-tipo. Si las variables

usadas por la corrutina son de tipo local, éstas estarán

Figura 15.a.- Varias máquinas individuales por tarea

Figura 15.b.- Varias máquinas individuales por módulo

Page 41: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 39

ubicadas en la pila.

Si se reservan espacios de pila diferentes para las

variables de cada una de las máquinas individuales de una

máquina-tipo (fig. 16), la ejecución de una máquina

individual se puede efectuar utilizando un único código

correspondiente a la máquina-tipo, el cual utiliza las

Figura 16.- Pila con corutinas y maquinas múltiples

Page 42: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 40

variables de la máquina individual pertinente mediante el

uso de la zona adecuada de la pila. Las variables, por

tanto, no tienen que estar indexadas con lo que se

simplifica el código, se aumenta la velocidad de ejecución y

se facilita la generación automática. El programa anexo

"gestor" realiza una implementación de máquinas múltiples

mediante corrutinas, con pequeñas variantes de tipo práctico

con respecto a lo mencionado en estas líneas (ver

comentarios del propio programa).

Page 43: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 41

2.4.- Tratamiento de cabeceras y colas.

En un determinado nivel, digamos nivel N, la

información que circula, denominada (N)-PDU, está

constituida, en general, por datos del nivel (N+1) y por una

información de control denominada técnicamente (N)-PCI, y en

forma más coloquial cabeceras y/o colas. El tratamiento de

estas cabeceras y colas supone, en general, un esfuerzo de

cálculo bastante considerable, principalmente para el

receptor, por lo que habrá de estudiarse y optimizarse al

máximo. Entre estas técnicas de optimización destacan:

- El uso de cabeceras y colas precalculadas, de forma

que el cómputo efectivo se reduce al mínimo.

- La anticipación y predicción de los posibles valores

de la cabecera y la cola de la próxima PDU, lo que

permite menores tiempos de respuesta y mayor velocidad

de proceso.

- La comprobación temprana de la consistencia y

contenido de la cabecera y la cola de una PDU, evitando

desperdiciar tiempo de CPU en procesar una PDU que será

descartado más adelante.

- El uso de hardware específico para tratamiento

(construcción y deconstrucción) de cabeceras y colas.

Page 44: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 42

2.5.- Relación entre SDUs y PDUs.

Las relaciones entre las unidades de datos del servicio

(SDUs) y las unidades de datos del protocolo (PDUs) para

cada una de las capas, pueden ser de tipo simple como las

recogidas en la figura 17. Sin embargo existen también tres

tipos de relación compleja entre SDUs y PDUs, que son los

siguientes (fig. 18):

Figura 17.- Relaciones simples entre SDUs y PDUs

Page 45: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 43

- Concatenación/separación en la que varias (N)-PDUs

constituyen una única (N-1)-SDU.

- Empaquetamiento/desempaquetamiento, situación en la

que varias (N)-SDUs constituyen una única (N)-PDU.

- Segmentación/reunificación en la que un único (N)-SDU

es enviado utilizando varios (N)-PDUs.

En general la concatenación y el empaquetamiento

mejoran las prestaciones (la eficiencia) aunque, en

determinadas circunstancias pueden aumentar en alguna medida

el retardo. Sin embargo la segmentación puede reducir

significativamente la eficiencia sin mejorar para nada el

retardo. Adicionalmente la segmentación complica

considerablemente la gestión de la memoria (buffers). Por

ello la segmentación debe reducirse al mínimo indispensable.

Figura 18.- Relaciones complejas entre SDUs y PDUs

Page 46: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 44

Una solución obvia es prohibirla, reduciendo con ello los

servicios suministrados por el protocolo. Una alternativa

mucho menos drástica es limitar el uso de la segmentación a

una única capa, con lo cual se limitan los inconvenientes

que plantea.

2.6.- Gestión de memoria.

El flujo de información de una capa a otra que se

produce en todo protocolo de comunicaciones, supone una

importante actividad de gestión de memoria en la que SDUs,

PDUs, IDUs, colas entre capas, variables locales y un largo

etcétera de unidades de información deben ser almacenadas

temporalmente en alguna parte de la memoria. Ello implica la

reserva de una zona de memoria del tamaño adecuado, la

coordinación con el resto de zonas de memoria, la

transferencia de información entre esta zona y el exterior o

entre dos zona distintas y, por último, la liberación de las

zonas una vez que su uso ya no es necesario. Esta gestión de

la memoria de almacenamiento temporal implica pues algunas

cuestiones interesantes, entre las que cabe destacar las 5

siguientes (tabla 4): a) ubicación de las zonas de

almacenamiento; b) gestor de la memoria; c) paso de PDU's

entre capas; d) almacenamiento de PDU's; y e) liberación de

la memoria.

Page 47: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 45

a) Ubicación de las zonas de almacenamiento. Para esta

cuestión se cuenta principalmente con dos alternativas: una

única zona global o una zona diferente para cada capa. El

uso de una única zona de memoria en la cual se almacena la

información de todas las capas del protocolo presenta

algunas peculiaridades. En primer lugar su uso sólo puede

realizarse, obviamente, cuando la estructura dada a las

capas (procesos, tareas, módulos, ...) y el contexto en el

que se enmarca (lenguaje, sistema operativo, plataforma,

...) permiten la definición de zonas comunes de memoria.

Cuando se utiliza esta técnica, en general se aprovecha

mejor el espacio, pues el tamaño de la zona manejada tiene

las reservas propias de la situación global más

desfavorable, mientras que si el almacenamiento es local en

cada capa las reservas deben ser dimensionadas al caso más

desfavorable de dicha capa, lo cual en conjunto suele ser

mayor que una reserva global. Por último, si se usa una zona

global debe ponerse especial cuidado en evitar que las

solicitudes de una capa o capas agoten rapidamente la

capacidad de almacenamiento, dejando sin servicio a otras

capas con poca información almacenada. Esto podría incluso

ocasionar un bloqueo del protocolo, al impedir que actúen

por saturación de la memoria las capas encargadas

precisamente de liberar esa memoria.

Si por el contrario se usan zonas locales en cada capa

se evitan los posibles problemas de bloqueo y se consigue

Page 48: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 46

una mayor independencia entre capas. El precio a pagar es la

necesidad de reservar mayores espacios para almacenamiento y

la pérdida de la gestión centralizada de la memoria.

Ubicación zonas dealmacenamiento

Una zona global Necesita acceso a zona común

Optimiza espacio (menores reservas)

Gestión centralizada

Cuidar asignación equitativa entre capas

Posibles bloqueos

Una zona por capa Mayor independencia entre capas

Evita problemas de asignación y bloqueos

Gestor de memoria Sistema Operativo Codificación más simple

Cuidado con asignación dinámica

Proceso específico: Mayor control

Pase de PDUs entre capas Por copia Simplicidad

Fuerte sobrecarga de proceso

Por referencia Técnica del impreso

Técnica de dispersión-recopilación

Características Gestión más compleja

Mayor velocidad

Almacenamiento de PDUs Contiguas Simplicidad del gestor

Poco aprovechamiento de la memoria

Eventual necesidad de compactación

No contiguas Gestor más complejo

Buen aprovechamiento de la memoria

Liberación de la memoria Donde Con paso de PDUs por copia: En la capa que recibela PDU

Con paso de PDUs porreferencia

En la capa más baja(transmisión)

En la capa más alta(recepción)

Peligros Aspecto siempre delicado

Retransmisión de PDUs

Fallos que descartan PDUs

Tabla 4.- Gestión de memoria

b) Gestor de la memoria. Se pueden plantear fundamentalmente

dos alternativas en cuanto al encargado de gestionar la zona

Page 49: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 47

o zonas de almacenamiento: el sistema operativo o un proceso

específico. El uso de las facilidades de gestión de memoria

que proporcionan el sistema operativo y el lenguaje de

programación que se use se traduce normalmente en una

codificación más simple, pues delega este tipo de funciones,

pero por el contrario redunda en una cierta pérdida de

control. Habrá que tener especial cuidado si además, como

suele ocurrir, estas zonas no tienen reservado un tamaño

fijo, sino que el sistema operativo las toma de las zonas

libres que dinámicamente vaya dejando el propio programa.

Esto en ocasiones puede provocar saturaciones prematuras si

la zona dinámica se ha quedado muy pequeña por el uso que de

ellan han hecho otros módulos del programa, y no

necesariamente de comunicaciones. Si por el contrario se

decide usar un proceso (tarea o módulo) específico para la

gestión de la memoria mejoramos el control de la misma y

evitamos los problemas de la gestión dinámica.

c) Paso de PDU's entre capas. Tres son las formas de

realizar la transferencia de una PDU de una capa a otra: por

copia, mediante la técnica de "impreso" y mediante la

técnica de "dispersión-recopilación". Evidentemente la forma

más directa de realizarlo es mediante copia, es decir, el

nivel N copia en un almacenamiento propio la PDU ofrecida

por el nivel (N+1), añadiéndole la cabecera y cola

correspondiente (fig. 19). Sin embargo esta continua copia

de datos supone, en general, una fuerte sobrecarga que está

Page 50: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 48

enérgicamente desaconsejada, y sólo se justifica por la

sencillez de la implementación. En la técnica del impreso

(fig. 20), el nivel más alto que genera una PDU (normalmente

el de aplicación, a excepción de las PDUs de control)

reserva un espacio suficientemente amplio como para albergar

a la SDU y al conjunto de las cabeceras y colas de los

niveles inferiores. De esta forma un nivel le pasa al

siguiente la PDU como un puntero a la zona del impreso donde

comienza la información pertinente a dicho nivel. Por

último, la técnica de dispersión-recopilación utiliza zonas

de memoria no contigua para almacenar cada una de las

distintas partes de una PDU, utilizando para unirlas

punteros de unas zonas a otras (fig. 21a) o una lista

externa de punteros (fig. 21b). Para poder reconstruir la

PDU original a partir de las diferentes porciones es a veces

conveniente incluir junto al puntero la longitud de la zona

referenciada. Las técnicas de paso de PDUs por referencia

proporcionan una mayor velocidad de ejecución al precio de

una gestión más compleja del paso de PDUs entre capas.

d) Almacenamiento de PDU's. La técnica más simple para

almacenar los PDUs consiste en ubicarlas, sin ninguna

transformación, en la zona de memoria correspondiente. Sin

embargo si, como es frecuente, las PDUs pueden ser de

tamaños muy diferentes, la gestión del almacenamiento se

hace compleja si no se quieren desaprovechar los huecos que

dejan vacantes las PDUs después de ser extraidas de la

Page 51: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 49

memoria. Este problema, similar al que se produce en la

gestión del espacio de un disco magnético donde se almacenan

y borran ficheros, puede solucionarse bien con una

compactación periódica de la memoria, con la consiguiente

sobrecarga de proceso, o bien mediante la división de la PDU

en trozos de tamaño fijo (relativamente pequeños) y el

almacenamiento de dichos trozos en zonas no contiguas de

memoria. Esta solución, si bien supone un gestor de memoria

más elaborado, optimiza el uso del almacenamiento disponible

con escaso consumo de CPU.

Figura 21.a.- Paso de PDUs por punteros

Page 52: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 50

e) Liberación de memoria. Tan importante como la reserva de

la memoria para el almacenamiento de PDUs es la liberación

de la misma cuando ya no resulta necesario su uso. Una

fuente de errores frecuente en el desarrollo de protocolos

radica en una inadecuada liberación de la memoria ocupada.

Por tanto a esta cuestión debe dedicársele un especial

cuidado. Si el paso de PDUs entre capas se realiza mediante

copia la liberación de la memoria correspondiente debe

realizarse, en general, en la capa que recibe el PDU. Si por

el contrario, el paso de PDUs se realiza por referencia

(técnicas de impreso o de dispersión-recopilación), la

memoria deberá ser liberada normalmente en la capa más alta

a la que vaya destinado la PDU (en recepción) o a la capa

inferior del protocolo (en transmisión). No obstante deben

considerarse con precaución dos situaciones especiales. La

Figura 21.b.- Paso de PDUs por lista de punteros

Page 53: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 51

primera es aquella situación que se presenta cuando, por

haber ocurrido algún error, un mensaje debe ser transmitido

de nuevo. Un segundo caso, también en presencia de fallos de

comunicación, es la necesidad de descartar una PDU. En estas

situaciones deberá estudiarse con cautela cuales son las

capas encargadas del almacenamiento temporal y de la

liberación de PDUs que pueden ser retransmitidas, tanto en

caso de fallo como en la ausencia del mismo. Igualmente

deberá considerarse con cuidado la capa o capas que

liberarán las PDUs que han sido descartadas por fallo de

transmisión.

Page 54: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 52

2.7.- Manejo del hardware de comunicaciones.

Toda implementación de un protocolo tiene que afrontar,

antes o después, el problema del hardware específico de

comunicaciones. Más aún, cuando se habla de estos

dispositivos no debe pensarse tan sólo en un hardware que se

limite a cumplir las funciones del nivel físico, sino que,

cada vez con más frecuencia, puede encontrarse hardware de

comunicaciones desempeñando tareas correspondientes a capas

intermedias del modelo OSI (típicamente niveles de enlace de

datos y de red).

El manejo del hardware de comunicaciones puede

abordarse principalmente desde dos perspectivas (tabla 5):

mediante gestión directa o usando las facilidades del

sistema operativo. La gestión directa del hardware de

comunicaciones puede realizarse, suponiendo que el

dispositivo lo permita, mediante alguna de las siguientes

tres técnicas: por exploración, por interrupción o por DMA.

La técnica de exploración consiste en la comprobación

cíclica del estado en el que se encuentran las etapas de

transmisión y recepción del dispositivo. Cuando el estado es

el adecuado se le inyecta una información a transmitir o se

le retira una información recibida. Aunque es un sistema muy

sencillo es ineficaz, supone una fuerte carga de proceso y

requiere una atención continua, so pena de desbordamiento de

las colas internas del dispositivo.

Page 55: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 53

Gestión directa Exploración Ineficaz

Fuerte sobrecarga

Posible pérdida de información

Interrupciones Sistema eficaz

Codificación compleja

DMA Sin interrupciones

Con interrupciones

Usando sistema operativo Sin activación asíncrona

Con activación asíncrona

Características Codificación simple

Mayor coordinación con otrastareas

Mayores tiempos de respuesta

Sobrecarga

Tabla 5.- Manejo del hardware de comunicaciones

Usando la técnica de interrupciones el dispositivo

avisa cuando está listo para transmitir una nueva

información o cuando se ha recibido por la línea algún dato.

Este sistema es en general mucho más adecuado pues no

sobrecarga la CPU y garantiza que no se pierde información,

siempre que la interrupción sea atendida con prontitud. Sin

embargo hay que considerar también que el manejo de

interrupciones es, normalmente, una tarea de una cierta

complejidad.

Por último, si el dispositivo está preparado para ello,

se puede realizar la gestión mediante acceso directo a

memoria (DMA). En esta alternativa la transmisión o

recepción de información se realiza sin el concurso de la

CPU principal, cuyo único cometido está en la iniciación del

proceso y en la recogida de resultados. La finalización del

Page 56: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 54

DMA puede detectarse por exploración cíclica o, lo que es

más frecuente, por interrupciones. Cuando el dispositivo

proporciona esta posibilidad, el DMA con interrupciones es,

sin duda, el más eficiente de los sistemas de manejo del

hardware.

Como contrapunto al manejo directo de los dispositivos

de comunicaciones, se puede considerar el uso del sistema

operativo para estas funciones. Una de las tareas típicas de

los sistemas operativos ha sido siempre la gestión de los

dispositivos periféricos para facilitar y coordinar el uso

que de los mismos realizan las distintas tareas. Normalmente

la parte del sistema operativo que realiza la gestión de un

dispositivo concreto recibe el nombre de manipulador

(driver). A ellos se accede mediante la invocación de

primitivas específicas del sistema operativo que permiten la

transmisión y recepción de información. Algunas de estas

primitivas permiten una activación asíncrona de alguno de

los módulos de la tarea que los invocó, permitiendo de esta

forma una más eficaz gestión de las transferencias de

información. El uso del sistema operativo para el manejo de

los dispositivos de comunicaciones supone por un lado una

mayor simplicidad de código y una mejor coordinación con el

resto de tareas, pero por el contrario hay una cierta

pérdida de control, los tiempos de respuesta son más

elevados y hay una cierta sobrecarga debido a la gestión

introducida por el sistema operativo.

Page 57: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 55

2.8.- Interfaz con el exterior.

El protocolo de comunicaciones no sólo debe comunicarse

con otros computadores a través de los canales adecuados,

sino que también deberá interrelacionarse con otros procesos

(tareas o módulos) que hagan uso de sus servicios. La forma

que adopte esta interfaz con el exterior dependerá en gran

medida de dónde se ubique el protocolo, pudiendo

distinguirse principalmente dos alternativas (tabla 6): en

un frontal de comunicaciones o en el mismo procesador que el

resto de los procesos. Si se utiliza un frontal de

comunicaciones (o "front-end") la relación con otros

procesos será mediante los mecanismos de comunicación que se

establezcan entre los procesadores respectivos. Dicha

comunicación estará normalmente basada en un protocolo

simple para canales punto a punto, con pocos errores y alta

velocidad. La tarea del frontal será, en este caso, la de

preprocesar la información y manejar los protocolos más

complejos que puedan estar presentes, liberando al

procesador principal de las tareas más pesadas de la

comunicación. La intercomunicación entre el frontal y el

procesador principal suele realizarse por alguno de los

siguientes métodos: por BUS (fig. 22a), normalmente con

acceso directo a memoria (DMA); por red local y procesador

específico en BUS con DMA (fig. 22b); y por línea de alta

velocidad, con o sin procesador específico (fig. 22c).

Page 58: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 56

Ubicación En un front-end Por BUS (DMA)

Por red local

Por línea de alta velocidad

En el mismo procesador En el sistema operativo: pordirectivas del Sistema Operativoy zonas globales de memoria

En tareas independientes.:Mediante comunicación entretareas del Sistema Operativo yzonas globales de memoria

En la misma tarea (librería):Mediante llamadas a módulos yzonas comunes de memoria

Tabla 6.- Interfaz con el exterior

Figura 22.a.- Frontal conectado al BUS

Figura 22.b.- Frontal conectado en red local

Page 59: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 57

Si se ubica en el mismo procesador que el resto de los

procesos se plantean, a su vez, tres posibles ubicaciones.

En primer lugar cabe que el protocolo se desarrollo como una

parte del sistema operativo, bien porque el desarrolle lo

realice el propio fabricante, o bien porque nos encontremos

ante sistemas operativos que pueden ser ampliados por el

usuario. En este caso el interfaz entre el protocolo y los

demás procesos se realizará mediante el manejo de las

primitivas del propio sistema operativo y, eventualmente, el

uso de zonas globales de memoria. Una segunda alternativa,

en sistemas multitarea, consiste en desarrollar el protocolo

como una o varias tareas autónomas, realizándose en este

caso la interfaz mediante las facilidades de comunicación

entre tareas que proporcione el sistema operativo y,

Figura 22.c.- Frontal conectado por línea de alta velocidad

Page 60: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 58

eventualmente también, a través de zonas globales de

memoria. Por último el protocolo puede tomar la forma de una

librería de módulos que se integran en una tarea mediante

llamada siguiendo las reglas del lenguaje de programación

elegido y utilizando zonas de memoria internas a la propia

tarea (aunque puedan estar compartidas entre varios

módulos). La decisión de usar una u otra alternativa depende

en general de consideraciones de diseño globales de la

aplicación y no, tan sólo, del protocolo de comunicaciones.

Page 61: Desarrollo protocolos

ANEXOS

Page 62: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 60

ANEXOS

Programa GESCOR.C.

Programa ejemplo de gestión de máquinas con corrutinas.

#include <stdio.h>/* Estos include son necesarios para los */#include <setjmp.h>/* procesos de gestión de corrutinas */

main() /* El main tan sólo sirve para dar soporte a la rutina gestor */{ printf("\n\n\n"); gestor_modulos();}

jmp_buf punto_salto_maquina_a;jmp_buf punto_salto_maquina_b;jmp_buf punto_salto_gestor_modulos;

gestor_modulos(){ arranca_maquina_a(100); arranca_maquina_b(100); for (;;) { corrutina_maquina_a(); corrutina_maquina_b(); }}

maquina_a(){ int valor,paso;

printf("Activada máquina A\n"); paso=0; for (;;) { valor=setjmp(punto_salto_maquina_a); if (valor==0) longjmp(punto_salto_gestor_modulos,1); printf("Máquina A. Paso %d\n",++paso); } /* Fin del bucle infinito */} /* Fin del módulo A */

arranca_maquina_a(reserva)int reserva;{ int valor;

reserva--; if (reserva<=0)

Page 63: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 61

{ valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_a(); } else arranca_maquina_a(reserva); return;}

corrutina_maquina_a(){ int valor;

valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_a,1); return;}

maquina_b(){ int valor,paso;

printf("Activada máquina B\n"); paso=0; for (;;) { valor=setjmp(punto_salto_maquina_b); if (valor==0) longjmp(punto_salto_gestor_modulos,1); printf("Máquina B. Paso %d\n",++paso); } /* Fin del bucle infinito */} /* Fin del módulo B */

arranca_maquina_b(reserva)int reserva;{ int valor;

reserva--; if (reserva<=0) { valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_b(); } else arranca_maquina_b(reserva); return;}

corrutina_maquina_b(){ int valor;

valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_b,1); return;}

Programa GESTOR.

Page 64: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 62

Programa ejemplo de gestión de corrutinas recursivas.

#include <stdio.h> /* Estos include son necesarios para los */#include <setjmp.h> /* procesos de gestión de corrutinas */

#include <stdlib.h> /* Estos include son necesarios */#include <time.h> /* para los procesos de aleatorización */

main() /* El main tan sólo sirve para dar soporte a la rutina gestor */{ randomize(); printf("\n\n\n"); gestor();}

#define n_tareas_modulo_A 3 /* Estos define deben ser generados de forma*/#define n_tareas_modulo_B 2 /*automática por el generador de código C */#define n_total_tareas 5 /*y coinciden con el número de máquinas

individuales de cada módulo y con elnúmero total de ellas */

#define RESERVA_STACK 100 /* Este define marca el número de vecesque se activa recursivamente la rutina dellamada de módulos antes de llamardefinitavemente al módulo. Es una medidaindirecta de la cantidad de espacioreservado en el stack para la llamada asubrutinas de cada máquina individual */

/* Cada módulo genera un conjunto de instruciones como las siguientes.Su objetivo es establecer los puntos de ida y vuelta paracada una de las máquinas individuales del módulo */

/***** Variables correspondientes al módulo A ******/

jmp_buf salto_ida_modulo_A [n_tareas_modulo_A];jmp_buf salto_vuelta_modulo_A;int arrancando_modulo_A=1; /* Utilizado en el proceso de arranque

del módulo */

/**** Variables correspondientes al módulo B *****/

jmp_buf salto_ida_modulo_B [n_tareas_modulo_B];jmp_buf salto_vuelta_modulo_B;int arrancando_modulo_B=1;

/**** Variables correspondientes al mmi *****/

jmp_buf salto_ida_mmi;jmp_buf salto_vuelta_mmi;

/****************************************************************** Rutina que realiza la gestión de todas las tareas.** En una primera fase inicializa todas las tareas

Page 65: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 63

* reservando espacio de stack para ellas y esbleciendo* los puntos de salto a las mismas. La activación de* estas rutinas se hace en forma de corrutinas que,* por tanto, están siempre activas. La llamada a cada* una de las tareas de cada uno de los módulos se hace* recursivamente.** En una segunda fase realiza un bucle infinito para* dar control en orden aleatorio a cada una de las tareas* que componen la especificación.** Adicionalmente deberían incluirse en el proceso de* inicilización como corrutinas y de cesión de control* en cada uno de los pasos del bucle infinito, todas* aquellas rutinas de simulación, almacenamiento,* presentación, y control general de la ejecución de* de la especificación que fuesen necesarias. El orden* de estas rutinas no tiene por que ser aleatorio sino* que normalmente se hará su activación en un orden* determinado al final de la activación de todas las* máquinas individuales.**********************************************************************/

gestor(){ int i,valor; int modulo,tarea;

/**** Fase de inicialización *********/

valor=setjmp(salto_vuelta_modulo_A); /* Establece punto de vuelta de la corrutina */

if (valor==0) llamada_modulo_A(0); /* Activa la primera tarea delprimer módulo */

/***** Fase de cesión cíclico de control ********/

while(1) { for (i=0;i<n_total_tareas;i++) { decide_tarea_aleatoriamente(i,&modulo,&tarea); switch (modulo) { case 0:

valor=setjmp(salto_vuelta_modulo_A); /* Punto de vuelta de lacorrutina */

if (valor==0) longjmp(salto_ida_modulo_A[tarea],1); /* Va a la corrutina

correspondiente a latarea calculada delmódulo A */

break;

case 1:valor=setjmp(salto_vuelta_modulo_B);if (valor==0) longjmp(salto_ida_modulo_B[tarea],1);break;

} /* Fin del switch */ } /* Fin del bucle de llamada a todas las máquinas individuales */

valor=setjmp(salto_vuelta_mmi); if (valor==0) longjmp(salto_ida_mmi,1);

printf("\n");

Page 66: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 64

}

return;}

/****************************************************************** Esta rutina calcula aleatoriamente el módulo y la tarea* a los que debe cederseles el control.** Para ello calcula unas tablas aleatorias que son inicializadas* cada vez que se ejecuta un paso en todas las máquinas* individuales.*****************************************************************/

decide_tarea_aleatoriamente(vez,modulo,tarea)int vez,*modulo,*tarea;{ static int tabla_modulo[n_total_tareas]={0,0,0,1,1}; /* Las constantes*/ static int tabla_tarea[n_total_tareas] ={0,1,2,0,1}; /* son obtenidaspor

el generador decódigo C */

static int tabla_modulo_elegido[n_total_tareas]; static int tabla_tarea_elegida[n_total_tareas]; static int tarea_elegida[n_total_tareas];

int i,j,n,contador;

/***** La primera activación en cada paso dispara el cálculo delas tablas aleatorias *****/

if (vez==0) { for (i=0;i<n_total_tareas;i++) tarea_elegida[i]=0; for (i=0;i<n_total_tareas;i++) { n=random(n_total_tareas-i); contador=-1; for (j=0;j<n_total_tareas;j++) {

if (tarea_elegida[j]==0) contador++;if (n==contador){ tarea_elegida[j]=1; tabla_modulo_elegido[i]=tabla_modulo[j]; tabla_tarea_elegida[i]=tabla_tarea[j]; break;}

} /* Fin de la busqueda de la tarea elegida */ } /* Fin del for i */ } /* Fin del calculo de las tablas aleatorias */

/***** Determinación del módulo y tarea basado en las tablas *****/

*modulo=tabla_modulo_elegido[vez]; *tarea=tabla_tarea_elegida[vez]; return;}

/********************************************************************* Módulo A correspondiente al cuerpo de un módulo en ESTELLE*

Page 67: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 65

* Aquí figurarán las transiciones, actuaciones y, en* general todos aquellos procesos necesarios para la* ejecución de una máquina individual.** La rutina tiene una primera fase de inicialización* donde va activando recursivamente todas las máquinas* individuales. Cuando finaliza, activa la primera máquina* individual del módulo siguiente.** La rutina no finaliza nunca sino que se le transfiere* y devuelve el control mediante la técnica de corrutinas**********************************************************************/

modulo_A(tarea)int tarea;{ int valor; int paso; int j; char buf[2];

/**** Variables tomadas como ejemplo para comprobar que los valores de las mismas son independientes para cada máquina individual */

j=random(100); buf[0]=random(100); buf[1]=random(100); printf("Entra en módulo A con tarea= %d; j= %d; buf: %d %d\n",

tarea,j,buf[0],buf[1]);

paso=-1; while (1) {

/****** Fase de inicialización de la corrutina *****/

valor=setjmp(salto_ida_modulo_A[tarea]); if (valor==0) if( tarea < (n_tareas_modulo_A-1) && arrancando_modulo_A)

llamada_modulo_A(++tarea,RESERVA_STACK); /* Activa otratarea del mimo módulo */

else {

if (arrancando_modulo_A){ arrancando_modulo_A=0; valor=setjmp(salto_vuelta_modulo_B); if (valor==0) llamada_modulo_B(0,RESERVA_STACK); /* Activa la primera

tarea del módulosiguiente */

}longjmp(salto_vuelta_modulo_A,1);

}

/****** Fase de ejecución de un paso de la máquina individual *****/

paso++; printf("Módulo A: Tarea %d; Paso %d; ",tarea,paso); j++; buf[0]++; buf[1]++; printf("j= %d; Buf: %d %d\n",j,buf[0],buf[1]); } /* Fin del bucle infinito */} /* Fin del módulo A */

Page 68: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 66

/*********************************************************************** Rutina para reservar espacio en el stack. Esta rutina debe* ser generada por el generador de código para cada uno de* los módulos de la especificación.** Si la activación de un módulo que se realiza para cada máquina* individual se hiciera directamente llamando a la rutina* correspondiente, las variables del módulo llamado se almacenarían* en el stack a continuación de las variables del módulo llamante* EN EL MOMENTO DE LA LLAMADA. Si posteriormente el módulo llamante* al tener control en alguno de sus ejecuciones, necesitara ampliar* el número de variables almacenados en el stack, machacaría* las variables del módulo llamado.** Para evitarlo, la activación se hace de forma que las variables* del módulo llamado no empiecen a continuación de las del módulo* llamador. Para ello se realiza un cierto número de veces la* llamada recursiva a esta rutina antes de llamar definitivamente* la rutina correspondiente. Con ello las variables de esta rutina* son las que se colocan en la pila una y otra vez a continuación* de las variables del módulo llamante, y serán por tanto las* variables destruidas por el módulo llamante en caso de ampliar* sus necesidades de uso del stack. Las variables destruidas no* necesitan ser conservadas por lo que esta destrucción no afecta* al funcionamiento del sistema.************************************************************************/

llamada_modulo_A(tarea,reserva)int tarea,reserva;{ reserva--; if (reserva<=0) modulo_A(tarea); else llamada_modulo_A(tarea,reserva); return;}

/******** Rutina correspondiente al módulo B *******/

modulo_B(tarea)int tarea;{ int valor; int paso; int k; char baf[2];

k=random(100); baf[0]=random(100); baf[1]=random(100); printf("Entra en módulo B con tarea= %d; k= %d; baf: %d %d\n",

tarea,k,baf[0],baf[1]); paso=-1; while (1) { valor=setjmp(salto_ida_modulo_B[tarea]); if (valor==0) if( tarea < (n_tareas_modulo_B-1) && arrancando_modulo_B)

llamada_modulo_B(++tarea,RESERVA_STACK); else {

if (arrancando_modulo_B){ arrancando_modulo_B=0;

Page 69: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 67

valor=setjmp(salto_vuelta_mmi); if (valor==0) llamada_mmi(RESERVA_STACK); /* Al ser el útimo módulo ESTELLE

activa el primer módulo de gestión,en este ejemplo el mmi que, además,es el único */

}longjmp(salto_vuelta_modulo_B,1);

}

paso++; printf("Módulo B: Tarea %d; Paso %d; ",tarea,paso); k++; baf[0]++; baf[1]++; printf("k= %d; Baf: %d %d\n",k,baf[0],baf[1]); }}

/****** Rutina de llamada al módulo B *****/

llamada_modulo_B(tarea,reserva)int tarea,reserva;{ reserva--; if (reserva<=0) modulo_B(tarea); else llamada_modulo_B(tarea,reserva); return;}

/********************************************************************** Rutina que simula el Man Machine Interface (MMI)*********************************************************************/

mmi(){ int tecla;

printf("Entra en el mmi\n"); while (1) { tecla=leetecla(); printf("MMI: Se pulsó la tecla %d\n",tecla); }}

/****** Rutina de llamada al mmi *****/

llamada_mmi(reserva)int reserva;{ reserva--; if (reserva<=0) mmi(); else llamada_mmi(reserva); return;}

/**************************************************************** Rutina de lectura de tecla** Si no se ha pulsado niguna tecla devuelve el control* al gestor de tareas con técnica de corrutina.*

Page 70: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 68

* Cuando se pulsa una tecla devuelve el control al MMI* para que ejecute un ciclo no muy largo y velva a llamar* a leetecla. Esta cesión de control se hace con técnica de* subrutina normal.******************************************************************/

leetecla(){ int i,j,valor;

while(!kbhit()) { valor=setjmp(salto_ida_mmi); if (valor==0) longjmp(salto_vuelta_mmi,1); } i=getch(); if (i!=0) return(i); j=getch(); return(j<<8);}

Programa GESCORM.C.

Programa ejemplo de gestión de máquinas múltiples concorrutinas.

/********************************************************************** Programa ejemplo de gestión de máquinas múltiples* con corrutinas*********************************************************************/

#include <stdio.h> /* Estos include son necesarios para los */#include <setjmp.h> /* procesos de gestión de corrutinas */

main() /* El main tan sólo sirve para dar soporte a la rutina gestor */{ printf("\n\n\n"); gestor_modulos();}

#define n_maquinas_tipo_a 3#define n_maquinas_tipo_b 2jmp_buf punto_salto_maquina_a[n_maquinas_tipo_a];jmp_buf punto_salto_maquina_b[n_maquinas_tipo_b];jmp_buf punto_salto_gestor_modulos;

gestor_modulos(){ int i;

Page 71: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 69

for (i=0;i<n_maquinas_tipo_a;i++) arranca_maquina_a(i,100); for (i=0;i<n_maquinas_tipo_b;i++) arranca_maquina_b(i,100); for (;;) { for (i=0;i<n_maquinas_tipo_a;i++) corrutina_maquina_a(i); for (i=0;i<n_maquinas_tipo_b;i++) corrutina_maquina_b(i); }}

maquina_a(tarea)int tarea;{ int valor,paso;

printf("Activada máquina A (%d)\n",tarea); paso=0; for (;;) { valor=setjmp(punto_salto_maquina_a[tarea]); if (valor==0) longjmp(punto_salto_gestor_modulos,1); printf("Máquina A (%d). Paso %d\n",tarea,++paso); } /* Fin del bucle infinito */} /* Fin del módulo A */

arranca_maquina_a(tarea,reserva)int tarea,reserva;{ int valor;

reserva--; if (reserva<=0) { valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_a(tarea); } else arranca_maquina_a(tarea,reserva); return;}

corrutina_maquina_a(tarea)int tarea;{ int valor;

valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_a[tarea],1); return;}

maquina_b(tarea)int tarea;{ int valor,paso;

printf("Activada máquina B (%d)\n",tarea); paso=0; for (;;) { valor=setjmp(punto_salto_maquina_b[tarea]); if (valor==0) longjmp(punto_salto_gestor_modulos,1); printf("Máquina B (%d). Paso %d\n",tarea,++paso); } /* Fin del bucle infinito */} /* Fin del módulo B */

arranca_maquina_b(tarea,reserva)int tarea,reserva;

Page 72: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 70

{ int valor;

reserva--; if (reserva<=0) { valor=setjmp(punto_salto_gestor_modulos); if (valor==0) maquina_b(tarea); } else arranca_maquina_b(tarea,reserva); return;}

corrutina_maquina_b(tarea)int tarea;{ int valor;

valor=setjmp(punto_salto_gestor_modulos); if (valor==0) longjmp(punto_salto_maquina_b[tarea],1); return;}

Programa MAQSIMP1.C.

Representación de una máquina de estados simplemediante tablas.

typedef struct{ int condicion; int accion; int nuevo_estado; void *siguiente_transicion;} transicion;

typedef struct{ int codigo; int parametro;} evento;

#define NULA 0#define NUM_ES_A 2#define NUM_EV_A 2

enum estados_a {ES1_A,ES2_A};enum eventos_a {EV1_A,EV2_A};enum condiciones_a {P1_A,P2_A};enum salidas_a {SAL1_A,SAL2_A,SAL3_A,SAL4_A};

transicion transicion_ES1A_EV1A={NULA,SAL2_A,ES2_A,NULA};transicion transicion_ES1A_EV2A={NULA,NULA,ES1_A,NULA};

Page 73: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 71

transicion transicion_ES2A_EV1A={NULA,SAL2_A,ES2_A,NULA};transicion transicion_ES2A_EV2A_1={P1_A,SAL3_A,ES1_A,NULA};transicion transicion_ES2A_EV2A_2={P2_A,SAL4_A,ES2_A,NULA};

transicion maquina[NUM_ES_A][NUM_EV_A];

int estado_a=ES1_A;int accion_inicial_a=SAL1_A;

evento lee_evento();

int estado_luz_roja,estado_luz_verde;

inicia_maquina_a(){ maquina[ES1_A][EV1_A]=transicion_ES1A_EV1A; maquina[ES1_A][EV2_A]=transicion_ES1A_EV2A; maquina[ES2_A][EV1_A]=transicion_ES2A_EV1A; maquina[ES2_A][EV2_A]=transicion_ES2A_EV2A_1; transicion_ES2A_EV2A_1.siguiente_transicion=&transicion_ES2A_EV2A_2;

accion_a(accion_inicial_a);}

maquina_a(){ evento ultimo_evento; transicion transicion_disparada,transicion_anterior;

ultimo_evento=lee_evento(); transicion_disparada=maquina[estado_a][ultimo_evento.codigo]; do { if (condicion_a(transicion_disparada.condicion)) { estado_a=transicion_disparada.nuevo_estado; accion_a(transicion_disparada.accion); } else { transicion_anterior=transicion_disparada; transicion_disparada=*(transicion*)(transicion_disparada.siguiente_transicion); } } while (transicion_anterior.siguiente_transicion!=NULA);} /* Fin del programa */

condicion_a(condicion)int condicion;{ switch (condicion) { case P1_A: if ( hay_solo_una_luz_encendida() &&

boton_y_luz_del_mismo_color() ) return(1); else return(0);

case P2_A: if (!hay_solo_una_luz_encendida() )return(1); else return(0);

}}

accion_a(accion)int accion;

Page 74: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 72

{ switch (accion) { case SAL1_A: activa_timer(); break;

case SAL2_A: enciende_luz_roja_o_verde(); activa_timer(); break;

case SAL3_A: apaga_una_luz(); break;

case SAL4_A: informa_perdida_juego(); break;

}}

Programa MAQSIMP2.C.

Representación de una máquina de estados simplemediante selección en código de tipo (estado, evento,condición).

typedef struct{ int codigo; int parametro;} evento;

enum estados_a {ES1_A,ES2_A};enum eventos_a {EV1_A,EV2_A};

int estado_a=ES1_A;

evento lee_evento();

int estado_luz_roja,estado_luz_verde;

inicia_maquina_a(){ activa_timer(); return;}

maquina_a(){ evento ultimo_evento;

Page 75: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 73

ultimo_evento=lee_evento(); switch(estado_a) { case ES1_A: switch (ultimo_evento.codigo) { case EV1_A: enciende_luz_roja_o_verde(); activa_timer(); estado_a=ES2_A; break;

case EV2_A: break;

} /* Fin del switch de los eventos del estado ES1_A */ break;

case ES2_A: switch (ultimo_evento.codigo) { case EV1_A: break;

case EV2_A: if ( hay_solo_una_luz_encendida() && boton_y_luz_del_mismo_color()) {

apaga_una_luz();estado_a=ES1_A;

} else if (!hay_solo_una_luz_encendida()) {

informa_perdida_juego(); } break;

} /* Fin del switch de los eventos del estado ES2_A */ break;

} /* Fin del switch de los estados */} /* Fin de la maquina a */

Programa MAQSIMP3.C

Representación de una máquina de estados simplemediante selección en código de tipo (evento, estado,condición).

Page 76: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 74

typedef struct{ int codigo; int parametro;} evento;

enum estados_a {ES1_A,ES2_A};enum eventos_a {EV1_A,EV2_A};

int estado_a=ES1_A;

evento lee_evento();

int estado_luz_roja,estado_luz_verde;

inicia_maquina_a(){ activa_timer(); return;}

maquina_a(){ evento ultimo_evento;

ultimo_evento=lee_evento(); switch(ultimo_evento.codigo) { case EV1_A: switch (estado_a) { case ES1_A: enciende_luz_roja_o_verde(); activa_timer(); estado_a=ES2_A; break;

case ES2_A: break;

} /* Fin del switch de los estados del evento EV1_A */ break;

case EV2_A: switch (estado_a) { case ES1_A: break;

case ES2_A: if ( hay_solo_una_luz_encendida() && boton_y_luz_del_mismo_color()) {

apaga_una_luz();estado_a=ES1_A;

} else if (!hay_solo_una_luz_encendida()) {

informa_perdida_juego(); } break;

} /* Fin del switch de los estados del evento EV2_A */ break;

} /* Fin del switch de los eventos */

Page 77: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 75

} /* Fin de la maquina a */

Programa MAQSIMP.C.

Programa de simulación de la máquina simple de juegos.

#include <simpro.h>

typedef struct{ int codigo; int parametro;} evento;

enum maquinas {MAQUINA_A};enum estados_a {ES1_A,ES2_A};enum eventos_a {EV1_A,EV2_A};

int estado_a=ES1_A;

int estado_luz_roja,estado_luz_verde;evento ultimo_evento;

main(){ inicializa_lista_sucesos(); inicia_maquina_a(); for (;;) { maquina_a(); lee_teclado(); }}

inicia_maquina_a(){ activa_timer(); return;}

maquina_a(){ lee_evento(); switch(estado_a) { case ES1_A: switch (ultimo_evento.codigo) { case EV1_A: enciende_luz_roja_o_verde(); activa_timer(); estado_a=ES2_A;

Page 78: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 76

break;

case EV2_A: break;

} /* Fin del switch de los eventos del estado ES1_A */ break;

case ES2_A: switch (ultimo_evento.codigo) { case EV1_A: enciende_luz_roja_o_verde(); activa_timer(); break;

case EV2_A: if ( hay_solo_una_luz_encendida() && boton_y_luz_del_mismo_color()) {

apaga_una_luz();estado_a=ES1_A;

} else if (!hay_solo_una_luz_encendida()) {

informa_perdida_juego(); } break;

} /* Fin del switch de los eventos del estado ES2_A */ break;

} /* Fin del switch de los estados */} /* Fin de la maquina a */

activa_timer(){ genera_suceso(EV1_A,MAQUINA_A,0,0,5.); return;}

apaga_una_luz(){ int luz;

luz=genera_error(0.5); if (luz) if (estado_luz_roja==1) estado_luz_roja=0; else estado_luz_verde=0; else if (estado_luz_verde==1) estado_luz_verde=0; else estado_luz_roja=0; printf("%5.0f %d %d\n",tiempo_transcurrido(),

estado_luz_roja,estado_luz_verde); return;}

boton_y_luz_del_mismo_color(){ int boton;

boton=ultimo_evento.parametro; /* 0:rojo; 1:verde */ if ( (boton==0) && (estado_luz_roja==1) ) return(1); if ( (boton==1) && (estado_luz_verde==1) ) return(1); return(0);}

Page 79: Desarrollo protocolos

DESARROLLO DE PROTOCOLOS Página 77

hay_solo_una_luz_encendida(){ int i;

i=estado_luz_roja+estado_luz_verde; if (i==1) return(1); else return(0);}

enciende_luz_roja_o_verde(){ int luz;

luz=genera_error(0.5); if (luz) estado_luz_roja=1; else estado_luz_verde=1; printf("%5.0f %d %d\n",tiempo_transcurrido(),

estado_luz_roja,estado_luz_verde); return;}

informa_perdida_juego(){ printf("Has perdido la partida. Para salir del juego pulsa x\n"); return;}

lee_evento(){ struct suceso ultimo_suceso; int *puntero;

lee_proximo_suceso(&ultimo_suceso); ultimo_evento.codigo=ultimo_suceso.codigo; puntero=ultimo_suceso.parametros; ultimo_evento.parametro=*puntero; return;}

lee_teclado(){ char tecla; static int boton;

tecla=getch(); if (tecla=='r') { boton=0; genera_suceso(EV2_A,MAQUINA_A,0,&boton,0.); return; } if (tecla=='v') { boton=1; genera_suceso(EV2_A,MAQUINA_A,0,&boton,0.); return; } if (tecla=='x') exit();}