Icono del sitio Oposinet

Tema 28 – Programación en tiempo real.

1. PROGRAMACIÓN EN TIEMPO REAL. 1

1.1 Introducción. 1

1.2 Rendimiento. 2

1.3 Bases de datos en tiempo real: 2

1.4 Sistemas operativos en tiempo real 3

1.5 Planificación en tiempo real 3

2. INTERRUPCIONES. 4

2.1 Conceptos generales sobre interrupciones. 4

2.2 Interrupciones en Turbo Pascal 4

3. SINCRONIZACIÓN Y COMUNICACIÓN ENTRE TAREAS. 7

3.1 Semáforos. 7

3.2 Monitores. 9

3.3 Mensajes. 9

3.4 Comunicación síncrona y asíncrona. 10

3.5 Comunicación en UNIX con C. 11

3.6 Threads en Java. 12

4. LENGUAJES. 13

4.1 Lenguaje ADA.. 13

4.2 Lenguaje Jovial 13

4.3 Lenguaje CHILL. 13

1. PROGRAMACIÓN EN TIEMPO REAL

1.1 Introducción

· Tiene 2 características que la definen:

· Todos los sucesos se generan como respuesta a estímulos que proceden del exterior del sistema, es decir, que proceden del mundo real.

· La respuesta a cada estímulo posee unas ligaduras de tiempo que hemos de respetar. Una vez detectado y analizado el estímulo se debe responder dentro de unos parámetros temporales establecidos de antemano.

· Clasificación de las tareas atendiendo a sus ligaduras de tiempo:

· Tareas rígidas: Es cumplimiento de la ligadura es obligatorio.

· Tareas flexibles: El cumplimiento de la ligadura es deseable.

· Los elementos que componen un sistema de SW en tiempo real son:

· Componentes de adquisición de datos: Adquiere y formatea los datos procedentes del exterior.

· Componentes de análisis: Transforma los datos recogidos según lo requiera la aplicación.

· Componente de control/salida: Genera la respuesta necesaria en cada momento.

· Componente de monitorización: Coordina a todos los demás componentes.

Ejemplos: Sistemas de detección de incendios, alarmas, sistema guía de misiles, piloto automático…

1.2 Rendimiento

Cuando se estudia un rendimiento de un sistema en tiempo real, hay dos factores claves:

· Tiempo de respuesta del sistema: Periodo de tiempo que transcurre desde que el sistema detecta un estímulo hasta que responde al mismo.

o Cambio de contexto: Tiempo y sobrecarga necesaria para conmutar tareas.

o Latencia de interrupción: Tiempo necesario para conmutar tareas.

o Otros factores: Velocidad de cálculo, velocidad de acceso a memorias masivas…

· Razón de transferencia de datos: Velocidad con la que los datos entran o salen del sistema.

Fiabilidad: Aspecto muy importante puesto que un fallo en estos sistemas puede generar accidentes.

1.3 Bases de datos en tiempo real:

· Los sistemas de tiempo real requieren del uso y la gestión de una base de datos. (Base de datos distribuida)

Ventajas:

· Podemos acceder más rápidamente a los datos y de forma individual.

· Conseguimos dividir el tráfico de entrada y salida y acortar colas de espera.

· Raramente provocará un fallo en la BD.

· Para conseguir esto tenemos que emplear redundancia.

· La redundancia mejora la velocidad de acceso.

Desventajas:

· Problemas logísticos de actualización y acceso a los datos.

· Control de concurrencia

o Solución1: Bloqueo y estampado de tiempo: Se crean intervalos regulares:

1. Bloqueamos la BD: no se permite realizar ninguna operación.

2. Se hace la actualización requerida.

3. Desbloqueamos la BD.

4. Se comprueba la corrección, validando los archivos.

5. Se indica que hemos terminado la actualización.

o Solución2: El protocolo del escritor exclusivo: Intenta eliminar la sobrecarga del sistema de bloqueo y estampado de tiempo. Solo permite que una tarea de escritura actualice un único archivo.

1.4 Sistemas operativos en tiempo real

Se pueden elegir 2 opciones para implementar nuestro sistema en tiempo real:

