Está en la página 1de 76

UNIVERSIDAD NACIONAL DE INGENIERÍA

FACULTAD DE INGENIERÍA MECÁNICA

DISEÑO DE SISTEMAS EN TIEMPO REAL (MT-325)

TEMA:
“CAPÍTULO 4: CONCEPTOS Y PRÁCTICA DE LABORATORIO”
Subtitle
DOCENTE:
Ing. CIP Daniel Leonardo Barrera Esparta
I.- HILOS Y PROCESOS
Cada programa está compuesto por uno o varios procesos
- Cada proceso dispone de su propio espacio virtual protegido de memoria para datos
globales, datos dinámicos y código, además de recursos solicitados al sistema operativo (ej.
ficheros).
- Cada proceso se ejecuta en uno o varios hilos (threads).
- Cada hilo corresponde a una línea de ejecución secuencial de instrucciones y dispone de
pila propia (datos locales, retornos de rutinas) e información de contexto propia (registros
de la CPU, estado).
I.- HILOS Y PROCESOS
Interacción entre procesos
• Servicios ofrecidos por el sistema operativo como intermediario.
• Datos: colas de mensajes, memoria compartida.
• Sincronización: semáforos, monitores, etc.
• Señales
I.- HILOS Y PROCESOS
Hilos (Threads)

• Objetivo: compartir recursos entre


procesos cooperantes de forma
cómoda
• Hilo = proceso ligero = lightweight
process = LWP = unidad fundamental
de uso de procesador
• Básicamente se compone de un CP,
una serie de registros y un área de pila
• Cada hilo comparte con los otros hilos
cooperantes código, datos y recursos
del SO
• El código, los datos y los recursos son
poseídos por otra entidad conocida
como tarea (task).
I.- HILOS Y PROCESOS
Hilos (Threads)

• Un proceso tradicional (proceso pesado) se


compone de una tarea con un hilo de
ejecución
• La conmutación de un hilo a otro en la misma
tarea requiere un coste mínimo ya que solo es
necesario salvar los registros y conmutar la
pila
• Los threads son muy adecuadas para sistemas
distribuidos y sistemas multiprocesador (cada
hilo se puede ejecutar en un procesador)
• Cada hilo sólo puede pertenecer a una tarea
• Los hilos pueden ser implementados en
espacio de usuario o como llamadas al
sistema
I.- HILOS Y PROCESOS
Ventajas de los Hilos

• Los hilos se crean más rápidamente que los procesos


• Los hilos se destruyen más rápidamente que los procesos
• El tiempo de conmutación (cambios de contexto) entre hilos de la misma tarea es más
rápida que la conmutación entre procesos
• Los hilos tienen espacio de direcciones comunes esto hace mas fácil coordinar actividades
• Menor sobrecarga de comunicaciones debido a que todos los hilos de una tarea comparten
memoria.
• Los hilos son útiles en aplicaciones que tiene que hacer muchas cosas concurrentemente:

– Un hilo espera por una I/O


– Otro hilo en el mismo proceso esta haciendo algo de procesamiento (algoritmo)
I.- HILOS Y PROCESOS
Ventajas de los procesos
• Espacios de memoria virtual independientes y estancos significa disponer de un alto nivel
de seguridad. Un proceso con problemas no puede corromper estructuras de datos de
otros.
• Codificados de forma independiente, preparados para ser ejecutados en computadores
diferentes.

Hilos en espacio de usuario


Ventajas:
• La conmutación entre hilos se puede realizar rápidamente sin ayuda del S.O.
• La planificación puede hacerla la aplicación
• Portabilidad entre SO diferentes
• Inconvenientes:
• Si el S.O. no sabe de la existencia de hilos en una tarea, el bloqueo de un hilo produce el
bloqueo del resto de hilos de la misma tarea
• Dos hilos de una misma tarea no se pueden ejecutar en procesadores diferentes
I.- HILOS Y PROCESOS
Hilos en espacio usuario

• La administración de los hilos es realizada


por una librería de hilos a nivel usuario
• Los hilos corren encima de un run-time
system, el cual es una colección de
procedimientos que administra los threads.
• Si los hilos son administradas en espacio
usuario, cada proceso necesita su propia
tabla de hilos
I.- HILOS Y PROCESOS
Hilos en espacio usuario

