Edición de «Práctica Programación Concurrente (Sistemas Operativos)»

De Cuba-Wiki
Advertencia: no has iniciado sesión. Tu dirección IP se hará pública si haces cualquier edición. Si inicias sesión o creas una cuenta, tus ediciones se atribuirán a tu nombre de usuario, además de otros beneficios.

Puedes deshacer la edición. Antes de deshacer la edición, comprueba la siguiente comparación para verificar que realmente es lo que quieres hacer, y entonces publica los cambios para así efectuar la reversión.

Revisión actual Tu texto
Línea 1: Línea 1:
{{Back|Sistemas Operativos}}
==Ejercicio 01:==
 
===Ejercicio 1===


{| class="wikitable" style="text-align:center"
{| class="wikitable" style="text-align:center"
Línea 20: Línea 18:
|g = fun1[a,d,f]||R(S4) = {a,d,f}||W(S4) = {g}
|g = fun1[a,d,f]||R(S4) = {a,d,f}||W(S4) = {g}
|-
|-
! S5
! 5
|f = sen[w]||R(S5) = {w}||W(S5) = {f}
|f = sen[w]||R(S5) = {w}||W(S5) = {f}
|-
|-
Línea 27: Línea 25:


<br>Condiciones de Bernstein: R(Si) n W(Sj) = W(Si) n R(Sj) = W(Si) n W(Sj) = {}  
<br>Condiciones de Bernstein: R(Si) n W(Sj) = W(Si) n R(Sj) = W(Si) n W(Sj) = {}  
<br>Se pueden ejecutar concurrentemente:
<br>Se pueden ejecutar concurrentemente S1 y S2, S1 y S3, S1 y S5, S2 y S5
<br>S1 y S2,  
<br>S1 y S3,  
<br>S1 y S5,  
<br>S2 y S3,
<br>S2 y S5


===Ejercicio 2*===
==Ejercicio 02:[*]==
a)
<br>a)
<pre>
<br> count2 = 2
    Count5 = Count6 = 2
<br> count = 3
<br> s1
<br> fork L2
<br> fork L3
<br> fork L4
<br> jump Sigo
<br> L2:
<br> s2
<br> jump Sigo2
<br> L3:
<br> s3
<br> Sigo2:
<br> join count2
<br> jump Sigo
<br> L4:
<br> s4
<br> Sigo:
<br> join count
<br>
<br>b)
<br> s1
<br> parbegin
<br> begin
<br> parbegin
<br> s2
<br> s3
<br> parend
<br> s5
<br> end
<br> s4
<br> parend
<br> s6
<br>


    s1
==Ejercicio 03:[*]==
    fork L1
<br>Que es indivisible? --> Indivisible es que es atomica. Que no puede ser interrumpida a la mitad. Esto es necesario ya que join mantiene una cuenta de cuantos procesos esta esperando, y si no fuera atomica, podria funcionar mal, y por ejemplo "matar" a todos los dos o mas procesos que tenian que unirse ahi.  
    fork L2
    s2
    goto L3
L2: s3
L3: join Count5
    s5
    goto L4
L1: s4
        L4: join Count6
            S6
</pre>
b)
<pre>
s1
parbegin
begin
parbegin
s2
s3
parend
s5
end
s4
parend
s6
 
</pre>
 
===Ejercicio 3*===
<br>La instrucción Join debe ser atómica ya que mantiene una cuenta de cuantos procesos esta esperando, y si no fuera atómica, podría funcionar mal, y por ejemplo "matar" a, los dos o mas procesos que tenían que unirse ahi.  
<br>
<br>
La definición de ''' join var''' es:
==Ejercicio 04:[*]==
<br>a) Me parece que es el tipo de grafos que no pueden ser representados con parbegin/parend. PREGUNTAR
<br>b) PREGUNTAR ESTO TB
<br>
<br>
1 ) var - - ;
==Ejercicio 05:==
<br>a) Agarramos el grafo de antes, sacamos la flecha de S5 a S6 y ponemos una flecha de S5 a S7, de S6 a S7 y de S4 a S7...
<br>b)
<br>c)
<br>
<br>
2 ) if (var =! 0) then wait;
 