· S.O de propósito general.

· S.O específico para los sistemas en tiempo real.

Los sistemas operativos en tiempo real presentan requisitos especiales en las áreas:

· Determinismo: Las operaciones se realizan en instantes fijos o períodos concretos.

· Sensibilidad: Periodo de tiempo que transcurre desde que el S.O reconoce una interrupción hasta que comienza a darle servicio.

· Control de usuario: El usuario debe ser capaz de conocer las tareas en funcionamiento…

· Fiabilidad: Probabilidad de que se produzca un error.

· Tolerancia de fallos: Capacidad de un sistema para corregir o solventar un problema.

Los sistemas en tiempo real actuales, incorporan estas características:

· Pequeño tamaño.

· Alarmas especiales y temporizadores.

· Planificación apropiativa con el uso de prioridades.

· Cambio de contexto rápido

· Multitarea.

· Herramientas de comunicación entre procesos como semáforos, señales, sucesos, etc.

El S.O que usemos debe permitir establecer prioridades sobre las interrupciones para aplicar la máxima prioridad a las interrupciones procedentes de nuestro sistema en tiempo real.

1.5 Planificación en tiempo real

Después del S.O, el planificador es el componente más importante en un sistema en tiempo real. La tarea del planificador consiste en llevar a cabo un algoritmo de planificación cuya función será llevar a cabo todas las ligaduras de tiempo posibles.

Los algoritmos de clasificación se pueden clasificar en:

· Métodos con tablas estáticas: Se usa con tareas periódicas. Se genera un plan para atender a todas las tareas antes de la ejecución del sistema.

· Métodos apropiativos con prioridades estáticas: También se realiza un análisis estático del sistema, pero no se traza ningún plan, simplemente asignamos a cada tarea una prioridad para que pueda ser manejada por un S.O apropiativo.

· Métodos dinámicos de planificación: Similar al primero, pero en este caso el análisis lo realizamos durante la ejecución del sistema. También se crea un plan de acción. Cada vez que llegamos a una nueva tarea estudiamos si es viable solventarla, si no, no la atendemos.

· Métodos dinámicos del mejor resultado: Similar al anterior. No se hace ningún estudio de viabilidad. El sistema intenta que se cumplan todos los plazos. Si algún proceso se ha iniciado y su plazo no se ha cumplido, se anula. Al llegar una nueva tarea le asignamos una prioridad en función de su importancia. Hasta que no se ha terminado el plazo de una tarea, no sabemos si se ha podido realizar.

2. INTERRUPCIONES

2.1 Conceptos generales sobre interrupciones

Consiste en detener la ejecución de una tarea. Cuando el ordenador interrumpe una tarea, al volver a ella debe continuar desde donde lo dejo la última vez.

Hay 2 tipos de interrupciones:

· Interrupciones HW: Un elemento HW es el que genera la interrupción.

· Interrupciones SW: Las provoca algún programa que solicita servicios especiales al S.O o a la BIOS. Estas son las que podemos generar en nuestros programas.

Cuando el ordenador recibe una interrupción debe realizar las siguientes tareas:

1) Detener el programa en ejecución.

2) Salvar el estado del programa interrumpido.

3) Determinar la naturaleza de la interrupción, y atenderla adecuadamente.

4) Restaurar el estado del programa interno.

5) Volver a ejecutar el programa.

No todas las interrupciones son iguales. Dependiendo de la importancia de la misma, se les suele dar una prioridad.

2.2 Interrupciones en Turbo Pascal

Tenemos 2 instrucciones para realizar una llamada a una interrupción.

· Intr($Número_de_la_interrupción, variable_tipo_register);

Número de la interrupción: el símbolo del dólar indica al compilador que se trata de un número hexadecimal (0-255).

Variable de tipo registro: Se utiliza para seleccionar el servicio de la interrupción, y para almacenar el resultado de la llamada a la interrupción. Esta variable se corresponde con los registros de CPU del 8088.

El procesador 8088 dispone de varios tipos de registros:

1. Registros de propósito general:

· AX = Registro acumulador, dividido en AH y AL (8 bits cada uno).