• La información necesaria para restaurar un


hilo esta almacenada en la tabla del hilo,
exactamente de la misma manera como el
kernel almacena información del proceso
en la tabla del proceso
• Examples:
- POSIX Pthreads
- Mach C-threads
- Solaris threads
I.- HILOS Y PROCESOS
Implementación de hilos en el Kernel

• Esta información es un subconjunto de la


información que tradicionalmente
mantiene el kernel acerca de cada uno de
sus procesos con un solo hilo.

Ejemplos:

- Windows 95/98/NT/2000
- Solaris
- Tru64 UNIX
- BeOS
- Linux
I.- HILOS Y PROCESOS
Aplicación: sistema de control

El objetivo global del sistema es el de mantener la temperatura y presión de algún proceso


químico dentro de unos límites preestablecidos.
I.- HILOS Y PROCESOS
Aplicación: sistema de control

Arquitectura de software
Hay tres actividades concurrentes:
• Control de presión.
• Control de temperatura.
• Visualización,
Se puede hacer de tres formas:
✓ Con un solo programa secuencial (ejecutivo cíclico ). No hace falta sistema operativo
✓ Con tres procedimientos secuenciales ejecutados como procesos de un sistema operativo
✓ Con un solo programa en un lenguaje concurrente . No hace falta sistema operativo, pero sí
un núcleo de ejecución.
I.- HILOS Y PROCESOS

Solución con ejecutivo cíclico


I.- HILOS Y PROCESOS
Solución con ejecutivo cíclico

Problemas:
• La temperatura y la presión se muestrean con la misma frecuencia.
• Mientras se espera leer una variable no se puede hacer nada con la otra. Si un sensor falla,
deja de leerse el otro también.
• Se basa en una espera ocupada. Además ambas actividades deberían estar desacopladas.
• El principal problema es que dos actividades independientes están acopladas entre sí
• Se utiliza un único programa que ignora la concurrencia lógica de T, P y S => No es necesario
sistema operativo.
I.- HILOS Y PROCESOS
Solución con ejecutivo
cíclico

• Es más fácil de
escribir y leer
• El código refleja
directamente el
paralelismo de la
aplicación
• Problema: Ambas
tareas hacen uso de
la pantalla => hace
falta gestionar el
• Acceso a este
recurso compartido.
I.- HILOS Y PROCESOS
Problemas

Ambas tareas envían datos a la pantalla


✓ Hay que gestionar el acceso a este recurso compartido
✓ Si la escritura no se hace de forma exclusiva por cada tarea, el resultado es ilegible
Hace falta un mecanismo para la gestión de recursos
✓ Sincronización de tipo exclusión mutua
✓ Hace falta el apoyo del entorno de ejecución
II.- PROGRAMACIÓN EN HILOS
Pthreads (Threads POSIX)

• Pthreads esta definida en ANSI/IEEE POSIX 1003.1


• Las subrutinas el cual comprende los Pthreads se dividen:
✓ Administracion Thread : trabaja directamente sobre la creacion, detaching, joining de hilos
etc.
✓ Mutex: Trata con un tipo de sincronización, llamado "mutex", el cual es una abreviación de
”exclusión mutua". Estas funciones proveen creación, destrucción, locking y unlocking de
mutexes. También son complementados por funciones de atributo de mutex que modifican
los atributos asociado con los mutex.
• Variables de Condición : trata con un tipo fino de sincronización basado sobre las condiciones
especificas del programador. Esta clase incluye funciones para crear, destruir, wait y signal en
base a valores de variables especificadas.
II.- PROGRAMACIÓN EN HILOS
Administración Thread

• int pthread_ create( pthread_ t *thread, pthread_ attr_ t *attr, void *(* start_ routine)( void *),
void *arg);
Crea un thread con sus atributos, rutina inicial y argumentos de esta

• int pthread_ exit( void *value);


El thread invocante termina y devuelve value a cualquier thread que haya invocado previamente
pthread_ join con él

• int pthread_cancel (pthread_t thread);


Pide la cancelacion de un hilo

• int pthread_ join( pthread_ t thread, void ** value_ ptr);