==Ejercicio 06:==
<br>a)
<br>b)
<br>
<br>
si justo es interrumpida después de 1) y antes de 2) y para ejecutar otro join con la misma variable. supongamos que var valia 0, y ahora se decrementa y pasa a varler -1, chau
<br>NOTA: Ejercicios del 7 al 10. Preguntar estos... Tienen pinta de entrar seguro :)  
tu programa queda en wait para siempre.
 
<br>
<br>


===Ejercicio 4*===
==(NOTA: PREMISAS DE DIJKSTRA:)==
<br>a) Es el tipo de grafos que no pueden ser representados con parbegin/parend, ya que no se puede dividirlo en subgrafos disjuntos concurrentes.
<br>b)
<pre>
var a,b,c,d,e,f,g,h: semaforo;
init(todos,0);
 
Parbegin
Begin S1;V(a);V(b);V(c); End
Begin P(a);S2;V(d);V(e); End
Begin P(b);S3;V(f); End
Begin P(c);S4;V(h); End
Begin P(d);P(f);S5;V(e); End
Begin P(g);P(h);S6:End
Parend
 
</pre>
 
===Ejercicio 5===
<br>a) Aca lo tienen:
<pre>
 
  __S1__
/  |  \
S2  S3  S4
\  /    |
  S5    S6
    \  /
    S7
 
</pre>
<br>b) Se puede construir el grafo de manera "secuencial", es decir, S1-S2-S3-S5-S4-S6-S7 (Claramente no tiene el mayor grado de concurrencia posible).
O simplemente basta con borrar algun Parbegin con su Parend correspondiente y decir que las instrucciones que habia en medio se podrian haber ejecutado concurrentemente.
<br>c)
<pre>
Count5 = Count7 = 2
 
S1
fork L3
fork L4
S2
jump Join5
 
L3:
S3
jump Join5
 
L4:
S4
S6
jump Join7
 
Join5:
join Count5
S5
        jump Join7
 
Join7:
join Count7
        S7
</pre>
 
===Ejercicio 6===
a) Si no lo hice mal, es una cosa mas o menos asi:
<pre>
S1
    ____________
  /    |      \
S2      S3    S7
  \    /      /  \
    S4      S8  S9
  /    \    |    |
S5    S6  S10    |
  \    /    \    /
  (x1)      (x2)
    \_________/
        S11
</pre>
b)
<pre>
s1
parbegin
begin
parbegin
s2
s3
parend
s4
parbegin
s5
s6
parend
(x1)
end
 
begin
s7
parbegin
begin
s8
s10
end
s9
parend
(x2)
end
parend
s11
</pre>
c) S1, S7, S8 (porque son los unicos de los que depende S10)
 
===Ejercicio 7===


<pre>
1) No deben hacerse suposiciones sobre las instrucciones de máquina ni la cantidad de procesadores. Sin embargo, se supone que las instrucciones de máquina (Load, Store, Test) son ejecutadas atómicamente, o sea que si son ejecutadas simultáneamente el resultado es equivalente a su ejecución  secuencial en un orden desconocido.
(NOTA PARA EJS 7-11)
PREMISAS DE DIJKSTRA:
1) No deben hacerse suposiciones sobre las instrucciones de máquina ni la cantidad de </br>procesadores. Sin embargo, se supone que las instrucciones de máquina (Load, Store, Test) son ejecutadas atómicamente, o sea que si son ejecutadas simultáneamente el </br>resultado es equivalente a su ejecución  secuencial en un orden desconocido.


2) No deben hacerse suposiciones sobre la velocidad de los procesos.
2) No deben hacerse suposiciones sobre la velocidad de los procesos.
Línea 208: Línea 94:
4) La decisión de qué procesos entran a una parte crítica no puede ser pospuesta indefinidamente.
4) La decisión de qué procesos entran a una parte crítica no puede ser pospuesta indefinidamente.
Los puntos 3) y 4) evitan bloqueos mutuos.
Los puntos 3) y 4) evitan bloqueos mutuos.
5) Debe existir un límite al número de veces que otros procesos están habilitados a entrar a secciones críticas</br> después que un proceso haya hecho su pedido.
</pre>


Similar al Alg. 2, no se asegura que un solo proceso este en la zona critica, ej:
5)- Debe existir un límite al número de veces que otros procesos están habilitados a entrar a secciones críticas
<br> T0 Tarea1 encuentra C2 = 1,<br>  T1 Tarea2 encuentra C1 = 1,<br>  T2 Tarea2 pone C2 = 0,<br>  T3 Tarea1 pone C1 = 0
después que un proceso haya hecho su pedido.