Usándolo se produce (en general) una instrucción que ocupa un byte menos que si se utilizaran otros registros de uso general. Su parte más baja, AL, también tiene esta propiedad. El último registro mencionado es el equivalente al acumulador de los procesadores anteriores (8080 y 8085). Además, hay instrucciones como DAA; DAS; AAA; AAS; AAM; AAD; LAHF; SAHF; CBW; IN y OUT que trabajan con AX o con uno de sus dos bytes (AH o AL). También se utiliza este registro (junto con DX a veces) en multiplicaciones y divisiones.

· BX = Registro base, dividido en BH y BL.

Es el registro base de propósito similar (se usa para direccionamiento indirecto) y es una versión más potente del par de registros HL de los procesadores anteriores.

· CX = Registro contador, dividido en CH y CL.

Se utiliza como contador en bucles (instrucción LOOP), en operaciones con cadenas (usando el prefijo REP) y en desplazamientos y rotaciones (usando el registro CL en los dos últimos casos).

· DX = Registro de datos, dividido en DH y DL.

Se utiliza junto con el registro AX en multiplicaciones y divisiones, en la instrucción CWD y en IN y OUT para direccionamiento indirecto de puertos (el registro DX indica el número de puerto de entrada/salida)

2. Registros de segmento:

· CS: Registro de segmento de código.

· DS: Registro de segmento de datos.

· ES: Registro de segmento extra.

· SS: Registro de segmento de pila.

3. Registros de desplazamiento (punteros)

· IP= Instruction pointer o puntero de instrucción. Como su nombre indica, apunta a la siguiente instrucción a ejecutar.

· SP = Puntero de pila (no se puede subdividir).

Aunque es un registro de uso general, debe utilizarse sólo como puntero de pila, la cual sirve para almacenar las direcciones de retorno de subrutinas y los datos temporarios (mediante las instrucciones PUSH y POP). Al introducir (push) un valor en la pila a este registro se le resta dos, mientras que al extraer (pop) un valor de la pila este a registro se le suma dos.

4. Registro de indicadores o banderas:

Hay nueve indicadores de un bit en este registro de 16 bits. Los cuatro bits más significativos están indefinidos, mientras que hay tres bits con valores determinados: los bits 5 y 3 siempre valen cero y el bit 1 siempre vale uno (esto también ocurría en los procesadores anteriores).

Registro de indicadores (16 bits)

Registro de indicadores (16 bits)

Bit

15

14

13

12

11

10

9

8

7

6

5

4

3

2

1

0

Flag

OF

DF

IF

TF

SF

ZF

0

AF

0

PF

1

CF

· CF (Carry Flag, bit 0): Si vale 1, indica que hubo “arrastre” (en caso de suma) hacia, o “préstamo” (en caso de resta) desde el bit de orden más significativo del resultado. Este indicador es usado por instrucciones que suman o restan números que ocupan varios bytes. Las instrucciones de rotación pueden aislar un bit de la memoria o de un registro poniéndolo en el CF.

· PF (Parity Flag, bit 2): Si vale uno, el resultado tiene paridad par, es decir, un número par de bits a 1. Este indicador se puede utilizar para detectar errores en transmisiones.

· AF (Auxiliary carry Flag, bit 4): Si vale 1, indica que hubo “arrastre” o “préstamo” del nibble (cuatro bits) menos significativo al nibble más significativo. Este indicador se usa con las instrucciones de ajuste decimal.

· ZF (Zero Flag, bit 6): Si este indicador vale 1, el resultado de la operación es cero.

· SF (Sign Flag, bit 7): Refleja el bit más significativo del resultado. Como los números negativos se representan en la notación de complemento a dos, este bit representa el signo: 0 si es positivo, 1 si es negativo.

· TF (Trap Flag, bit 8): Si vale 1, el procesador está en modo paso a paso. En este modo, la CPU automáticamente genera una interrupción interna después de cada instrucción, permitiendo inspeccionar los resultados del programa a medida que se ejecuta instrucción por instrucción.