Suspende el thread invocante hasta que el thread nombrado termina. value_ ptr apunta al valor
devuelto por el thread que termina .
II.- PROGRAMACIÓN EN HILOS
Ejemplo 1

#include <pthread.h> printf("Creating thread %d\n", t);


#include <stdio.h> rc = pthread_create(&threads[t], NULL,
#define NUM_THREADS 5 PrintHello, (void *)t);
void *PrintHello(void *threadid) if (rc){ printf("ERROR ");
{ exit(-1); }
printf("\n%d: Hola !\n", threadid); }
pthread_exit(NULL); pthread_exit(NULL);}
}

int main()
{
pthread_t threads[NUM_THREADS];
int rc, t;
for(t=0;t < NUM_THREADS;t++){
II.- PROGRAMACIÓN EN HILOS
Ejemplo 2
if (pthread_create(&th1, NULL, periodic,
#include <pthread.h> &period1)==-1)
void * periodic (void *arg) { {
int period; perror("");
period = *((int *)arg); }
while (1) { if (pthread_create(&th2, NULL, periodic,
printf("En tarea con periodo %d\n", period); &period2)==-1)
sleep (period); {
} perror("");
} }
main() { sleep(30);
pthread_t th1, th2; printf("Salida del hilo principal\n");
int period1, period2; exit(0);
period1 = 2; }
period2 = 3;
II.- PROGRAMACIÓN EN HILOS
Atributos con los Hilos

• int pthread_ attr_ init( pthread_ attr_ t *attr);


Inicializa un atributo de thread a su valor por defecto
• int pthread_ attr_ destroy( pthread_ attr_ t *attr);
Destruye un atributo de thread
• int pthread_ attr_ getstacksize( const pthread_ attr_ t *attr, size_ t *stacksize);
Devuelve el tamaño de pila de un atributo de thread
• int pthread_ attr_ setstacksize( pthread_ attr_ t *attr, size_ t *stacksize);
Establece el tamaño de pila de un atributo de thread
• int pthread_ attr_ getstackaddr( pthread_ attr_ t *attr, void *stackaddr);
Devuelve la dirección de la pila de un atributo de thread
• int pthread_ attr_ setstackaddr( pthread_ attr_ t *attr, void stackaddr);
Establece la dirección de la pila de un atributo de thread
II.- PROGRAMACIÓN EN HILOS
Atributos con los Hilos

• int pthread_ attr_ getdetachstate( const pthread_ attr_ t *attr, void *detachstate);
Devuelve el estado detach de un atributo de thread. Un thread detached no es joinable
• int pthread_ attr_ setdetachstate( pthread_ attr_ t *attr, void *detachstate);
Establece el estado detach de un atributo de thread
• int pthread_ attr_ setscope( pthread_ attr_ t *attr, int contention _scope);
Establece el ámbito de contienda del atributo de un thread
• Int pthread_ attr_ getscope( pthread_ attr_ t *attr, int *contention_ scope);
Devuelve el ámbito de contienda del atributo de un thread
• int pthread_ attr_ setschedpolicy( pthread_ attr_ t *attr, int policy);
Establece la política de planificación del atributo de un thread
• int pthread_ attr_ getschedpolicy( pthread_ attr_ t *attr, int *policy);
Devuelve la política de planificación del atributo de un thread
II.- PROGRAMACIÓN EN HILOS
Ejemplo 3 pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,
#include <pthread.h> PTHREAD_CREATE_UNDETACHED);
#include <stdio.h> for(t=0;t < NUM_THREADS;t++) {
#define NUM_THREADS 3 printf("Creating thread %d\n", t);
void *BusyWork(void *null){ rc = pthread_create(&thread[t], &attr,
int i; double result=0.0; BusyWork, NULL);
for (i=0; i < 1000000; i++) { }
result = result + (double)random(); } /* libero atributo y espero a los otros hilos */
printf("result = %d\n",result); pthread_attr_destroy(&attr);
pthread_exit((void *) 0); for(t=0;t < NUM_THREADS;t++)
} { pthread_join(thread[t], (void **)&status);
void main(){ pthread_t thread[NUM_THREADS]; printf("Completed join with thread %d status=
pthread_attr_t attr; %d\n",t, status); } pthread_exit(NULL);}
int rc, t, status;
/* Initialize and set thread detached attribute */
II.- PROGRAMACIÓN EN HILOS
Ejemplo 3
II.- PROGRAMACIÓN EN HILOS
MUTEX
Hay dos mecanismos que permiten sincronizar hilos:
• Un mutex es una variable que proporciona exclusión mutua mediante dos operaciones, lock y
unlock
• una variable de condición proporciona sincronización condicional mediante dos operaciones,
wait y signal
Ambos tipos de variables pueden tener atributos
II.- PROGRAMACIÓN EN HILOS
MUTEX

• int pthread_ mutex_ init( pthread_ mutex_ t *mutex, pthread_ mutexattr_ t *attr);
Inicializa un mutex con ciertos atributos.
• int pthread_ mutex_ destroy( pthread_ mutex_ t *mutex);
Destruye el mutex. Comportamiento indefinido si el mutex está cerrado.
• int pthread_mutexattr_init (attr)
Crea un objeto atributo mutex e inicializa con valores por defecto.
• int pthread_mutexattr_destroy (attr)
Elimina el objeto de atributo de mutex attr
• int pthread_ mutex_ lock( pthread_ mutex_ t *mutex);
Cierra el mutex.El propietario del mutex es el thread que lo cerró.
• int pthread_ mutex_ unlock( pthread_ mutex_ t *mutex);
Abre el mutex cuando lo invoca su thread propietario Comportamiento indefinido si no es el
thread propietario o el mutex está abierto. Activa un thread suspendido en el mutex.
II.- PROGRAMACIÓN EN HILOS
Ejemplo
#include <pthread.h>
#include <stdio.h>
struct { int data[1]; /* buffer compartido */
pthread_mutex_t mutex; /* mutex */ }
buffer;
int main(void) {
pthread_t rd; /* id del consumidor */
pthread_attr_t attr; /* atributos */
int valores[50], indice,in, c,x;
void *status; /* exit thread status */
void *consumidor(void *); /* prototipo */
void escritura(int); /* prototpo */
II.- PROGRAMACIÓN EN HILOS
Ejemplo
mutex_init(&buffer.mutex,NULL) /* inicializa el mutex*/
pthread_attr_init(&attr); /* inicializa atributos de hilo*/
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE );
/* create hilo consumidor */
pthread_create(&rd,&attr,consumidor,NULL)
// lleno valores a escribir y leer
for(indice=0;indice<15;indice++)
{ valores[indice]=indice; }
valores[indice]=50;
//---------------------- PRODUCTOR ------------------------//
/* el main es el hilo productor */
indice=0;
do { c=valores[indice++]; /* lee un valor */
escritura(c); /*produce/almacena en buffer. */ }
while (c !=50); / * continua hasta que dato es 50 */
II.- PROGRAMACIÓN EN HILOS
Ejemplo

/* espera hasta que finalice el consumidor */


pthread_join(rd,&status)
printf("fin del programa\n"); exit(0); }
//-----------------------CONSUMIDOR -----------------------
void *consumidor(void *arg) {
int c,yy;
int lectura(); /* prototype */
do {
c = lectura(); /* lee un valor */
} while (c != 50);
}
II.- PROGRAMACIÓN EN HILOS
Ejemplo

void escritura(int c) {
int xx; pthread_mutex_lock(&buffer.mutex); /* lock mutex */
buffer.data[1] = c; /* inserta un dato */
pthread_mutex_unlock(&buffer.mutex); /* unlock mutex */
}
int lectura() {
int elem, c;
pthread_mutex_lock(&buffer.mutex); /* lock mutex */
elem = buffer.data[1]; /* lee un dato */
c=elem; printf("%3d",c); /* imprime valor */
pthread_mutex_unlock(&buffer.mutex); /* unlock mutex */
return elem; }
II.- PROGRAMACIÓN EN HILOS
Variable de condición

◼Variables de Condición(VC) proveen otra manera de sincronizar hilos. Mientras que los
Mutexs implementan sincronización controlando al hilo el acceso de dato, las variables
de condición permiten sincronizar hilos en base al valor actual del dato

◼Sin VC, el programador podría tener hilos que continuamente estén polling
(posiblemente en una sección critica),para verificar si una condición se mantiene.

pthread_cond_init(condition, attr)
Inicializa una vc y fija sus atributos
pthread_cond_destroy(condition)
libera una VC que ya no es necesario
pthread_condattr_init(attr)
pthread_condattr_destroy(attr)
estas rutinas son usado para crear y destruir atributos de la variable de condición.
II.- PROGRAMACIÓN EN HILOS
Variable de condición

pthread_cond_wait (condition, mutex)


Bloquea al hilo que llama hasta que la condición especificada es señalada. Esta rutina deberá ser
llamada mientras el mutex esta locked, y este librara automáticamente el mutex mientras este
espera. Debe unlock el mutex después que la señal ha sido recibida

pthread_cond_signal(condition)
Se usa para señalar o despertar otro hilo el cual esta esperando sobre la VC. Este debe ser
llamado después que el mutex es is locked, y debe unlock mutex para que
thread_cond_wait()complete
II.- PROGRAMACIÓN EN HILOS
Variable de condición

#include <pthread.h>
#include <stdio.h>
struct { int data[1]; /*buffer comnpartido */
pthread_mutex_t mutex; /* mutex */
pthread_cond_t cond; /* variable de condition */
} buffer;
int nnElem=0; //indica si el buffer esta lleno/vacio int main(void)
{ pthread_t rd; /* id del consumidor */
pthread_attr_t attr; /* atributos */
int valores[50],indice, c,x;
void *status; /* exit thread status */
void *consumidor(void *); /* prototype */
void write_to_buffer(int); /* prototype */
II.- PROGRAMACIÓN EN HILOS
Variable de condición

indice=0;
/* inicializa mutex */
pthread_mutex_init(&buffer.mutex, NULL)
/* initicializa variable de condicion */
pthread_cond_init(&buffer.cond, NULL) /*
inicializa atributos de hilo */ pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE );
/* crea hilo consumidor */
pthread_create(&rd,&attr,consumidor,NULL)

//lleno valores a leer y escribir for(indice=0;indice<15;indice++)

{valores[indice]=indice;} valores[indice]=50;
II.- PROGRAMACIÓN EN HILOS
Variable de condición

//--------------------- PRODUCTOR -----------------------------


/* el programa principal es el hilo productor */
indice=0;
do {
c = valores[indice++]; /* lee a caracter */
write_to_buffer(c); /* inserta en el buffer */
} while (c !=50);
/* espera al consumidor a terminar */
pthread_join(rd,&status)
printf("Fin de Programa\n");
exit(
II.- PROGRAMACIÓN EN HILOS
Variable de condición

//--------------------CONSUMIDOR ------------------------------
void *consumidor(void *arg)
{ int c; int read_from_buffer(); /* prototype */
do { c = read_from_buffer(); /* lee un dato */
} while (c != 50);
}
void write_to_buffer(int c) {
pthread_mutex_lock(&buffer.mutex); /* lock mutex */
if (nnElem == 1) //si esta lleno no escribe /* no puede escribir, buffer full ==> espera */
pthread_cond_wait(&buffer.cond,&buffer.mutex); buffer.data[tamano] = c; /* inserta data */
nnElem=nnElem+1; /* increase para indicar lleno */
/* despierta el hilo consumidor */
pthread_cond_signal(&buffer.cond); pthread_mutex_unlock(&buffer.mutex); /* unlock mutex*/ }
II.- PROGRAMACIÓN EN HILOS
Variable de condición

int read_from_buffer() {
int elem,c;
pthread_mutex_lock(&buffer.mutex); /* lock mutex */
if (nnElem == 0) /* no puede leer, buffer vacio ==> espera */
pthread_cond_wait(&buffer.cond,&buffer.mutex);
elem = buffer.data[tamano]; /* consume un dato */
nnElem=nnElem-1; /* decrementa para indicar vacio*/
/* despierta al hilo productor*/
pthread_cond_signal(&buffer.cond);
pthread_mutex_unlock(&buffer.mutex); /* unlock mutex */
return elem; }
II.- PROGRAMACIÓN EN HILOS

Productor y consumidor
III.- SEÑALES

Señales en POSIX.

• Son interrupciones software (suceso asíncrono) que pueden ser enviadas a uno o mas
procesos/threads para informarle de la ocurrencia de un evento.
• Señal es a proceso como interrupción es a procesador
• Una señal es gestionada por el S.O.
• Una señal interrumpe la tarea actualmente en curso y se activa la tarea asociada a la señal.
• Permite asociar señales independientes a cada proceso o thread
• Una señal se puede producir o generar de varias formas, por ejemplo:
✓fallos de hardware
✓expiración de temporizadores
✓un acción explícita de otro proceso o thread, invocando la función kill() u otra similar
III.- SEÑALES
Identificadores de señal
Cada tipo de señal se identifica mediante un número entero positivo
Se definen constantes (etiquetas) para dar nombres simbólicos a las señales
Ejemplos:
SIGABRT ‘abort signal’
Terminación anormal
Se genera cuando se llama a la función abort()
Causa la terminación del proceso
No debe ser ignorada
SIGALRM ‘alarm signal’
Fin de temporización
Se genera cuando expira el tiempo
programado con alarm() o con setitimer()
SIGFPE ‘floating point exception signal’
operación aritmética errónea
Ejemplo: dividir por cero, desbordamiento (overflow)
III.- SEÑALES
Identificadores de señal
SIGINT ‘interruption signal’
Señal de atención interactiva
Se envía a proceso asociado a un terminal cuando se pulsa la tecla interrupción [CTRL-C]
Acción por defecto terminar el proceso
SIGKILL ‘kill signal’
Señal de terminación abrupta
No puede ser ignorada ni armada
Acción por defecto genera ‘core’ + terminar proceso
III.- SEÑALES
Identificadores de señal

SIGSEV ‘segment violation signal’


Referencia a memoria inválida
Acción por defecto genera ‘core’ + terminar proceso
SIGTERM ‘termination signal’
Terminación, por ejemplo, ante ‘shutdown’ del sistema
Similar SIGKILL, pero puede ser ignorada y armada
Acción por defecto terminar el proceso
SIGUSR1 & SIGUSR2 ‘user signal’
Reservadas para uso del programador
Ninguna aplicación estándar.
Acción por defecto terminar el proceso
III.- SEÑALES
III.- SEÑALES
Generación y entrega
Una señal se genera cuando ocurre el suceso que la produce
Una señal se entrega cuando se produce la acción asociada en el proceso o thread
Que puede hacer un proceso ante una señal?
Bloquear Temporalmente
Ignorar la señal
Ejecutar la acción por defecto asociada a la señal (normalmente terminar el proceso, la aporta el
kernel)
Manejar la señal mediante una función definida por el programador

Armado de una señal: Indicarle al sistema operativo el nombre de la rutina que ha de tratar
cuando llegue ese tipo de señal POSIX ---- > sigaction( ) System V ---- > signal( )
Ignorar una señal : Algunas se pueden ignorar y otras se pueden enmascarar momentáneamente
Acción por defecto: Cuando un proceso recibe una señal sin haberla armado o enmascarado
previamente. En la mayoría de los casos consiste en matar al proceso
III.- SEÑALES
III.- SEÑALES
Tratamiento de usuario

Sistemas en Tiempo Real EASM


1. El proceso detiene su ejecución en la instrucción actual
2. Bifurca a una rutina de tratamiento que ha de formar parte del propio proceso
3.Terminada esta rutina, continua el proceso donde fue interrumpido
III.- SEÑALES
Bloqueo de señales
Un proceso puede enmascarar o bloquear un tipo de señales
• cada proceso o thread tiene una máscara que define las señales bloqueadas
• la máscara se hereda inicialmente del padre
Señales y threads
• Las señales generadas por un error de un thread se envían sólo a ese thread
• Las generadas por un suceso asíncrono (p. ej. E/ S) se envían al proceso
• Las generadas por programa se pueden enviar a un thread o a todo un proceso
Funciones POSIX para el manejo de Señales
El conjunto de de funciones POSIX para el manejo de señales permiten:
1.Establecer conjunto de señales
2.Armado de señales
3.Envió de señales
4.Mascara de señales
5.Espera de señales
6.Señales pendientes de entrega
IV.- EVENTOS
IV.- EVENTOS
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES
V.- TIEMPO Y TEMPORIZADORES

También podría gustarte