===Ejercicio 8===
==Ejercicio 07:==
Similar al Alg. 3, no cumple 4) ya que ambos procesos pueden quedar en espera indefinida, ej:
<br>
<br> T0 Tarea1 encuentra C1 = 0,<br>  T1 Tarea2 encuentra C2 = 0


===Ejercicio 9===
==Ejercicio 08:==
Similar al Alg. 4, no cumple 4) ya que ambos procesos pueden quedar en espera indefinida (por el while)
<br>
 
==Ejercicio 09:==
===Ejercicio 10===
<br>
<br> No cumple 4), ya que si no hace el while suficientemente rapido siempre entra el proceso i en vez de j.
==Ejercicio 10:==
 
<br>
===Ejercicio 11===
==Ejercicio 11:==
Creo que podrian ser asi (cualquier cosa corrijan):
<br>a)
<br>a)
<pre>
<br>b)
Procedure Pi
Do
key = T;
while(key)
Swap(lock, key);
seccion critica
lock = F;
seccion no critica
End
 
Begin Main
lock = F;
Parbegin
P0;P1;...;Pn
Parend
End
</pre>
<br>
<br>
<br>b)
==Ejercicio 12:[*]==
<pre>
Procedure Pi
Do
while(TestAndSet(lock));
seccion critica
lock = F;
seccion no critica
End
 
Begin Main
lock = F;
Parbegin
P0;P1;...;Pn
Parend
End
</pre>
 
===Ejercicio 12*===
<br>Porque como cambian el valor del semaforo y despues lo evalua, al haber concurrencia se pueden quedar en WAIT dos ejecuciones de P. Ver Test-And-Set  
<br>Porque como cambian el valor del semaforo y despues lo evalua, al haber concurrencia se pueden quedar en WAIT dos ejecuciones de P. Ver Test-And-Set  
<br>
<br>
===Ejercicio 13===
==Ejercicio 13:==
<br>Si alguien llega al puente, y no hay nadie esperando de ninguno de los dos lados, se lo pone a cruzar. Si hay alguien cruzando o esperando, y alguien llega, esa persona espera. Cuando una persona termina de cruzar el puente, si hay alguien esperando del lado que llego, le dice che, ahora cruza vos. Y si no, mira para atras, y si del otro lado hay alguien esperando para cruzar le grita cheeeeeeeeeee cruza vooooooooosss.  
<br>Si alguien llega al puente, y no hay nadie esperando de ninguno de los dos lados, se lo pone a cruzar. Si hay alguien cruzando o esperando, y alguien llega, esa persona espera. Cuando una persona termina de cruzar el puente, si hay alguien esperando del lado que llego, le dice che, ahora cruza vos. Y si no, mira para atras, y si del otro lado hay alguien esperando para cruzar le grita cheeeeeeeeeee cruza vooooooooosss.  
<br> Solucion de Andy:
<br>
<pre>
==Ejercicio 14:[*]==
x=1, y=0, gentelado1=gentelado2=0
<br> Monitor ej14
<br> var: bandera : condicion
<br>
<br> procedure P(x)
<br> begin
<br> x = x-1  
<br> if (x<0) then
<br> Wait(bandera)
<br> endif
<br> end
<br>
<br> procedure V(x)
<br> begin
<br> x = x+1
<br> if (x<=0) then
<br> Signal(Bandera)
<br> endif
<br> end
<br>
<br>ver pag 19 al final
<br>
==Ejercicio 15:[*]==
<br>a)
<br> E = 1; S = 0
<br>
<br> procedure Productor
<br> begin
<br> P(E)
<br> ....
<br> V(S)
<br> end
<br>
<br> procedure Consumidor
<br> begin
<br> P(S)
<br> ....
<br> V(E)
<br> end
<br>
<br>b)
programa Productor_Consumidor;
MAX = ......;
monitor M;
buffer : Array (0..MAX-1);
in, out, n; enteros;
buff_lleno, buff_vacío: condición;
procedure Almacenar (v);
begin
if n = MAX then Wait (buff_vacío);
buffer (in) = v;
in = (in + 1) mod MAX;
n = n + 1;
Signal (buff_lleno)
end;
procedure Retirar (v);
begin
if n = 0 then Wait (buff_lleno);
v = buffer (out);
out = (out + 1) mod MAX;
n = n - 1;
Signal (buff_vacio)
end;
begin (* Cuerpo del monitor *)
in, out, n = 0;
end; (* fin monitor *)
procedure Productor;
begin
v = "dato producido"
Almacenar (v)
end;
procedure Consumidor;
begin
Retirar (v);
Hacer algo con v
end;
begin Programa-Principal
Begin;
cobegin
Productor;
Consumidor
coend
end.<br>