· IF (Interrupt Flag, bit 9): Si vale 1, la CPU reconoce pedidos de interrupción externas enmascarables (por el pin INTR). Si vale 0, no se reconocen tales interrupciones. Las interrupciones no enmascarables y las internas siempre se reconocen independientemente del valor de IF.

· DF (Direction Flag, bit 10): Si vale 1, las instrucciones con cadenas sufrirán “auto-decremento”, esto es, se procesarán las cadenas desde las direcciones más altas de memoria hacia las más bajas. Si vale 0, habrá “auto-incremento”, lo que quiere decir que las cadenas se procesarán de “izquierda a derecha”.

· OF (Overflow flag, bit 11): Si vale 1, hubo un desborde en una operación aritmética con signo, esto es, un dígito significativo se perdió debido a que tamaño del resultado es mayor que el tamaño del destino.

https://www.alpertron.com.ar/8088.HTM à para profundizar más.

3. SINCRONIZACIÓN Y COMUNICACIÓN ENTRE TAREAS

· En un sistema operativo multitarea tenemos muchos procesos funcionando simultáneamente.

· Estos procesos tienen que relacionarse entre ellos con el fin de compartir información o comunicarse, y para sincronizarse.

· Normalmente los S.O multitarea se ejecutan sobre ordenadores con un solo procesador.

· Para dar sensación de multitarea se mezclan las instrucciones de diferentes procesos. (interfoliación)

· La interfoliación puede provocar efector indeseados cuando varios procesos diferentes comparten recursos.

· Cuando no es posible aplicar la interfoliación debemos definir secciones críticas.

· Las secciones críticas son simplemente bloques de código a los que no podemos aplicarle la interfoliación.

Existen muchos mecanismos diferentes para comunicar y sincronizar procesos.

3.1 Semáforos

· Los semáforos permiten sincronizar procesos.

· Es responsabilidad del programador incluirlos y hacerlo de forma concreta.

· Fueron creados por Dijkstra (1965)

· El semáforo se basa en crear una variable entera que es gestionada por el S.O.

· Se pueden realizar 2 operaciones indivisibles:

o Down(s): Decrementa el valor del semáforo.

§ Si el valor de la variable entera s es 0, el proceso se detiene en el semáforo.

§ Si el valor de la variable entera S es mayor que 0, se decremento su contenido.

o Ups(s): Incrementa el valor del semáforo. También se pueden dar 2 casos.

§ No hay procesos en espera: incrementamos en 1 el contador.

§ Si estaban esperando procesos en el semáforo, seleccionamos uno, y lo despertamos, dejando que el haga su Down(s).

· Los semáforos pueden ser generales (valores mayores que 1) o binarios solo (2 valores, 0 y 1).

· Si hay varios procesos esperando la solución es crear una cola. Le daremos prioridad al primer elemento de la cola.

· Debemos usar las primitivas que duermen y las que despiertan procesos. En UNIX sleep y wakeup.

3.1.1 Semáforos en UNIX

· En UNIX podemos usar directamente los semáforos.

· En el estándar POSIX están definidos los semáforos, sus características y funcionamiento.

· En el estándar POSIX, no manejamos los semáforos de forma individual, sino que trabajamos con conjuntos de semáforos.

· Cada conjunto de semáforos se nombra mediante un identificado, y además disponemos de un campo de 9 bits, donde podremos indicar las opciones de acceso, y los archivos.

· Disponemos de 3 servicios asociados a los semáforos:

1) S_Id=semget(clave, Número_semáforos, Opciones);

Usamos para crear un conjunto de semáforos. También sirve para buscar un conjunto se semáforos y acceder a su identificador.

2) R= semctl(S_Id, Semáforo, Código_operación, Comando_Operación);

Nos sirve para realizar operaciones de control sobre un semáforo que ya existe.

Las operaciones que podemos realizar sobre un semáforo son:

– Inicializar el contador

– Obtener el valor actual del contador

– Obtener el número de procesos en espera

– Cambiar los permisos de acceso

– Destruir el conjunto de semáforos

3) R=semop(S_Id, Operaciones, Número_Operaciones);

Se usa para realizar operaciones sobre los semáforos. Para tal propósito usaremos una tabla donde en cada posición se almacena una operación elemental.

3.1.2 Propiedades y características de los semáforos

Orden de las operaciones: El orden de las operaciones puede afectar al resultado.

o El tiempo de acceso al recurso ha de ser el menor posible.

o Si realizamos una operación down sobre una sección crítica y el contador vale 0, el proceso se quedará detenido, bloqueando el recurso.

Selección del proceso que se desbloquea: Si tenemos procesos en la cola de espera hemos de seleccionar qué proceso debemos activar. Pero puede ocurrir que haya dos procesos muy rápidos y que monopolicen el recurso. Para evitar esto se puede crear una cola de espera.

Granularidad: Hace referencia al conjunto de recursos compartidos que se controlan con un semáforo:

o Granularidad fina: Cada recurso compartido tiene asignado un único semáforo evitando que los procesos se interfieran. Pero hay más riesgo de interbloqueo.

o Granularidad gruesa: Los semáforos controlan varios recursos compartidos. Se evitan los interbloqueos. Pero perdemos el paralelismo y se generan más problemas de acceso.

3.2 Monitores

Otra solución a la sincronización de los procesos es desarrollada en 1974 por Hoare.

Un monitor debe cumplir:

1) Está formado por conjunto de datos locales y procesos que acceden a estos.

2) Los procesos pueden acceder a estos datos a través de unas funciones públicas y nunca directamente.

3) Sólo puede usar el monitor un solo proceso, de forma simultánea.

4) Aparece una variable de condición. Sirven para organizar listas de procesos en espera.

5) Los procesos que ya está usando el monitor tienen preferencia sobre los que quieren empezar a usarlo.

6) Disponemos de 2 primitivas para sincronizar nuestros procesos:

a. Wait(C): Esperamos a que este libre la variable de condición C. Si no está libre, la variable se añade a la lista de espera. Si está libre, accedemos directamente al recurso.

b. Signal(C): Liberamos la variable de condición C. Si hay procesos en la lista de espera se le da acceso al de mayor prioridad.

Inconvenientes:

– Pocos compiladores permiten el uso de los monitores, por lo que tenemos que crearlos nosotros.

– Se pueden dar casos de interbloqueo si un monitor usa funciones de otro.

3.3 Mensajes

Permiten a los procesos intercambiar información entre ellos. Nos permiten comunicar y sincronizar procesos.

Se basa en 2 operaciones:

1) Send(destino, mensaje): sirve para enviar un mensaje a un proceso.

2) Receive(fuente, mensaje): recoge un mensaje de un proceso.

Existen 2 formas para identificar el proceso al que vamos a enviar el mensaje o del que lo vamos a recibir.

1) Directamente: Sabemos el nombre del proceso y lo situamos en la primitiva. Podemos usar la comunicación síncrona.

2) Mediante un buzón: Sirve para intercambiar información entre procesos. No sabemos realmente el nombre del proceso al que vamos a enviar la información, pero si sabemos el nombre del buzón sobre el que trabajamos. Posibilita la comunicación asíncrona.

Proceso process_Abel;

Send(Cain, mensaje);

Proceso process_Cain;

Receive (Abel, mensaje);

Usando una comunicación usando buzones. Lo primero que debemos hacer es crear nuestro buzó, y cuando terminemos de usarlo, por supuesto destruirlo.

CreateMailBox(Nombre_Buzón);

DestroyMailBox(Nombre_Buzón);

//creamos nuestro buzón:

CreateMailBox(hermanos);

//comunicamos los 2 procesos mediante el buzón creado:

Proceso Process_Abel;

Send(Hermanos, mensaje);

Proceso Process_Cain;

Receive(Hermanos, mensaje);

//por último destruimos nuestro buzón cuando no queramos usarlo más

DestroyMailBox(Hermanos);

En UNIX, los buzones se pueden implementar usando las tuberías o pipes.

3.4 Comunicación síncrona y asíncrona

La comunicación entre procesos se puede llevar a cabo de 2 formas:

1) Comunicación síncrona: Los dos procesos han de coincidir en el tiempo real para que se produzca la comunicación. El envío de mensajes tiene ese inconveniente.

