Sistemas en tiempo real y Lenguajes de Programación

Anuncio
Sistemas en tiempo real y
Lenguajes de Programación
Objetivos del curso:
• Comprender las características de los sistemas en tiempo real.
• Aplicar los conceptos en el dominio industrial
• Investigación
Requisitos
• Conocimientos de un lenguaje de programación
secuencial.
• Ingeniería de Software.
• Introducción a los conceptos básicos del control
automático.
Tema 1
• Definición de los sistemas en tiempo real.
• Ejemplos.
Qué es un sistema en tiempo real?
• Definición: “ Cualquier sistema en el que el tiempo en el que
se produce la salida es significativo.” La entrada al sistema
corresponde a un estímulo externo y se producirá una salida
relacionada con esta entrada en un período de tiempo finito y
determinado.
• Control en tiempo real. El tiempo es crucial en cualquier
sistema embebido.
• El programador debe lograr que los programas sincronicen con el
tiempo.
• Especificar los tiempos en los que deben ser realizadas las acciones y
completadas. Se deben responder a situaciones en las que no todos los
requisitos temporales se deben satisfacer.
• Responder a situaciones en las que los requisitos temporales cambian
dinámicamente.
Qué es un sistema en tiempo real?
• La corrección de un sistema de tiempo real depende no solo
del resultado lógico sino tambien del tiempo en el que se
producen los resultados.
• Sistemas embebidos: Cuando el commputador es un
componente del proceso de información en un sistema de
ingeniería más grande.
Terminología
•
Hard real-time — Sistemas de tiempo real estrictos. En estos sistemas el tiempo de
respuesta es crucial y debe producirse dentro del límite especificado. Ejemplo:
Sistemas de control de vuelo.
•
Soft real-time — Sistemas de tiempo real no estrictos. En estos sistemas el tiempo
de respuesta es importante pero el sistema seguirá funcionando correctamente
aunque los tiempo límite no se cumplan ocasionalmente. Ejemplo: Sistemas de
adquisición de datos.
•
Real real-time — Sistemas de tiempo real son sistemas en los cuales el tiempo de
respuesta es muy corto.
•
Firm real-time — Sistemas de tiempo real firme son sistemas de tiempo real no
estricto pero el tiempo límite puede no cumplirse ocasionalmente pero no hay
beneficio por la entrega retrasada.
Ejemplos de sistemas en tiempo real
• Ejemplos:
• Control de procesos
• Fabricación: mantener los costes bajos y el nivel de productividad.
• Comunicación, mando y control: Reserva de plazas de una compañía
aérea, las funcionalidades médicas para cuidado automático del
paciente, control de tráfico aéreo, contabilidad bancaria remota.
• Sistema de computador embebido generalizado: siempre interacciona
con el equipamiento físico en el mundo real.
• Dispositivos del mundo real se debe muestrear la información a
intervalos de tiempo regulares.
• Reloj de tiempo real.
• Consola de operador para la intervención manual.
• Displays de diferentes tipos incluyendo gráficos.
A simple fluid control system
Interfase
Tubería
Lectura del
Flujo de entrada
Medidor de Flujo
Procesado
Ángulo de
la válvula de salida
Tiempo
Ordenador
Válvula
Sistema de Control de Procesos
Consola del operador
Ordenador del cotrol
Del proceso
Producto
final
Materia prima
PLANTA
Un sistema de mando y control
Puesto de mando
Ordenador de mando y
control
Temperatura, Presión, Potencia, etc.
Terminales
Sensores/Actuatores
Sistema Típico Embebido
Reloj de
tiempo real
Algoritmos para
Control Digital
Registro de datos
Base de
Datos
Recuperación y
Presentación de datos
Consola del
operador
Interfaz
Del operador
Interfaz
Dispositivos de
Presentación
Sistema de Ingeniería
Sistema de
monitorización
remota
Ordenador tiempo real
Características de los sistemas embebidos
• Los sistemas embebidos deben responder a eventos
del mundo real.
• Los sistemas de tiempo real precisan mantenimiento
constante y mejoras durante sus ciclos de vida.
• Deben ser extensibles.
• Los sistemas de tiempo real son a menudo complejos
Tema 2: Características de
un RTS(Sistema en tiempo
real)
Características de un RTS
• Grande y complejo: Los programas deben cambiar
continuamente. Precisan de mantenimiento constante y
mejoras durante su ciclo de vida. Se pueden dividir en
componentes más pequeños más fáciles de gestionar.
• El software y el hardware deben ser fiables y seguros.
• Control concurrente de los distintos componentes separados
del sistema. Un sistema embebido suele constar de
ordenadores y otros elementos externos con los que los
programas deben interactuar simultámeamente. La naturaleza
de estos elementos externos del mundo real suelen existir en
paralelo. Cuando los elementos externos se encuentran
distribuidos geográficamente se deben considerar sistemas
embebidos distribuidos o multiprocesadores.
Características de un RTS
• Control en tiempo real: Los sistemas de tiempo real se
construyen habitualmente utilizando procesadores con
considerable
capacidad
adicional
garantizando
el
comportamiento en el peor de los casos. El programador debe
especificar los tiempos en los que deben ser realizadas las
acciones, especificar los tiempos en los que las acciones
deben ser completadas, responder a situaciones en las que no
todos los requisitos temporales se pueden satisfacer y
responder a situaciones en las que los requisitos temporales
cambian dinámicamente ( modos de cambio). El programador
debe sincronizar el programa con el tiempo.
Caracterísricas de un RTS
• Interacción con interfaces hardware.
• Especificación de registro de dispositivos y control de
interrupciones.
• Lenguajes de alto nivel, niveles de calidad.
• Control directo y evitar las arquitecturas con una capa de
funciones del sistema operativo.
• Implementación eficiente y entorno de ejecución.
• Evaluar el coste de las características de implementación.
Resumen
• Clasificación de los sistemas de tiempo real:
• Sistemas en tiempo real crítico
• Sistemas en tiempo real acrítico
• Características de un sistema de tiempo real o embebido:
• Grandeza y complejidad,
• Fiabilidad y seguridad extrema,
• Control concurrente de componentes separados del
sistema,
• Control en tiempo real,
• Interacción con interfaces hardware,
• Implementación eficiente,
Paradigmas de Programación
• Un paradigma de programación representa un enfoque
particular para la construcción del software.
• Según las especificaciones del sistema un paradigma resulta
más apropiado que otro.
• Ejemplos de paradigmas:
• Imperativo o por procedimientos: C
• Funcional: Lisp
• Lógico: PROLOG
• Orientado a Objetos: Phyton, Java, C++
Imperativo o por procedimientos
• Se describe la programación en términos del estado del
programa y secuencias que cambian dicho estado.
• Los programas son una serie de instrucciones que indican al
ordenador cómo realizar una tarea.
• Programación alto nivel y bajo nivel, la diferencia es el tipo de
sentencias y variables.
• Ejemplos:
• C,C++, Java
Programación dirigida por eventos
• La estructura como la ejecución de los programas van
determinados por los sucesos que ocurran en el
sistema o que ellos mismos provoquen.
• A diferencia de la programación estructurada es el
usuario o lo que esté accionando el programa el que
dirija el flujo del programa.
• La programación orientada a eventos permite
interactuar con el usuario en cualquier momento de la
ejecución. Los programas creados bajo esta
arquitectura se componen
por un bucle exterior
permanente encargado de recoger los eventos y
distintos procesos que se encargan de tratarlos.
Descomposición y Abstracción
• Programación Estructurada:
• Diseño descendente
• Abstracción - Diseño Inductivo
• Módulos: conjunto de objetos y operaciones relacionadas
lógicamente.
• La técnica de encapsulamiento sirve para aislar una
función en un módulo.
• La estructura Módulo permite:
• Ocultación de Información
• Compilación por Separado: permite la construcción de
bibliotecas de componentes precompilados. Favorece la
reutilización.
• Tipos abstractos de datos uso de estructuras
• ADA, Java – paquetes
• C archivos separados la compilación (estático)
• Java – clases y objetos
Técnicas
• En la programación estructurada se utilizan estructuras
de tipo secuencial, condicional e iterativa.
• La programación modular consiste en dividir un
programa en módulos o subprogramas con el fin de
hacerlo más legible y manejable. Es una evolución de
de la programación estructurada para solucionar
problemas de programación más grandes y complejos.
Un problema complejo debe de ser dividido en varios
subproblemas más simples, repitiendo este proceso
hasta obtener subproblemas simples como para poder
ser resueltos por un lenguaje de programación. Este
proceso se llama análisis descendente (Top-Down). En
la práctica suelen representarse los módulos como
procedimientos o funciones. Si un módulo necesita de
otro se debe definir una interfaz de comunicación.
Lenguajes de programación RTL
• Lenguajes de programación secuenciales: Soporte del sistema
operativo:
• RTL/2, Coral 66, Jovial, C.
• Sin soporte del sistema operativo: lenguajes de alto nivel
concurrentes: Ada, Chill, Modula-2, Mesa, Java.
•
•
•
•
Java/Real-Time Java
C and Real-Time POSIX (Laboratorios)
Ada 95
Also Modula-1 for device driving
Sistemas de Tiempo Real y Sistemas
Operativos
User Programs
Operating
Hardware
System
Configuracón de un Sistema Operativo
User Program
Including Operating
Hardware
System Components
Configuración de un Sistema Embebido
Diseño de sistemas de Tiempo Real
„
„
Lo más importante del desarrollo de un sistema de
tiempo real es la generación de un diseño consistente
que satisfaga una especificación acreditada de los
requisitos.
Fases:
–
–
–
–
–
–
Especificación de requisitos
Diseño arquitectónico
Diseño detallado
Implementación
Prueba
Otras actividades:
• Prototipado previo a la implementación final
• Diseño de la interfaz hombre-máquina
• Criterios para la evaluación de los lenguajes de implementación
Diseño de Sistemas en tiempo real
„
Niveles de notación: Métodos McDermid(1989)
– Informal – lenguaje natural y
– Estructurada
– Formal
Tema 2: Características de los
Sistemas en tiempo real
Fiabilidad y Tolerancia a Fallos
1
Fiabilidad, fallos y defectos
• Para cualquier lenguaje de programación de
tiempo real es requisito tener las herramientas
necesarias para faciliar la construcción de
sistemas altamente fiables. Por ejemplo:
herramientas de gestión de excepciones.
• Podemos definir la fiabilidad como: … una
medida del éxito con el que el sistema se ajusta
a alguna especificación definitiva de su
comportamiento. Randell et al. (1978).
• Cuando el comportamiento de un sistema se
desvía del especificado para él, se dice que es
un fallo. Randel et al. (1978)
2
Fiabilidad, fallos y defectos
• Los fallos que analizamos están relacionados
con el comportamiento de un sistema. Los fallos
son el resultado de problemas internos no
esperados
que
el
sistema
manifiesta
eventualmente en su comportamiento externo.
Los problemas se denominan errores y sus
causas mecánicas o algorítmicas defectos.
• Un componente defectuoso de un sistema es un
componente que reproducirá un error bajo un
conjunto concreto de circunstancias durante la
vida del sistema.
3
Fiabilidad, fallos y defectos
• Un fallo en un sistema puede inducir un
defecto en otro, el cual puede acabar en
un error y en un fallo potencial de ese
sistema…puede continuar y producir un
defecto en cualquier sistema relacionado,
según se ilustra en le siguiente diagrama:
Fallo
Defecto
Error
Fallo
4
Fiabilidad, fallos y defectos
• Tipos de Fallos:
– Fallos transitorios: Comienza en un instante de tiempo concreto,
se mantiene en el sistema durante algún período de tiempo y
luego desaparece. Muchos de los fallos de los sitemas de
comunicación son transitorios.
– Fallos permanentes: comienzan en un instante determinado y
permanecen en el sistema hasta que son separados, p.e.cable
roto, error de diseño del software.
– Fallos intermitentes: Son fallos transitorios que ocurren de vez
en cuando. Un ejemplo es un componente hardware sensible al
calor que funciona durante un rato deja de funcionar, se enfría y
entonces empieza a funcionar de nuevo.
5
Modo de fallo
Dominio del valor
Error de Límites
Valor
erroneo
Dominio del tiempo
Adelantado
Fallo de Silencio:
a partir de un
momento falla el
servicio
Omission:
El servicio no
es entregado
Fallo de parada
Es un fallo
silencioso pero los
otros sistemas
detectan el fallo
Arbitrario
(Valor y Tiempo)
Retraso:error
De prestaciones
Fallo
controlado:
Falla de una
forma
Controlada y
especificada
6
Aproximaciones para Diseñar
Sistemas Fiables
• Prevención de Fallos: se refiere al intento
de impedir que cualquier posibilidad de
fallo se cuele en el sistema antes de que
esté operativo.
• Tolerancia a Fallos: hace posible que el
sistema continúe funcionando incluso ante
la presencia de fallos.
Ambas intentan producir sistemas con modos de fallo bien definidos
7
Prevención de Fallos
• Fases en la prevención de fallos: evitación y
eliminación
– Fase Evitación se intenta limitar la introducción de
componentes potencialmente defectuosos durante la
construcción del sistema.
• Hardware:
– Componentes más fiables dentro de las restricciones de coste
y prestaciones dadas.
– Técnicas exhaustivamente refinadas para la interconexión de
componentes y el ensamblado de subsistemas.
– Aislamiento para protegerlo de formas de interferencia
esperadas.
8
Prevención de Fallos
– Fase Evitación Cont.
• Software:
– Especificación de requisitos rigurosas, si no formales.
– Metodología de diseño.
– Lenguajes que faciliten la abstracción de datos y la
modularidad.
– Herramientas de ingeniería de software para ayudar en
la manipulación de los componentes de software y en la
gestión de complejidad.
9
Prevención de Fallos
• Fase Eliminación de Fallos:
– Procedimientos para encontrar y eliminar las
causas de los errores.
• Revisores de diseño.
• Verificación de programas
• Inspecciones del código.
• Pruebas del sistema.
10
Prevención de Fallos
Nota: Como los componentes de HW pueden fallar la
prevención de fallos puede ser inapropiada cuando la
frecuencia o la duración de los tiempos de reparación
resulten inaceptables o cuando no se pueda acceder al
sistema para actividades de mantenimiento y
reparación.
11
Tolerancia a fallos.
• Niveles de tolerancia a fallos de un sistema:
– Tolerancia total frente a fallos: el sistema continua en
funcionamiento en presencia de fallos, aunque por un
período limitado, sin una pérdida significativa de
funcionalidad o prestaciones. (Sistemas de
seguridad)
– Degradación controlada( o caída suave) el sistema
continua en operación. Si los sistemas son muy
complejos y deben funcionar de forma continua con
requisitos de alta disponibilidad.
– Fallo seguro: el sistema cuida de su integridad
durante el fallo acaptando una parada temporal de su
funcionamiento.
12
Degradación controlada y recuperación en el sistema de
control del tráfico aéreo.
Funcionalidad completa dentro
del tiempo de respuesta
especificado
Funcionalidad mínima
Precisa para mantener el
control aéreo básico
Funcionalidad de emergencia
sólo para dar separación
Entre aviones.
Mecanismo de respaldo adyacente: cuando
ocurre un fallo catastrófico ( terremoto)
13
Redundancia
• Todas las técnicas utilizadas para
conseguir tolerancia a fallos se basan en
añadir elementos extra al sistema para
que detecte y se recupere de los fallos,
estos componentes no son necesarios
para el normal funcionamiento del sistema
(Redundancia protectora)
• Tipos: Hw y SW
14
Redundancia HW
• Estática o Enmascarada:
– TMR-Triple Modular Redundancy consiste en tres componentes
o idénticos y en circuitos de votación por mayoría. Los circuitos
comparan la salida de todos los componentes, y si alguna difiere
de las otras dos es bloqueada. En este caso se supone que el
fallo se debe a un error transitorio o al deterioro de algún
componente.
– NMR- redundancia N modular
• Dinámica: es la redundancia aportada dentro de un
componente que hace que el mismo indique que la
salida es errónea. ( dotar de detección de errores: la
suma de comprobación en las transmisiones de
comunicaciones y las paridades de bits en las memorias
15
Software- Tolerancia de fallos
(causa-errores de diseño)
• Dinámica:
– Detección y recuperación de errores: similar
al enfoque de redundancia dinámica de HW,
en el sentido de que se activan los
procedimientos de recuperación después de
haberse detectado el error.
• Estática - Programación de N-versiones:
similar al de redundancia enmascarada
del HW
16
Programación de N-versiones
Versión 1
Versión 2
estatus
Versión 3
estatus
estatus
voto
vote
voto
Director
17
Programación de N-versiones
Aspectos principales
T1
T2
> Tth
T3
no
> Tth
si
> Tth
Cada versión produce
un resultado diferente
pero
correcto(comparación
consistente)
si
P1
P2
P3
no
> Pth
> Pth
> Pth
si
18
V1
V2
V3
Programación de N-versiones
• Aspectos principales:
– Especificación Inicial Un error en la
especificación permanecerá en las Nversiones.
– Independencia en el diseño: Un sistema de
tres versiones es más fiable que un sistema
de alta calidad de versión única.
– Presupuesto adecuado: con la mayoría de los
sistemas embebidos el principal coste se
debe al software.
19
Redundancia de software dinámica
• Detección de errores
• Confinamiento y valoración de daños.
– Construcción de cortafuegos
• Recuperación del error
– Diagnosis del error.
– Existe un intervalo de tiempo entre el fallo y la
manifestación del error asociado.
– Estrategias: recuperación hacia delante (excepción
asíncrona) y hacia atrás (bloques de recuperación).
• Tratamiento del fallo y continuación del servicio.
20
Redundancia de software dinámica
• Técnicas de detección de errores
– En el entorno – HW ( violación de protección,
desbordamiento aritmético) SW (valor fuera de rango,
referencua a apuntador nulo)
– En la aplicación
•
•
•
•
•
•
•
Comprobación de réplicas
Comprobaciones temporales
Comprobaciones inversas Una entrada y una salida.
Códigos de comprobación checksum, corrupción de datos
Comprobaciones de racionalidad (aserciones)
Comprobaciones estructurales
Comprobaciones de racionalidad dinámica
21
Mecanismo del bloque de
recuperación
Restaura punto de
recuperación
fallo
Establece punto
de recuperación
Alguna
alternativa
Si
Ejecuta
siguiente
alternativa
Pasa
Evalúa test
De
aceptación
Descarta punto
de recuperaciónint
No
Falla bloque
De recuperación
22
Redundancia dinámica y
excepciones
• Una excepción es la ocurrencia de un
error.
• Generar una excepción es mostrar la
condición de excepción que ha causado
dicha excepción.
• La respuesta es la captura de la
excepción.
23
Manejo de excepciones
• Representación de excepciones: de forma
explícita o implícita.
• Dominio de un manejador de excepciones:
con cada manejador viene asociado un
dominio que significa la región cómputo
durante la cual se activará el manipulador
si aparece una excepción. El dominio se
encuentra asociado a un bloque, un
subprograma o una sentencia.
24
Ejemplos
• C
if(llamada_funcion(parametros) == Un_Error) {
/* código para el manejo de errores */
} else {
/*código para el retorno normal*/
};
25
Ejemplos
• C++ - definición del dominio
try {
// sentencias que pueden generar una
//excepción
}
catch (ExceptionType e) {
// manejador para e e
}
26
Manejo de excepciones
• Propagación de excepciones: Cuando se
lance una excepción, puede que no exista
ningún manejador de excepciones en el
dominio que la encierra. En este caso
podrá propagarse la excepción hacia el
siguiente nivel de dominio que lo
envuelve, o podrá considerarse como un
error de programación.
27
Modelo de Reanudación
Hq
P invoca Q
Hr genera
la excepción
q
P
Hq reanuda Hr
4
5
1
Hr
Q invoca R
R genera
la excepción
r
Q
Hq reanuda R
2
6
3
R
28
Modelo
deTerminación
Procedimiento P
1
P invoca Q
Procedimiento Q
3
Q invoca R
Procedimiento R
5
2
4
Generada la
excepción r
8
Manejador
visto
6
Termina
el
procedimiento
7
Manejador
para r
29
Manejo de excepciones
• El modelo de reanudación o de
terminación determina la acción a tomar
tras el manejo de una excepción. Con el
modelo de reanudación el invocador de la
excepción se reanuda en la sentencia
posterior a la que provocó la excepción.
Con el modelo de terminación el bloque o
procedimiento que contiene el manejador
es terminado, y se pasa el control al
bloque o procedimiento que lo llamó.
30
Tema 3: Concurrencia
Programación Concurrente
Teoría y ejemplos de implementación
1
Programación Concurrente
• Denominamos programación concurrente a la
notación y técnicas de programación que expresan
el paralelismo potencial y que resuelven los
problemas resultantes de la sincronización y la
comunicación. La implementación del parlelismo
es un tema de los sistemas informáticos (SW y
HW) La importancia de la programación
concurrente está en que proporciona un entorno
abstracto donde estudiar el paralelismo sin tener
que enfrescarse en los detalles de implementación.
(Ben-Ari, 1982)
2
Programación Concurrente
• Los sistemas operativos proporcionan mecanismos
para crear procesos concurrentes. Cada proceso se
ejecuta en su propia máquina virtual, para evitar
interferencias con otros procesos no relacionados.
• Los sistemas operativos modernos permiten crear
procesos dentro del mismo programa accediendo
de modo compartido, y sin restricciones, a la
memoria común ( estos procesos suelen llamarse
hilos o hebras).
3
Multiprogramación
• Los procesos son ejecutados,
concurrentemente, compartiendo uno o más
procesadores (c/u con su memoria)
• El kernel del sistema operativo multiplexa
los procesos en los procesadores
Proc7
Proc1
Proc2
M1 CPU1
Proc6
M2 CPU2
Proc5
Proc3
Proc4
4
Multiprocesamiento
• Se ejecuta un proceso en cada procesador
• Los procesos comparten una memoria en
común
Proc1
CPU1
Proc2
Proc3
Proc4
CPU2
CPU3
CPU4
Memoria común
5
Procesamiento distribuido
• Los procesos están conectados a través de
una red de comunicación
• Cada procesos cuenta con su memoria
Proc1
Proc2
Proc3
Proc4
CPU1
CPU2
CPU3
CPU4
M1
M2
M3
M4
red de comunicación
6
Diagrama de estados sencillo para un
proceso
NOexistente
existente
NO
Noexistente
existente
No
Creado
Creado
Inicialización
Inicialización
Terminado
Terminado
Ejecutable
Ejecutable
7
Programación concurrente
• Los sistemas operativos que se ajustan a
POSIX se debe distinguir entre
concurrencia de programas (procesos) y la
concurrencia dentro de un programa (hilos).
8
Programación concurrente
• Servicios fundamentales:
– La expresión de ejecución concurrente
mediante la noción de proceso.
– La sincronización de procesos.
– La comunicación entre procesos.
9
Programación concurrente
• Interacción entre procesos:
– Independiente: no se comunican o sincronizan
entre sí.
– Cooperativo: Se comunican con regularidad y
sincronizan sus actividades para realizar alguna
operación común.
– Competitivo: Para que los procesos obtengan
una proporción justa de recursos deben
competir entre sí.
10
Hilos
• Hilos: permiten múltiples flujos de control
que se ejecutan de manera concurrente
dentro de uno de sus programas. Los hilos
permiten que su programa emprenda varias
tareas de cómputo al mismo tiempo, una
característica que da soporte al programa
orientado a eventos.
11
Hilos y Programación Concurrente
• Un ordenador tiene un CPU o elemento de
procesamiento (PE Processing Element) que
ejecuta un programa a la vez.
• Un programa es un proceso que se ejecuta una
sola vez.
• Dentro de un proceso el control suele seguir a un
solo hilo de ejecución que por lo general empieza
con el primer elemento de main, recorriendo una
secuencia de instrucciones y terminando cuando se
regresa a main. Programación de un solo hilo.
12
Hilos y Programación Concurrente
• Los lenguajes de programación como el C
tienen un único hilo (hebra) de control.
Existe una única traza.
• El término concurrente indica paralelismo
potencial.
• C/C++ POSIX permite múltiples tareas y
programación concurrente.
13
Hilos y Programación Concurrente
• Los programas de un solo hilo son buenos
para cálculos sencillos.
• Los programas dinámicos, interactivos,
controlados por eventos, suelen incluir
varias partes activas que se ejecutan de
manera independiente e interactúan o
cooperan de alguna manera para alcanzar
las metas.
14
Hilos y Programación Concurrente
• Se pueden utilizar hilos para desacoplar
actividades
con
velocidades
de
procesamiento muy diferentes.
• Un programa de procesamiento multihilos
tiene que coordinar varias actividades
independientes y evitar la posibilidad de
que se encimen entre sí.
15
Programa de Varios Hilos
Main()
start()
run()
run()
run()
16
Pthreads – hilos en C/C++
• Un hilo es un flujo de control separado que
ejecuta su propio código. En C/C++ el hilo
es un objeto de la clase Pthread.
17
Pthreads – hilos en C/C++
• Lanzando Hilos. Un hilo en ejecución puede
lanzar o crear otros hilos creando objetos
tipo hilo e invocando sus métodos start para
que empiecen a ejecutarse
independientemente.
18
Pthreads – hilos en C/C++
• Control de hilo. Un hilo padre da lugar a un
hilo hijo al crear un objeto de Thread e
invocar a su método start, el cual causa que
el objeto se vuelva un nuevo hilo listo para
ejecutarse.
19
Hilos y Programación Concurrente
• Estos programas incluyen cuatro aspectos
nuevos e importantes que no incluyen los
programas de un solo hilo:
–
–
–
–
Exclusión mutua
Sincronización
Calendarización
Punto muerto
20
Hilos y Programación Concurrente
Exclusión mutua
• Los hilos de un programa suelen necesitar ayuda
para lograr de una cierta tares. Esta cooperación
generalmente implica el acceso de diferentes hilos
a las mismas construcciones del programa.
• Cuando varios hilos comparten un recurso común
(campo, arreglo u otro objeto) puede darse el
acceso simultáneo de más de un hilo.
• Es necesario organizar un acceso mutuamente
exclusivo a a cantidades compartidas. Solo un hilo
puede accesar al mismo tiempo la cantidad
protegida por exclusión mutua (mutex).
21
Tema 4: Tareas
Exclusión Mutua
Mutex
1
Guía
•
•
•
Interacción y sincronización procesos
Corutinas, fork-join y cobegin-coend
Exclusión mutua, sección crítica y condición de
competencia
• Soluciones problema exclusión mutua
2
Características procesos
concurrentes
• Los procesos son concurrentes si existen
simultáneamente
• Pueden funcionar en forma totalmente
independiente, unos de otros
• Pueden ser asíncronos lo cual significa que
en ocasiones requieren cierta sincronización
y cooperación
3
Interacción entre procesos
• Para poder cooperar, procesos concurrentes
deben comunicarse y sincronizarse
• Comunicación permite ejecución de un
proceso para influenciar ejecución de otro
• Comunicación entre procesos esta basada en
el uso de variables compartidas o envío de
mensajes
4
Representación de procesos
• La estructura del programa permite localizar los
segmentos que pueden ejecutarse concurrentemente
• Mecanismos básicos para representar la ejecución
concurrente:
• corutinas
• fork-join
• el enunciado co-begin
• Pueden usarse para especificar un número estático o
dinámico de procesos
5
Corutinas
• Propuestas por Conway en 1963
• Corutinas son subrutinas que permiten una
transferencia de control de una forma
simétrica más que jerárquica
• Cada corutina puede ser vista como la
implementación de un proceso
• Bien usadas, son un medio para organizar
programas concurrentes que comparten un
mismo procesador
6
Elementos corutinas
• El enunciado resume
– transfiere control a la corutina mencionada
– guarda información necesaria para controlar la
ejecución de regreso
• El enunciado call
– inicializa el cálculo de la corutina
• El enunciado return
– transfiere el control de regreso al procedimiento
7
que realizó un call
Comentarios corutinas
• La ejecución, por parte de un proceso, de
resume provoca una sincronización
• No son adecuadas para un verdadero
procesamiento paralelo
• Son procesos concurrentes en el que el
“switcheo de procesos” ha sido
completamente especificado y no dejado al
kernel o a la implementación
• Lenguajes: SIMULA I y SL5
8
Enunciados fork-join
• Enunciado fork especifica que una rutina
puede empezar su ejecución
• La rutina invocada y la rutina invocadora
proceden concurrentemente
• Para sincronizar invocada e invocadora, esta
última puede ejecutar un join
• Enunciado join retrasa ejecución rutina
invocadora hasta que la rutina invocada
termine
9
Comentarios fork-join
• Enunciados fork-join puede aparecer en
condicionales y ciclos
– es necesario entender bien la ejecución del programa,
para saber que rutinas se van a ejecutar concurrentemente
• Cuando se usa de forma disciplinada los
enunciados son prácticos y poderosos
– fork proporciona un mecanismo para la creación
dinámica de procesos
– enunciados similares también están incluidos en PL/I y
Mesa
10
El enunciado cobegin
• Es una forma estructurada de denotar una
ejecución concurrente
• Por ejemplo:
cobegin S1 || S2 || … || Sn coend
– denota una ejecución concurrente de S1, S2, …
Sn
– cada uno de los Si’s puede ser cualquier
enunciado incluyendo un cobegin o un bloque
con declaraciones locales
11
Características cobegin
• La ejecución de un cobegin solo termina cuando la
ejecución de todos los Si’s terminó
• No es tan potente como fork-join, pero es
suficiente para especificar la mayor parte de los
cálculos concurrentes
• Sintaxis hace explícito cuales rutinas son
ejecutadas concurrentemente
• Variantes implementadas en ALGOL68, CSP,
Edison y Argus
12
Ejecución concurrente:
Tareas/Procesos
• Declaración explícita de los procesos:
task body Process is
begin
. . .
end;
Ejemplo: ADA
13
Ejecución Concurrente Posix
• Mecanismos: fork y pthreads.
• fork crea un nuevo proceso
• pthreads son una extensión POSIX y
permite que un hilo/hebra sean creados.
• Todos los hilos/hebras tienen atributos que
permiten su manipulación.
14
Ejemplo
15
Brazo del Robot en POSIX
const int WORK=1000000;
const int MAXREGS=50;
pthread_attr_t attributes;
pthread_t xp, yp, zp;
time_t inicio, tiempo;
int new_setting(int *D);
void move_arm(int *D, int P);
void *controller(void * dim);
16
int main() {
int X, Y, Z, rc;
void *result;
printf("Programa ejecutandose...Para finalizar, cierre la ventana. \n");
inicio=time(NULL); // cogemos tiempo de referencia
X = 1,
Y = 2;
Z = 3;
/* se establecen los atributos por defecto */
rc=pthread_attr_init(&attributes);
checkResults("pthread_attr_init()", rc);
rc=pthread_create(&xp, &attributes, controller, (void *)&X);
checkResults("pthread_create(X)", rc);
rc=pthread_create(&yp, &attributes, controller, (void *)&Y);
checkResults("pthread_create(Y)", rc);
rc=pthread_create(&zp, &attributes, controller, (void *)&Z);
checkResults("pthread_create(Z)", rc);
pthread_join(xp, (void **)&result);
/* Necesario para bloquear el programa principal
La rutina pthread_join espera a que finalizen las hebras,
con lo que se queda en espera pues las hebras no finalizan
*/
exit(EXIT_FAILURE);
/* El programa no debería terminar con lo que si finaliza se devuelve un
error */
}
17
void *controller(void * dim)
{
int position, setting, j;
double result;
int *eje;
eje=(int*)dim;
position = 0;
while (1) {
/*
Bucle para que la CPU este
trabajando y las hebras se puedan ir
alternando
*/
result=0;
for (j=0; j < WORK; j++)
result = result + j/2;
move_arm(eje, position);
setting = new_setting(eje);
position = position + setting;
}
/* Notar que la hebra no finaliza */
}
18
/*
Rutina de simulacion del movimiento del brazo.
Se graban mensajes en un fichero para cada eje de movimiento
*/
void move_arm(int *D, int P)
{
FILE * pFile;
char c[50];
long t;
if (P<MAXREGS ) // ponemos condición para grabar en fichero sino se grabaría indefinidamente
{
if (P==0) // creamos los ficheros al inicio del movimiento
{
if (*D==1) pFile = fopen ("ejex.txt","w");
if (*D==2) pFile = fopen ("ejey.txt","w");
if (*D==3) pFile = fopen ("ejez.txt","w");
}
else
{
if (*D==1) pFile = fopen ("ejex.txt","a+");
if (*D==2) pFile = fopen ("ejey.txt","a+");
if (*D==3) pFile = fopen ("ejez.txt","a+");
}
tiempo=time(NULL);
t=tiempo-inicio;
sprintf(c, "[clock: %d seg.] Valor de posicion P=%d \n",t,P);
fputs (c,pFile);
fclose (pFile);
}
}
19
A implementar new_setting
20
Comunicación y Sincronización
• El comportamiento correcto de un programa concurrente
depende estrechamente de la sincronización y la
comunicación entre procesos.
• Sincronizar es satisfacer las restricciones en el entrelazado
de las acciones de diferentes procesos. Una acción
particular de un proceso sólo ocurre después de una acción
específica de otro proceso.
• La sincronización debe llevar simultáneamente a dos
procesos a estados predefinidos.
• Comunicar es pasar información de un proceso a otro. Los
dos conceptos están ligados, puesto que algunas formas de
comunicación requieren sincronización y la sincronización
puede ser considerada como comunicación sin contenido.
21
La exclusión mutua
• Garantizar que si un proceso utiliza una variable o
algún recurso compartido, los demás no podrán
usarlos al mismo tiempo
• Conocida como mutex
• Cada proceso debe verificar que durante cierta
parte del tiempo, puede tener acceso a la memoria
compartida de archivos o realizando labores
críticas que pueden llevar a conflictos
22
Exclusión Mutua
• La comunicación entre procesos se basa
normalmente o en el uso de variables
compartidas o en el paso de mensajes.
• Aunque las variables compartidas parecen
una forma directa de pasar información
entre procesos su uso debe ser restringido.
Debido a los problemas de actualización.
23
Exclusión mutua
• Una secuencia de sentencias que debe
aparecer como ejecutada indivisiblemente
se denomina sección crítica.
• La sincronización que se precisa para
proteger una sección crítica se conoce como
exclusión mutua.
24
Sección crítica
• Parte del código en el cual se tiene acceso a
una variable o archivo compartido
• Corolario:
– si dos procesos no están al mismo tiempo en
sección crítica, podemos evitar las condiciones
de competencia
25
Asignación recursos
• Sean n procesos que entran en conflicto por el
acceso a un recurso único no compartible
• Recurso a usar en sección crítica
• Necesario usar un protocolo formado de tres
partes:
protocolo de adquisición
<uso del recurso en sección crítica>
protocolo de liberación
26
Gestión de Mutex
27
Gestión de Mutex
Introducción
•
•
•
Mutex es una abreviación de "mutual exclusion". Las rutinas Mutex ofrecen una
de las principales herramientas para implementar sistemas de sincronización de
hebras y de protección de variables compartidas cuando se accede en modo
escritura desde varios hilos de forma simultánea. Cuando hablamos de mutex
nos referimos en realidad a la variable asociada al mutex o “mutex variable”
El mutex actúa como una llave, protegiendo el acceso a recursos compartidos. El
concepto básico subyacente a las rutinas mutex usadas en Pthread es que
únicamente 1 hebra puede tener el control o bloquear una variable mutex en un
instante del tiempo, es decir, si varias hebras intentan simultáneamente tomar el
control de la variable, únicamente una de ellas lo conseguirá, poniéndose las
demás hebras en ‘cola de espera’ hasta que el mutex sea desbloqueado por la
hebra propietaria.
Los mutex se pueden utilizar para evitar situaciones inconsistentes por ejemplo
en una situación en que se accede simultaneamente a una variable que acumula
el saldo de una cuenta podríamos encontrarnos con la siguiente situación:
Tiempo
T1
Hebra 1
Saldo leído: 8000
T2
T3
T6
•
•
Saldo
8000
Saldo leído: 8000
Ingresa: 200
T4
T5
Hebra 2
8000
8000
Ingresa: 200
Actualiza Saldo: 8000+200
8000
8200
Actualiza Saldo: 8000+200
8200!!
En este ejemplo, la hebra que actualiza el saldo debe bloquear el mutex antes de
la lectura del saldo y desbloquearlo una vez actualizado su valor. A menudo las
acciones que efectúa una hebra propietaria de un mutex es actualizar variables
globales. Esta es la manera de garantizar que el resultado de la actualización será
el mismo que se hubiera producido si solamente tuviéramos una hebra en
ejecución.
La secuencia típica de instrucciones de un mutex es la siguiente:
o Inicializar y crear el mutex
o Varias hebras intentan bloquear el mutex
o Unicamente una de ellas lo consigue, pasando a ser la hebra propietaria
o La hebra propietaria efectúa una serie de acciones
o La hebra propietaria desbloquea el mutex
o Otra hebra toma el control del mutex y se repite la secuencia
o Finalmente se destruye el mutex.
Creación y destrucción de Mutex
•
POSIX define las siguientes rutinas para la inicialización y destrucción de
MUTEX:
int pthread_mutex_init(pthread_mutex_t *mutex, const
thread_mutexattr_t *attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutexattr_init( pthread_mutexattr_t *attr );
int pthread_mutexattr_destroy( pthread_mutexattr_t *attr );
Pthread_mutex_init:
•
Las varibales Mutex deben declarase con el tipo pthread_mutex_t, y deben
inicializarse antes de poder trabajar con ellas. Cuando se crea, la variable mutex
está en estado ‘desbloqueada’. Hay 2 maneras de inicializar una variable Mutex:
1. Estáticamente en el momento en que se declara la variable, mediante la
macro PTHREAD_MUTEX_INITIALIZER. En este caso se inicializa con los
valores por defecto:
pthread_mutex_t mymutex = PTHREAD_MUTEX_INITIALIZER;
2. Dinámicamente con la rutina pthread_mutex_init() routine. Este
•
método permite especificar los atributos del mutex, attr.
La variable attr apunta al objeto que se utiliza para establecer las propiedades
del mutex y debe ser del tipo pthread_mutexattr_t. Si se deja a NULL, el mutex
se inicializa con las propiedades por defecto con lo que el resultado es
equivalente a inicializarlo estáticamente.
Pthread_mutex_destroy:
•
La rutina pthread_mutex_destroy() debe usarse para destruir un mutex no
bloqueado que no deba volver a utilizarse en el programa.
Pthread_mutexattr_init/destroy:
•
•
•
Las rutinas pthread_mutexattr_init() y pthread_mutexattr_destroy()
se usan para crear y destruir respectivamente los objetos ‘atributo de mutex’
pthread_mutexattr_init() inicializa el objeto apuntado por attr con los
valores por defecto. Estos atributos actuan como parámetros adicionales que se
pasan en la creación del mutex. Cuando un mutex es inicializado con sus
atributos, dichos atributos se copian dentro de mutex por lo que, una vez
inicializado el mutex, los atributos ya no son necesarios para el resto del
programa y se pueden destruir.
Si se desean crear los mutex con los atributos por defecto, no es necesario
inicializar explícitamente con pthread_mutexattr_init().
Bloqueo y desbloqueo de Mutex
•
POSIX define las siguientes rutinas para la bloqueo y desbloqueo de MUTEX:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
Pthread_mutex_lock:
•
La rutina pthread_mutex_lock() se utiliza para que una hebra solicite la
propiedad del mutex especificado por la variable mutex. Si el mutex esta
bloqueado en ese instante por otra hebra, la llamada a la rutina bloqueará la
ejecución de la hebra solicitante hasta que el mutex se desbloquee por la hebra
propietaria.
Pthread_mutex_trylock:
•
pthread_mutex_trylock() intenta bloquear el mutex especificado por la
variable mutex. Sin embargo, si el mutex está bloqueado, la rutina retorna de
forma inmediata a la hebra solicitante (sin esperar a su desbloqueo) pero con un
código de retorno que indica que el mutex está bloqueado. Esta rutina puede ser
útil para evitar situaciones de “deadlock” en que conditions, as in a priorityinversion situation.
Pthread_mutex_unlock:
•
•
•
•
•
•
•
pthread_mutex_unlock() desbloquea el mutex si es llamado por la hebra que
lo ha bloqueado (hebra propietaria del mutex). Se debe llamar a esta rutina una
vez la hebra ha completado las instrucciones que debían ser protegidas de
accesos concurrentes. Se produce un error si el mutex ya está desbloqueado o
bien la hebra no es propietaria del mutex.
Cuando varias hebras estan esperando el desbloqueo de un mutex para adquirirlo
como propietarias, a menos que se haya aplicado algún mecanismo de
planificación (priorización), la asignación del mutex a una u otra hebra se delega
al sistema operativo, el cual, lo hará de forma más o menos aleatoria.
Es responsabilidad del programador hacer buen uso de la utilización del mutex.
En el siguiente ejemplo (en el cual hay una hebra para la cual no se ha
especificado mutex) se ha producido un error de lógica de programación ya que
el resultado final será aleatorio:
Hebra 1(*)
Lock
A = 2
Unlock
Hebra 2(*) Hebra 3(***)
Lock
A = A+1
A = A*B
Unlock
(*) prioridad alta, (**) prioridad media, (***) prioridad baja
Ejemplo: Uso de Mutex
Programa de ejemplo - Mutex
En este programa se muestra la utilización de mutex para garantizar la
exclusividad en la actualización de variables globales.
Se trata de un ejercicio a efectos meramente didácticos.
Se toma el valor de la variable 'saldo' al inicio del bucle que simula que
la CPU está trabajando y se incrementa el saldo con el valor 'ingreso'
Deberemos observar el comportamiento del programa con mutex y sin
mutex..
#include
#include
#include
#include
#include
<pthread.h>
<windows.h>
<stdio.h>
<stdlib.h>
"check.h"
int const NUMTHRDS=4;
int const INGRESO=1;
int const WORK=1000;
int sw_mutex;
no
// variable para indicar al programa si utiliza mutex o
/* Definimos variables globales y variables mutex */
int num_thrd[NUMTHRDS];
double Saldo;
pthread_t callThd[NUMTHRDS];
pthread_mutex_t mutexsum;
void *acumsaldo(void *arg);
/*
El programa principal crea las hebras que actualizan el saldo.
Antes de crear las hebras se inicializa la variable saldo.
Para evitar que las hebras actualizen simultaneamente la variable
debe usarse mutex. El programa principal debe esperar a que acaben
todas las hebras antes de presentar el resultado final.
*/
int main (int argc, char *argv[])
{
int i, rc;
void *status;
pthread_attr_t attr;
sw_mutex=-1;
while (sw_mutex<0 || sw_mutex>1) {
cout<<"Con mutex, entre 1, sin mutex, entre 0: ";
cin>>sw_mutex;
}
if (sw_mutex==0) cout<<endl<<"Sin mutex"<<endl<<endl;
else cout<<endl<<"Con mutex"<<endl<<endl;
Saldo=0;
rc=pthread_mutex_init(&mutexsum, NULL);
checkResults("pthread_mutex_init()", rc);
rc=pthread_attr_init(&attr);
checkResults("pthread_attr_init()", rc);
// creamos la hebra en modo "joinable" a efectos de portabilidad
rc=pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
checkResults("pthread_attr_setdetachstate()", rc);
for(i=0;i<NUMTHRDS;i++)
{
// asignamos valores a la variable pasada por parametro.
Identificará el mutex
num_thrd[i]=i+1;
// creamos la 4 hebras
rc=pthread_create( &callThd[i], &attr, acumsaldo, (void
*)&num_thrd[i]);
checkResults("pthread_create()", rc);
}
rc=pthread_attr_destroy(&attr);
checkResults("pthread_attr_destroy()", rc);
/* Esperamos la finalización de las 4 hebras */
for(i=0;i<NUMTHRDS;i++) {
rc=pthread_join( callThd[i], &status);
checkResults("pthread_join()", rc);
}
/* Despues del join, presentamos el resultado */
printf ("Saldo = %f \n", Saldo);
pthread_mutex_destroy(&mutexsum);
system("pause");
pthread_exit(NULL);
}
void *acumsaldo(void *arg)
{
int i,j, *hebra;
double saldo,k;
hebra = (int *) arg;
// Bloqueo del mutex antes de actualizar la variable.
if (sw_mutex==1) pthread_mutex_lock (&mutexsum);
saldo=Saldo;
printf ("Saldo inicio hebra %d %f \n",*hebra, saldo);
for (i=1; i<=WORK ; i++)
{
saldo += INGRESO;
}
//
Sleep(1);
// para simular consumo CPU
for (i=1; i<=2*WORK ; i++)
for(j=1; j<=WORK ; j++) k=i/j;
Saldo += saldo;
printf ("Saldo final hebra %d %f \n",*hebra, Saldo);
// Desbloqueo del mutex antes de actualizar la variable.
if (sw_mutex==1) pthread_mutex_unlock (&mutexsum);
pthread_exit((void*) 0);
}
Descargar
Colecciones de estudio