Proc CruzarLado1
==Ejercicio 16:[*]==
P(exc)
<br>[Diego Edit: esta mal. La idea es que cuando hay alguien escribiendo no puede haber nadie leyendo. Lo hicieron en clase a este programa [Multiples Lectores, un solo escritor]. ]
P(X)
<br>
gentelado1++
<br> X = 1  
V(exc)
<br>
cruzar
<br> procedure Leer  
P(exc)
<br> begin  
gentelado1--
<br> //leo el archivo  
if (gentelado2>0 || gentelado1==0)
<br> end  
V(Y)
<br>
else
<br> procedure Escribir  
V(X)
<br> begin  
endif
<br> P(X)  
V(exc)
<br> //escribo el archivo  
 
<br> V(X)  
Proc CruzarLado2
<br> end  
P(exc)
<br>
P(Y)
<br> begin Prog-Principal(operacion)  
gentelado2++
<br> if (operacion == "Leer") then  
V(exc)
<br> Leer  
cruzar
<br> else
P(exc)
<br> Escribir
gentelado2--
<br> endif  
if (gentelado1>0 || gentelado2==0)
<br> end
V(X)
<br>
else
==Ejercicio 17:==
V(Y)
<br>
endif
==Ejercicio 18:==
V(exc)
<br>a)
</pre>
<br> // en este esquema cualquiera de los dos puede entrar primero, si entra deshabilita al otro el acceso:
 
<br> x=1
===Ejercicio 14*===
<br> Procedure Romanos
<pre>
<br> Begin
Monitor ej14
<br> P(x)
var: bandera : condicion
<br> .....
 
<br> V(x)
procedure P(x)
<br> End
begin
<br> Procedure Egipcios
x = x-1
<br> Begin
if (x<0) then
<br> P(x)
Wait(bandera)
<br> .....
endif
<br> V(x)
end
<br> End
 
<br> Do forever
procedure V(x)
<br> Parbegin
begin
<br> Egipcio;Romanos
x = x+1
<br> End
if (x<=0) then
<br>
Signal(Bandera)
<br>b)
endif
<br> x=1
end
<br> colaegipcios=0
 
<br> w=1-colaegipcios
</pre>
<br> Procedure Romanos
(Ver pag 19 al final)
<br> Begin
 
<br> P(excl)//este semaforo no se si hace falta ay que analizarlo
===Ejercicio 15*===
<br> P(w)
a)
<br> P(x)
<pre>
<br> V(excl)
E = 1; S = 0
<br> .....
 
<br> P(excl)
procedure Productor
<br> V(x)
begin
<br> V(w)
P(E)
<br> P(excl)
....
<br>
V(S)
<br> End
end
<br> Procedure Egipcios
 
<br> Begin
procedure Consumidor
<br> P(excl)
begin
<br> // en esta parte incremento la cola, que hace que w sea negativo y los romanos se queden esperando
P(S)
<br> V(colaegipcios)
....
<br> P(x)
V(E)
<br> P(excl)
end
<br> .......
</pre>
<br> P(excl)
 
<br> V(x)
b)
<br> P(colaegipcios)
<pre>
<br> P(excl)
programa Productor_Consumidor;
<br>
MAX = ......;
<br> End
 
