Está en la página 1de 52

Informática Industrial

Nociones de
Programación Concurrente
y
Sistemas en Tiempo Real

Alberto Hamilton
ULL
Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++11


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 2/52


Concurrencia: Planteamiento
Proceso: Instancia de un programa en ejecución
– Procesos concurrentes

solapan su ejecución
– Comienzo P2 después comienzo P1 antes fin P1
– Procesos paralelos

se ejecutan al mismo tiempo

Concurrencia = Paralelismo potencial

P1 P1

P2 P2

P3 P3
t t

Concurrencia y STR 3/52


Procesos UNIX
– Tienen un identificador: PID
– Tienen un proceso padre: PPID

init primer proceso del sistema
– Hijos mueren antes que los padres

Deben ser esperados por sus padres
– caso contrario quedan zombi
– No comparten memoria

solo ficheros abiertos
#Lista todos los procesos
ps xa
#Lista procesos e indica jerarquía
ps xaf
#Lista más información
ps xal
#Lista personalizada
ps xaf -o '%p %P %u %y %a'
Concurrencia y STR 4/52
Procesos UNIX: manejo
– creación proceso hijo: fork()

se crea proceso copia exacta del padre

fork()devuelve: 0 si hijo, el PID del hijo si padre
– esperar proceso hijo: wait()
– remplazo proceso: exec()

ejecutar otro programa
pid_t childpid;
if ((childpid = fork()) == 0) {
/* El código del hijo va aquí*/
/* proceso con otro programa */
exec("/usr/ls");
} else {
int status; pit_t pidHijo;
/* El código del padre va aquí */
pidHijo = wait(&status)
/* hijo terminó */
}
Concurrencia y STR 5/52
Concurrencia: problemas
Problemas si procesos compiten/colaboran
– Recurso compartido: memoria, hardware, etc.
– Sección crítica: instrucciones que acceden al recurso
compartido
Problema 1: Exclusión mutua
– sólo debe haber un proceso en su sección crítica
– ejemplos:

impresora

variable compartida

Concurrencia y STR 6/52


Concurrencia: problemas
Problema 2: Sincronización
– Acceden al recurso cuando en estado adecuado
– Bloquear procesos si estado no adecuado
Ejemplo: buffer capacidad limitada

Consumidor:
– acceder cuando buffer no vacío, si no bloquear

Productor:
– acceder cuando buffer no lleno, si no bloquear

Productor1 Buffer Consumidor1

Consumidor2
Productor2

ProductorN ConsumidorM

Concurrencia y STR 7/52


Concurrencia: Propiedades
Las soluciones deben cumplir
– Seguridad → sin corrupción del recurso

Exclusión mutua

Sincronización

No interbloqueo
– que exista progreso
– Viveza → sin pérdidas de tiempo

No interbloqueo activo
– consumo de CPU sin producir

No inanición: postergación indefinida
– proceso nunca puede acceder al recurso
– aparece cuando se definen prioridades

Concurrencia y STR 8/52


Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++11


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 9/52


Concurrencia: Semáforos
Solución propuesta por Disjktra
– para resolver problemas de la concurrencia
Operaciones: (indivisibles)
– sem_init(sem_t* s,int comp,int v)

hace que s valga v
– sem_wait(sem_t* s)

Si s>0 entonces s=s-1

caso contrario bloquea al proceso en s
– sem_post(sem_t* s)

Si hay procesos bloqueados en s, desbloquea un proceso

caso contrario s=s+1

Concurrencia y STR 10/52


Semáforos: Exclusión mutua
Solución:
– Inicialización común previa

#include <semaphore.h>
// código inicial
sem_t sem;
sem_init(&sem,0,1);

– En cada proceso:
/* cada proceso */
sem_wait(&sem); //protocolo de entrada

/* Sección crítica */
/* Manejo de recurso compartido */

sem_post(&sem); //protocolo de salida

Concurrencia y STR 11/52


Semáforos: Sincronización
//codigo inicialización
//Buffer de N posiciones
sem_t exmu, vacios, llenos;
sem_init(&exmu, 0, 1);
sem_init(&vacios,0, N);
sem_init(&llenos,0, 0);

//Consumidor //Productor
while(1) { while(1) {
sem_wait(&llenos); /* Produce dato */
sem_wait(&exmu);
sem_wait(&vacios);
/* retira elemento */ sem_wait(&exmu);

sem_post(&exmu); /* añade elemento */


sem_post(&vacios);
sem_post(&exmu);
/* Consume el dato */ sem_post(&llenos);
} }