2) Comunicación asíncrona: Los dos procesos no han de coincidir en el tiempo para que se produzca la comunicación. 2 formas:

a. Sin copia intermedia: Un proceso realiza una petición, el S.O la anota y sigue ejecutándose de forma normal. Cuando el S.O recibe respuesta de la petición, lo notifica al proceso mediante una interrupción.

b. Con copia intermedia: Cuando un proceso envía un mensaje se guarda en una cola. Si otro proceso va a recoger su mensaje se retira de la cola.

3.5 Comunicación en UNIX con C
3.5.1 Creación de procesos y manejo de los mismos

Podemos usar procesos usando la orden fork de UNIX. Hay un proceso padre que es el creador del nuevo proceso y un proceso hijo que es el que se acaba de crear:

– El mismo entorno (código, pila, datos, descriptores de ficheros abiertos)

– Capturan las mismas señales.

– Privilegios y prioridades.

– Las librerías compartidas.

– Directorio raíz y directorio actual.

Los procesos padre e hijo no comparten:

– PID (process ID)

– Las señales pendientes

– Las alarmas pendientes y los temporizadores se inicializan.

3.5.2 Comunicación de procesos en UNIX

Hay 3 mecanismos para comunicar procesos en UNIX:

1) Tuberías interconexiones o pipes.

· Forma sencilla de interconectar 2 procesos. Una tubería es una zona de memoria, cuyo tamaño puede variar, pero oscila entorno a los 4096 bytes.

· Los procesos deben tener el mismo padre y residir en la misma máquina.

Ejemplo:

Sort <f | head.

2) Señales o signals.

Son como las interrupciones que se envían entre procesos. Para responder a una determinada señal debemos haber programado una respuesta.

3 alternativas al recibir una señal:

– Ignorarla

– Tomar la acción por defecto

– Capturar la señala y responderla nosotros desde nuestro programa.

3) Conectores o sockets.

Permiten la comunicación entre procesos de diferentes padres y de distintas máquinas.

UNIX permite los siguientes sockets:

De flujo: No hay límite de tamaño. Flujo de datos dúplex.

De paquetes en secuencia: permite limitar el tamaño.

De datagramas: Transfieren mensajes de diferentes tamaños. No garantiza ni su llegada ni su orden.

De mensajes certificados: igual que el anterior, pero se asegura que llegarán.

3.6 Threads en Java

Los hilos o hebras nos permiten ejecutar simultáneamente carios procesos.

Hay 2 formas de crear hilos:

– Usando la clase Thread

– Implementar la interfaz Runnable

Un hilo puede adoptar 4 estados:

Creado

– En ejecución

o Dormido

o Suspendido

o Bloqueado

o Esperando

– Parado

– Muerto

4. LENGUAJES

Características deseables:

– Deben permitir programas varios procesos que se ejecutan de forma simultánea de manera que podamos sincronizar y comunicar estos procesos entre sí.

– Es necesario que dispongan de metodologías modernas como la modular, poo…

– Es deseable que podamos probar y simular el sistema antes de implantarlo.

4.1 Lenguaje ADA

· Toma nombre de la primera programadora de la historia (Ada lovelace, hija de lord Byron)

· La idea era buscar un lenguaje de programación oficial para los proyectos informáticos de EEUU.

· Es un lenguaje multipropósito.

· Características:

o Sintaxis similar a PASCAL.

o No diferencia Mayus y minus.

o Es modular

o Distingue procedimientos y funciones.

4.2 Lenguaje Jovial

· “Jules Own Versión of the International Algorithmic Language”

· Lenguaje de propósito específico.

· Se usa para diseños de aviones de combate como misiles de crucero avanzados, Blackhawk…

Para desarrollar Jovial disponemos de unas herramientas que funcionan bajo los sistemas UNIX y MS-DOS.

4.3 Lenguaje CHILL

· CCITT High Level Language.

· Empleado en el campo de telecomunicaciones, pero no solo en esa área.

· Características:

o POO

o Concurrencia

o Herencia simple

o Uso de plantillas (templates)

Salir de la versión móvil