<br> Do forever
Monitor M;
<br> Parbegin
var: buffer : Array (0..MAX-1);
<br> Egipcios
in, out, n; enteros;
<br> Romanos
buff_lleno, buff_vacío: condición;
<br> End
<br>c)
procedure Almacenar (v);
<br>
begin
==Ejercicio 19:[*]==
if n = MAX then Wait (buff_vacío);
<br> Monitor PeruanosLocos
buffer (in) = v;
<br> var: HayCarretilla: Boolean
in = (in + 1) mod MAX;
<br> CintaOcupada, CintaLibre : condition
n = n + 1;
<br>
Signal (buff_lleno)
<br> Procedure CargarCarretilla
end;
<br> Begin
<br> If (HayCarretilla) then
procedure Retirar (v);
<br> wait(CintaLibre)
begin
<br> HayCarretilla = true
if n = 0 then Wait (buff_lleno);
<br> Signal(CintaOcupada)
v = buffer (out);
<br> End
out = (out + 1) mod MAX;
<br>
n = n - 1;
<br> Procedure RetirarCarretilla
Signal (buff_vacio)
<br> Begin
end;
<br> If (!HayCarretilla) then
<br> wait(CintaOcupada)
begin (* Cuerpo del monitor *)
<br> HayCarretilla = false
in, out, n = 0;
<br> Signal(CintaLibre)
end; (* fin monitor *)
<br> End
<br>
procedure Productor;
<br> Begin Body
begin
<br> HayCarretilla = false
v = "dato producido"
<br> End
Almacenar (v)
<br>
end;
<br> End Monitor
<br>
procedure Consumidor;
<br> Procedure ObreroCargador
begin
<br> Begin
Retirar (v);
<br> CargarCarretilla
Hacer algo con v
<br> End
end;
<br>
<br> Procedure ObreroRetirador
begin Programa-Principal
<br> Begin
Begin;
<br> RetirarCarretilla
cobegin
<br> End
Productor;
<br>
Consumidor
<br> Begin Main
coend
<br> Parbegin
end.
<br> ObreroCargador
</pre>
<br> ObreroRetirador
 
<br> Parend
===Ejercicio 16*===
<br> End
La idea es que cuando hay alguien escribiendo no puede haber nadie leyendo. (Multiples Lectores, un solo escritor)
<br>
<pre>
==Ejercicio 20:==
Escr = 1  
<br>La b). Ya que siempre uno llega primero y "espera" y el otro sigue de largo. La c tambien. Por la misma razon.  
 
<br>
procedure Leer  
==Ejercicio 21:[*]==
begin  
<br>Ni idea que quieren
P(X)
<br>
CantLectores++
==Ejercicio 22:[*]==
if (CantLectores == 1) then P(Escr)
<br>Estoy harto de esos ejercicios que te meten enunciados al pedo== ==
V(X)
<br>
//leo el archivo  
==Ejercicio 23:[*]==
P(X)
<br>Por que estamos diciendo que se puede ejecutar en paralelo:
CantLectores--
if (CantLectores == 0) then V(Escr)
V(X)
end  
procedure Escribir  
begin  
P(Escr)  
//escribo el archivo  
V(Escr)  
end  
begin Prog-Principal(operacion)  
if (operacion == "Leer") then  
Leer  
else
Escribir
endif
end
 
</pre>
 
===Ejercicio 17===
<br> (Hay que darle prioridad a un solo lado del puente)
<pre>
x = 1, sentido = izq, autosizq  = ?, autosder = ?
Proc Cruzar
if (sentido == izq)
if (autosizq > 0)
P(x)
cruzar
V(x)
P(exc)
if (--autosizq == 0)
sentido = der
V(exc)
else
sentido = der
actualiza(autos)
endif
elseif (sentido == der)
if (autosder > 0)
P(x)
cruzar
V(x)
P(exc)
if (--autosder == 0)
sentido = izq
V(exc)
else
sentido = izq
actualiza(autos)
endif
endif
</pre>
 
===Ejercicio 18===
a)
<pre>
// en este esquema cualquiera de los dos puede entrar primero, si entra deshabilita al otro el acceso:
x=1
Procedure Romanos
Begin
P(x)
.....
V(x)
End
Procedure Egipcios
Begin
P(x)
.....
V(x)
End
Do forever
Parbegin
Egipcio;Romanos
End
</pre>
 
b)
<pre>
x=1
colaegipcios=0
w=1-colaegipcios
Procedure Romanos
Begin
P(excl)//este semaforo no se si hace falta, hay que analizarlo
P(w)
P(x)
V(excl)
.....
P(excl)
V(x)
V(w)
P(excl)
End
Procedure Egipcios
Begin
P(excl)
// en esta parte incremento la cola, que hace que w sea negativo y los romanos se queden esperando
V(colaegipcios)
P(x)
P(excl)
.......
P(excl)
V(x)
P(colaegipcios)
P(excl)
End
Do forever
Parbegin
Egipcios
Romanos
End
</pre>
Esta es otra forma (Cortesia Papa Noel)
<pre>
CantEgipcios = 0
 