Concurrencia y STR 12/52


Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++11


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 13/52


Hilos (threads)
Secuencia de ejecución independiente
– dentro de un proceso
– entidades ligeras

Crear un proceso tarda 10 veces más que crear hilo
– comparten memoria:

Variables globales

Memoria dinámica (new)

Permiten concurrencia más fácilmente

Concurrencia y STR 14/52


C++ hilos
A partir de C++11
– crear objeto std::thread

constructor se le pasa:
– función a ejecutar
– parámetros que espera la función

#include <thread>
void funcionHilo1(int v1, double v2)
{
// código a ejecutar en el hilo
}
int main()
{
std::thread hilo1(funcionHilo1, 4, 3.3);
std::thread hilo2(funcionHilo1, 10, -5.97);
//hilo1, hilo2 e hilo principal
// se ejecutan concurrentemente
...
Concurrencia y STR 15/52
C++ hilos: métodos
Si queremos ejecutar método
– constructor se le pasa

puntero al método

puntero al objeto

parámetros que espera la función

class X {
public:
void doWork(int n);
};
int main()
{
X my_x;

std::thread h(&X::doWork,&my_x, 4);


...
Concurrencia y STR 16/52
C++ hilos: id
Cada hilo tiene su identificador único
– se obtiene con get_id()

#include <thread>
int main()
{
std::thread hilo1(fHilo1, 4, 3.3);
std::thread hilo2(fHilo1, 10. -5.97);

std::thread::id id1 = hilo1.get_id();


std::thread::id id2 = hilo2.get_id();

id1 != id2; //son distintos


...

Concurrencia y STR 17/52


C++ hilos: join
Necesario esperar que hilo termine
– antes de que el objeto thread se destruya

antes de que salga de ámbito
– con el método join()

borra toda la información del hilo

no posible hacer dos join() sobre mismo hilo
#include <thread>
int main()
{
std::thread hilo1(fHilo1, 4, 3.3);
std::thread hilo2(fHilo1, 10. -5.97);
//hilo1, hilo2 e hilo principal
// se ejecutan concurrentemente
hilo1.join(); //esperamos hilo 1 termine
hilo2.join(); //esperamos hilo 2 termine
}
Concurrencia y STR 18/52
C++ hilos: deatach
Si no queremos esperar por el hilo
– invocamos método detach()

se sigue ejecutando en backgroud (sin control)

borra toda la información del hilo

después no podremos hacer join()

#include <thread>
int main()
{
std::thread hilo1(fHilo1, 4, 3.3);
hilo1.detach(); //desconectamos hilo 1
// hilo1 se seguirá ejecutando
// se borran sus recursos en cuanto termine
}

Concurrencia y STR 19/52


C++: exclusión mutua
Dispone de mutex → clase std::mutex
– como semáforos inicializados a 1

banderín
– métodos:

lock() → trata de ganar el mutex (wait() semáforo)

unlock() → libera el mutex (post() semáforo)

#include <mutex>
std::mutex miMutex;

void fHilo1(int v1, double v2)


{
miMutex.lock(); // protocolo de entrada
// sección crítica
// acceso al recurso
miMutex.unlock(); // protocolo de salida
}

Concurrencia y STR 20/52


C++: lock_guard
Preferible utilizar ténica RAII
– Resource acquisition is initialization

creación de un objeto → adquisición del recurso

destrucción del objeto → liberación del recurso
– se consigue con std::lock_guard

libera mutex incluso si se produce excepción en Sección Crítica
#include <mutex>
std::mutex miMutex;
void fHilo1(int v1, double v2)
{
std::lock_guard<std::mutex> guard(miMutex);
// se ha hecho lock() sobre miMutex

// sección crítica
// sale de ámbito guard -> se libera miMutex
}
Concurrencia y STR 21/52
C++: unique_lock
Como lock_guard pero además:
– con métodos de mutex: lock() y unlock()
– se puede usar donde vaya mutex

std::mutex miMutex;
void fHilo1(int v1, double v2)
{
std::unique_lock<std::mutex> uLock(miMutex);
// se ha hecho lock() sobre miMutex
// sección crítica
uLock.unlock()

// código no crítico
uLock.lock()
// sección crítica
// sale de ámbito uLock -> se libera miMutex
}
Concurrencia y STR 22/52
C++: sincronismo
Se utilizan las variables de condición (VC)
– cola de hilos esperando condición de sincronización
– clase std::condition_variable
– asociadas a mutex que protege recurso
– wait() bloquea hilo y libera mutex

si el recurso en estado inadecuado

cuando se desbloquea tiene el mutex

condición puede no cumplirse
– volver a comprobar → bucle while
– notify_one() notify_all()

desbloquea el primer (todos) hilo bloqueado en la VC

invocar cuando estado del recurso a cambiado

Concurrencia y STR 23/52


C++: solución buffer
Ejemplo mutex y VC → solución buffer
Inicialización común:
// tamaño del buffer
#define TAMBUF 8
// numero de datos actuales en buffer
int NumDatos = 0;
// mutex exclusión mutua sobre buffer
std::mutex mBuffer;
// VC los que esperan por huecos
std::condition_variable vcHuecos;
// VC los que esperan por datos
std::condition_variable vcDatos;

Concurrencia y STR 24/52


C++: solución buffer
// Productor
{ // tratamos de meter dato en buffer
std::unique_lock<std::mutex> lck(mBuffer);
while(NumDatos == TAMBUF) {
// no hay hueco -> nos bloqueamos
vcHuecos.wait(lck);
}
// sección crítica -> metemos dato en el buffer
vcDatos.notify_one(); // ahora hay datos
} //lck sale de ámbito y se libera mBuffer

// Consumidor
{ // tratamos de sacar dato en buffer
std::unique_lock<std::mutex> lck(mBuffer);
while(NumDatos == 0) {
// no hay datos -> nos bloqueamos
vcDatos.wait(lck);
}
// sección crítica -> sacamos dato en el buffer
vcHuecos.notify_one(); // ahora hay huecos
} //lck sale de ámbito y se libera mBuffer
Concurrencia y STR 25/52
Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 26/52


Sistemas de Tiempo Real
Definiciones:
Sistema informático que tiene que responder a un estímulo de
entrada en un tiempo finito y especificado
– Interaccionan con el entorno
Podemos ver todos los sistemas como STR
– STR blando

no cumplir: degradación rendimiento
– ejemplo botón de televisor
– STR duro:

no cumplir: fallo, catástrofe
– sistema ABS, sistema de vuelo

blando STR duro


Concurrencia y STR 27/52
Características STR
Suelen tener alguna o varias de las siguientes
– Grande y complejo

supervisado constantemente
– Manipulan números reales

valores de sensores analógicos
– Extremadamente fiable y seguro

Tolerancia a fallos → redundancia

Si fallo → dejar sistema en estado seguro
– Concurrencia entre distintos componentes

sistema complejo se divide
– Interacción con interfaces hardware
– Atención a la eficiencia y entorno de ejecución

No portable sino óptimo

Aprovechar recursos de la máquina
Concurrencia y STR 28/52
Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 29/52


Medida y control del tiempo
En STR es fundamental:
– Medir tiempo
– Controlar duración de acciones
Clasificación las tareas
– Periódicas → intervalos fijos
– Esporádicas → interrupciones externas
Hay que solucionar:
– relojes / temporizadores
– retardos y tareas periódicas
– timeouts: tarea se alarga demasiado
– planificadores: que tarea ejecutar

Concurrencia y STR 30/52


Tiempo
Magnitud fundamental
– unidad: el segundo
Tiempo absoluto
– sistema de referencia

Origen = "época" (epoch)

Astronómico: UT0, UT1, UT2

Atómicos:
– IAT (átomo de cesio),
– UTC (IAT sincronizado con UT2)

Sufre ajustes: adelantos/retrasos

Tiempo relativo
– intervalos de tiempo

Concurrencia y STR 31/52


Hardware para el Tiempo
Tiempo absoluto: Relojes
– Siempre contando
– Sistema referencia externo

señales UTC

RTC alimentado por baterías
– Reloj hw interno

Generador de pulsos (tiks): contador + sw de gestión
– granularidad: frecuencia de los pulsos
– Intervalo: capacidad del contador (antes de desbordar)
– Habitualmente se combinan los 2 anteriores
Tiempo relativo: Temporizadores
– arranca y para
– varios en hw, pero número limitado
Concurrencia y STR 32/52
Monotonía del tiempo
Tiempo monótono → siempre creciente
– el contador no se desborda
Tiempo no monótono → no siempre creciente
– Contador se desborda

Pasa del valor máximo (por ejemplo 60) a 0

Utilizable para intervalos no superiores al desbordamiento
– Reloj calendario

no monótono por ajustes de sincronización
– segundos se añaden o quitan al final de año

Concurrencia y STR 33/52


Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 34/52


C++: Relojes
A partir de C++11 → definidos en <chrono>
Proporcionan la siguiente información:
– el valor actual del reloj → now()
– el tipo de valor que se obtiene → rep()
– el periodo de cambio (tick) → duration()

se expresa como std::ratio
– si el monótono (steady) → is_steady()
Relojes predefinidos:

system_clock → típicamente hora del sistema
– no steady

steady_clock → típicamente tiempo desde encendido del sistema
● high_resolution_clock

Concurrencia y STR 35/52


C++: Duraciones
Se definen std::chrono::duration<P1, P2>
– P1: tipo numérico cuenta (int, long o double)
– P2: es std::ratio indica segundos por unidad:

ratio<60,1> minutos; ratio<1,1000> milisegundos

Existen duraciones predefinidas



nanoseconds, seconds, minutes, hours, ...

Se obtiene cuenta con count()


Conversiones si no se pierde precisión:

hours → minutes
– Caso contrario usar duration_cast<> (trunca)
Definidas operación aritméticas con duraciones:

sumar, restar, multiplicar, comparar, etc.
Concurrencia y STR 36/52
C++: Instantes tiempo
Definidos con time_point<P1,P2>:
– P1: reloj a considerar
– P2: duración desde su epoch (inicio)

Los epochs típicos suelen ser:
– 00:00 del 1 de enero de 1970

epoch para medir tiempo en los sistemas UNIX
– Instante en que se arrancó el ordenador

Los devuelven método now() de los relojes


Operaciones:

instantes de tiempo +/- duraciones:
– generan nuevos instantes de tiempo

restar instantes con el mismo reloj:
– generan duraciones

comparar
Concurrencia y STR 37/52
C++11: chrono ejemplos
#include <chrono>
// conversiones de unidades
std::chrono::hours d1 = hours(15);
std::chrono::minutes mi(d1);
std::cout << "\n Minutos: " << mi.count();
std::chrono::seconds s(d1);
std::cout << "\n Segundos: " << s.count();
std::chrono::nanoseconds n(d1);
std::cout << "\n Nanosegundos: " << n.count();

// medir tiempos
auto start = std::chrono::high_resolution_clock::now();
// hacer algo
auto stop = std::chrono::high_resolution_clock::now();
std::chrono::nanoseconds tardo = stop - start;
std::cout<< "Se tardó " << tardo.count() << " nanosegundos";

Concurrencia y STR 38/52


Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 39/52


Tareas periódicas y Retardos
Tareas a ejecutar cada cierto tiempo (periodo)
Opción 1: Retardos por bucle comprobaciones
– Consume ciclos CPU → espera ocupada

auto periodo = std::chrono::seconds(2);


while (1) {
/*Acciones a realizar por la tarea*/

/* Retardo con bucle de comprobaciones */


auto inicial = std::chrono::steady_clock::now(); // time_point
auto siguiente = inicial + periodo; // time_point

do { //bucle de coparaciones -> espera ocupada


auto ahora = std::chrono::steady_clock::now(); // time_point
} while(ahora < siguiente);
}

Concurrencia y STR 40/52


Tareas periódicas y Retardos
Opción 2: Retardo suspendiendo ejecución:
– más eficiente
– Función
● this_thread::sleep_for(duration dur)

auto periodo = std::chrono::seconds(2);


while (1) {
/*Acciones a realizar por la tarea*/

this_thread::sleep_for(periodo);
}

– No se tiene en cuenta lo que duran acciones



Se acumulan los retrasos → deriva acumulada
Retraso Retraso Retraso
periodo
Concurrencia y STR 41/52
Eliminación deriva acumulada
Opción 3: El retraso se recalcula en cada etapa
auto periodo = std::chrono::seconds(2);
while (1) {
auto inicial = std::chrono::steady_clock::now(); // time_point

/*Acciones a realizar por la tarea*/


/* Calculamos retraso a apllicar */
auto ahora = std::chrono::steady_clock::now(); // time_point
auto siguiente = inicial + periodo; // time_point
auto retardo = siguiente - ahora; // duration
this_thread::sleep_for(retardo);
}
– Siguen existiendo imprecisiones → deriva local

al calcular retraso

al aplicar retraso
Retraso Ret Ret Ret

Concurrencia y STR 42/52


Reducción deriva local
Opción 4: Se bloquea hasta siguiente disparo
– con sleep_until()
– se evita errores de cálculo

auto periodo = std::chrono::seconds(2);


auto siguiente = std::chrono::steady_clock::now(); // time_point
while (1) {

/*Acciones a realizar por la tarea*/


/* Calculamos retraso a apllicar */
siguiente += periodo; // time_point
this_thread::sleep_until(siguiente);
}

Concurrencia y STR 43/52


Activación por temporizadores
Avisan transcurrido cierto tiempo
– una sola vez
– periódicamente

Tarea periódica sin deriva acumulada

Pueden ser
– hardware → interrupción

sin derivas
– software

En sistemas UNIX → señales al proceso

Precisión de segundos

Derivas locales

Concurrencia y STR 44/52


Tiempos Límites
Tarea suspendida en espera de evento
– Entrada / Salida
– Respuesta de otra tarea
Problema: Puede estar suspendida por siempre
Solución: tiempo máximo de espera (time-out)
– C++11: clases con métodos _for y _until
timed_mutex.try_lock_for(duration);
timed_mutex.try_lock_until(time_point);

unique_lock.try_lock_for(duration);
unique_lock.try_lock_until(time_point);

condition_variable.wait_for(duration);
condition_variable.wait_until(time_point);

Concurrencia y STR 45/52


Contenido

Concurrencia
– Planteamiento
– Semáforos
– Concurrencia en C++


Sistemas en Tiempo Real
– Características
– Medida y control del tiempo
– C++ chrono
– Tareas periódicas y retardos
– Planificación

Concurrencia y STR 46/52


Planificación
Organizar uso de los recursos

principalmente CPU

Garantizar requisitos temporales


Tareas (Ti) caracterizados por
● Ci: tiempo de cómputo
● Pi: periodo de ejecución
● Di: tiempo límite (deadline)
– simplificación Di = Pi
Ci Ci Ci

Pi

Di

Concurrencia y STR 47/52


Planificación
Debe constar de
– Algoritmo de planificación

determina el orden de ejecución
– Método análisis del comportamiento temporal

Requisitos garantizados en todos los casos

Se estudia peor caso

Estimar duración de cada tarea en el peor caso

Algoritmos:
– Planificación cíclica
– Planificación basada de prioridades

Prioridades estáticas

Prioridades dinámicas

Concurrencia y STR 48/52


Planificación cíclica
Se define periodo base (PB):
– el del proceso de menor periodo
– periodo del resto procesos → múltiplo del PB
Existe:
– ciclo principal: proceso mayor periodo
– ciclo secundarios: de PB

...
C. Secundario

Ciclo Principal

Concurrencia y STR 49/52


Planificación cíclica
Se organiza de manera que
– Todos los procesos se activan al menos una vez en el ciclo
principal
– Algunos procesos se activan varias veces
Ejemplo:
– A: tarea de menor PA → define PB
– B: tarea PB=2*PB
– C: tarea de mayor periodo PC = 4*PB
A B C A A B A

C. Secundario (PB)

Ciclo Principal (4*PB)


Concurrencia y STR 50/52
Planificación por Prioridades
Al activarse proceso más prioritario,
– opciones:

Apropiativo: expulsa proceso menor prioridad

No apropiativo: espera termine proceso

Apropiación diferida: expulsa pasado cierto tiempo

Prioridad al más frecuente:


– La más utilizada.

Es estática → fixed-priority scheduling, FPS
– Tiene herramienta de análisis (N tareas)
● utilidad tarea Ti → Ui= Ci / Pi
● utilidad total U = ΣUi

Condición suficiente: U ≤ N(21/N-1)
– No necesaria → aunque no se cumpla puede funcionar

Concurrencia y STR 51/52


Planificación por Prioridades dinámica
Más utilizado:
– Proximidad al plazo de respuesta

earliest deadline first EDF

Es superior al prioridad al más frecuente (FPS)
– Capaz en casos que la FPS no puede

Inconvenientes
– es dinámica → sobrecarga
– tiempo límite no es el único criterio

prioridad fija incorpora otros aspectos
– Problema al incorporar procesos sin tiempo límite

Concurrencia y STR 52/52

También podría gustarte