Procedure Romanos
If (CantEgipcios > 0) then P(X)
....
V(X)
End
 
Procedure Egipcios
CantEgipcios++
If (CantEgipcios > 0) then P(X)
P(Y)
....
V(Y)
CantEgipcios--
If (CantEgipcios == 0) then V(X)
End
</pre>
c)
 
===Ejercicio 19*===
<pre>
Monitor PeruanosLocos
var: HayCarretilla: Boolean
CintaOcupada, CintaLibre : condition
 
Procedure CargarCarretilla
Begin
If (HayCarretilla) then
wait(CintaLibre)
HayCarretilla = true
Signal(CintaOcupada)
End
 
Procedure RetirarCarretilla
Begin
If (!HayCarretilla) then
wait(CintaOcupada)
HayCarretilla = false
Signal(CintaLibre)
End
 
Begin Body
HayCarretilla = false
End
 
End Monitor
 
Procedure ObreroCargador
Begin
CargarCarretilla
End
 
Procedure ObreroRetirador
Begin
RetirarCarretilla
End
 
Begin Main
Parbegin
ObreroCargador
ObreroRetirador
Parend
End
 
</pre>
 
===Ejercicio 20===
<br>La b) ya que siempre uno llega primero y "espera" y el otro sigue de largo.
<br>La c) tambien, por la misma razon.
 
===Ejercicio 21*===
<pre>
Monitor Lectores-Escritores
var lectores : integer
escribiendo : boolean
sePuedeLeer, sePuedeEscribir : condition;
Procedure Inicio_Lectores
if (escribiendo or novacio (sePuedeEscribir))
wait(sePuedeLeer)
endif
lectores ++;
signal(sePuedeLeer);
end
Procedure Fin_Lectores
lectores - -
if (lectores = 0 )
signal(sePuedeEscribir);
endif
end
Procedure Inicio_Escritores
if (lectores <> 0 or escribiendo)
wait(sePuedeEscribir)
endif
escribiendo := TRUE
end
Procedure Fin_Escritores
escribiendo := FALSE;
if (novacio (sePuedeLeer))
signal(sePuedeLeer)
else
signal(sePuedeEscribir)
end
Begin Monitor
lectores = 0
escribiendo = FALSE
End
Procedure LECTOR
Do Forever
Inicio_Lectores;
//Leer
Fin_Lectores;
End
End
</pre>
 
===Ejercicio 22*===
<pre>
Monitor SuperTormino
 
Var: ocupado :boolean;
no_ocupado :condition;
 
Procedure Cliente
Begin
if ocupado then wait(no_ocupado);
ocupado = V;
End
 
Procedure Balanza
Begin
ocupado = F;
pesar
signal(no_ocupado);
End
 
Begin Monitor
ocupado = F;
End
</pre>
 
===Ejercicio 23*===
<br>Porque estamos diciendo que se puede ejecutar en paralelo:
<br>a = b + c  y
<br>a = b + c  y
<br>e = a / b + n * n
<br>e = a / b + n * n
<br>y no es lo mismo ejecutarlas en paralelo, que en forma secuencial ya que la 2nda instruccion tiene una dependencia de datos con la primera.  
<br>y no es lo mismo ejecutarlas en paralelo, que en forma secuencial ya que la 2nda instruccion tiene una dependencia de datos con la primera.  
<br>
<br>
[[Category:Prácticas]]
Ten en cuenta que todas las contribuciones a Cuba-Wiki pueden ser editadas, modificadas o eliminadas por otros colaboradores. Si no deseas que las modifiquen sin limitaciones, no las publiques aquí.
Al mismo tiempo, asumimos que eres el autor de lo que escribiste, o lo copiaste de una fuente en el dominio público o con licencia libre (véase Cuba-Wiki:Derechos de autor para más detalles). ¡No uses textos con copyright sin permiso!

Para editar esta página, responde la pregunta que aparece abajo (más información):

Cancelar Ayuda de edición (se abre en una ventana nueva)

Plantilla usada en esta página: