Arquitecturas cliente/servidor
Contenido
•
Procesos
•
Semáforos
•
Sincronización
•
Lectura y Escritura de Archivos
•
Servidores Orientados a Conexión
•
Servidores No Orientados a Conexión
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Procesos
•
Un programa en ejecución es un proceso.
•
Un proceso es cualquier secuencia de
operaciones que están ejecutándose en
memoria acPva, realizando una o varias
instrucciones sobre ciertos datos.
Los procesos pueden ser concurrentes
o paralelos.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Programación concurrente
•
Cada proceso representa un programa secuencial que
ejecuta una serie de instrucciones.
•
Al ejecutarse un programa secuencial, éste sigue un solo
“hilo de control” (thread), esto es se inicia con una
operación atómica (indivisible) del proceso y se mueve a
través del proceso conforme las operaciones se van
Procesos concurrentes
•
Procesos disjuntos
–
Son disjuntos si se ejecutan en diferentes bloques
de programa, sin posibilidad de accederse entre
sí. Ambos se ejecutan simultáneamente y
proceden concurrentemente hasta que alguno o
los dos terminan.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Procesos cooperaPvos
Tienen la necesidad de comunicarse entre sí en la
realización de una tarea, comparPendo recursos en
común por lo que requieren alguna forma de
Programación paralela
•
Un programa paralelo o distribuido es aquel formado
por varios procesos secuenciales que se ejecutan en
varios procesadores conectados entre sí.
•
Si la red se forma por conexiones dentro de una sola
computadora se considera un esquema de
programación paralela.
•
Si la red se forma por conexiones entre diferentes
computadoras se considera un esquema de
programación distribuida.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Organización de memoria
•
En un sistema mulPproceso existen recursos
considerados comunes o comparPdos por todos
los procesadores, como son el caso de la
memoria y los medios periféricos.
•
La comunicación entre los procesos se clasifica en
dos Ppos:
–
Fuertemente acoplados
•
Para el caso en que el grado de interacción es alto
–
Débilmente acoplados
Comunicación entre procesos
La manera como la memoria es “reparPda” entre
los procesadores determina el MECANISMO DE
COMUNICACIÓN a uPlizar:
•
Variables compar:das
–
En caso de memoria comparPda y por lo general
procesos fuertemente acoplados.
•
Paso de mensajes
–
En caso de memoria distribuida y por lo general
procesos bajamente acoplados.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Memoria comparPda
•
Un sistema mulPproceso con memoria
comparPda es aquél que permite el acceso de
cualquier procesador del sistema a cualquier
localidad de memoria común.
•
Cada dirección es única e idénPca para todo
•
La comunicación entre procesos se realiza por
lectura y escritura
de variables en la misma
sección de memoria.
Memoria
Procesador 1
P1
P2
P3
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Memoria comparPda
•
Para asegurar la integridad de los datos en
memoria comparPda son necesarios:
–
Mecanismos para el soporte de comunicaciones
–
Ambiente de programación que provea la
planeación
–
Localización
–
Sincronización
Memoria distribuida
•
Un sistema mulPprocesador de memoria
distribuida es aquél en el que cada
procesador uPliza su propia memoria
privada.
•
La comunicación con otros procesadores
mediante una red de interconexión.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Programación Distribuida
•
La comunicación entre procesos se realiza
mediante envío y recepción de datos. Este
Conceptos de programación
concurrente y paralela
EXCLUSION MUTUA
•
Se refiere a la situación en que dos o más procesos
comunicándose entre sí, mantengan la integridad de
los datos que comparten.
–
En el caso de variables comparPdas, es la serialización de
las acciones de escritura o lectura sobre una variable.
–
En el caso de paso por mensajes se refiere a que cada
proceso es capaz de recibir un solo mensaje a la vez.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Conceptos…
CONDICIÓN DE COMPETENCIA
•
Dada la situación de exclusión mutua en la
comunicación entre procesos se hace necesario
que los procesos “
compitan
” entre sí para tomar
posesión de las variables comparPdas, o en caso
de paso de mensajes que dos o más procesos
compitan porque su mensaje sea recibido antes
por un tercer proceso.
Conceptos…
JUSTICIA
•
Se refiere al hecho de proporcionar la
oportunidad a todos los procesos que ejecuten
sus acPvidades sin que ninguno quede
suspendido o bloqueado permanentemente.
GRANULARIDAD
•
Indica la canPdad de instrucciones a ejecutar que
cada procesador realiza con respecto al Pempo
que cada proceso tarda en comunicarse
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Conceptos…
BALANCE DE TRABAJO
•
Para lograr un trabajo eficiente y una distribución
equitaPva de procesador(es) es necesario aplicar
técnicas de balance de trabajo para que se
distribuyan lo más uniformemente posibles los
procesadores.
•
Un balance de trabajo ópPmo manPene los
procesadores ocupados, procurando que todos
ellos terminen casi al mismo Pempo.
Conceptos…
TERMINACION
•
Se refiere al hecho de asegurar que todos los
procesos de un programa concurrente
finalicen correctamente.
•
Si al menos uno de los procesos no termina
por alguna causa, el programa concurrente
simplemente no termina con éxito.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Abrazo mortal (dead lock)
•
Un programa concurrente se encuentra en
abrazo mortal si todos sus procesos se
encuentran bloqueados entre sí, es decir,
ninguno puede llegar a terminar
•
También existe otro problema llamado inanición,
que es cuando un proceso no puede llegar a
ejecutarse, en el ejemplo de los filósofos que
nunca pueda comer ya que no puede obtener los
dos tenedores necesarios.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Si se Pene programación paralela eso significa que ¿se
puede dividir el Pempo de procesamiento en el
número de procesadores existentes?
P0
P1
P2
P3
P0
P2
P1
P3
Ley de Amdahl
•
Propone normalizar el Pempo que toma realizar la operación en un solo
procesador al valor de 1.
•
La fracción del cálculo que sólo se puede realizar secuencialmente será
F, entonces la fracción paralelizable es1-‐F.
•
Entonces, el incremento de velocidad máximo que puede obtenerse
con P elementos de procesamiento está dado por la ecuación:
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
El caso ideal…
•
Como un ejemplo, si nuestra aplicación no
Pene sección secuencial , entonces el
incremento de velocidad máximo estará dado
exactamente por el número de elementos de
procesamiento:
Programación concurrente y
semáforos en Java
•
En Java es posible ejecutar tareas en paralelo,
uPlizando hebras de control (hilos, threads).
Este modo de programación permite tener un
espacio de memoria, código o recursos
comparPdos. Tiene la ventaja que su
ejecución resulta más económica que un
proceso completo.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Semáforos
•
Java cuenta con semáforos implícitos de la forma:
Object mutex = new Object();
/* …*/
Synchonized (mutex){
/* … */
}
Que solo pueden ser uPlizados para exclusión mutua.
Solo una hebra de control puede ejecutarse en el bloque
synchonized en un momento dado.
Ejemplo del uso de la palabra
Synchonized
import java.io.*;
class Banco {
public staPc void main ( String args[]) {
try {
// Declaramos los dos montones de billetes
Contador co1 = new Contador ();
Contador co2 = new Contador ();
// Declaramos los dos cajeros
Cajero c1 = new Cajero(co1);
Cajero c2 = new Cajero(co2);
// Se ponen a contar..
c1.start();
c2.start();
c1.join();
c2.join();
} catch ( ExcepPon e ){
e.printStackTrace(); }
}
}
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Clase Contador: Cuenta billetes y
almacena la suma
class Contador {
int numBilletes = 0 ;
long suma = 0 ;
final int TOTAL_BILLETES = 10000 ;
final int VALOR_BILLETES = 200 ;
void cuenta () {
// A contar la suma de los billetes
for ( numBilletes =0 ; numBilletes < TOTAL_BILLETES;
numBilletes ++ ) {
suma += VALOR_BILLETES ;
// Billetes de 200 pesos Thread.yield(); }
System.out.println
( numBilletes+ " suman : "+ suma +
" pesos");
}
Clase Cajero: Recibe cierta canPdad de
billetes para contar
class Cajero extends Thread {
Contador contadorCajero ;
Cajero ( Contador paramContador )
{
contadorCajero = paramContador ;
}
public void run ()
{
contadorCajero.cuenta();
}
}
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Resultado:
•
10000 suman 2000000 pesos
•
10000 suman 2000000 pesos
Es correcto, dado que cada cajero Pene su
canPdad de billetes para contar.
ComparPendo el recurso
•
Ahora supongamos que los dos cajeros deben contar del mismo montón, o sea, lo
comparten y por tanto, la suma de lo que haya contado cada uno debe ser el
resultado total.
•
Para ello, modificaremos el código añadiendo lo siguiente
Declaramos los dos montones de billetes :
–
Contador co1 = new Contador ();
// Ahora sobra, Contador co2 = new Contador ();
// Declaramos los dos cajeros y el mismo montón.
Cajero c1 = new Cajero(co1);
Cajero c2 = new Cajero(co1);
Con este cambio obtenemos:
•
10000 suman: 2000200 pesos
•
10001 suman: 2000200 pesos
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
•
El resultado anterior es incorrecto
•
Por tanto, debemos uPlizar un mecanismo de
sincronización que garanPce que cuando un cajero
cuente un billete y lo sume, el otro no pueda intentar
coger el mismo billete y sumarlo. La solución que
ofrece Java para resolver este problema es de lo más
simple y eficiente, uPlizando la cláusula synchronized
en la declaración del método donde se realiza la tarea
"críPca".
•
Por tanto, cambiaremos el método void cuenta() por:
•
Si realizamos ese cambio, obtenemos el
siguiente resultado:
•
10000 suman : 2000000 pesos
•
10000 suman : 4000000 pesos
•
Esto ocurre porque no se inicializa la variable
suma antes del ciclo que cuenta los billetes,
por lo que el segundo cajero conPnúa la suma
en donde la dejó el anterior.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Inicializando el contador dentro de
cada
•
Si modificamos el código e incluimos la inicialización, tendremos:
void cuenta () {
// Cada cajero cuenta lo suyo …
suma = 0 ;
// A contar la suma de los billetes
for ( numBilletes =0 ; numBilletes < TOTAL_BILLETES ;numBilletes ++ )
{
suma += VALOR_BILLETES ;
Thread.yield();
}
}
A parPr de este momento obtenemos el siguiente resultado esperado tal y como detallamos:
10000 suman : 2000000 pesos
•
Otra forma de realizar la sincronización consiste en declarar el
objeto comparPdo como sincronizado en vez del método que lo
conPene. Se realiza entonces el siguiente cambio:
public void run(){
contadorCajero.cuenta();
}
por:
public void run(){
synchronized (contadorCajero ) { contadorCajero.cuenta();
}
}
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Monitores
•
El siguiente nivel dentro de la solución a los
problemas de exclusión mutua y sincronización
entre procesos concurrentes fue desarrollado por
C.A.R. Hoare y P. Brinch Hansen.
•
Ahora se considera como recurso comparPdo no
únicamente las variables comparPdas, sino
también a los procedimientos y funciones que
actúan sobre las variables
Seudocódigo
•
Notación propuesta por Hoare (Simula 67)
monitorname: monitor
begin
//declaraciones de datos locales del monitor
procedure
//procname { parametros formales }
begin
// cuerpo del procedimiento
…
// otros procedimiento locales del monitor
end
…
..
//inicialización de los datos locales del monitor
end
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Monitores
Ejemplo
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
:TubTest t:Tuberia p:Productor c:Consumidor new Tuberia() new Productor(t) new Consumidor(t) start( ) start( ) run( ) run( ) lanzar(c ) lanzar(c ) recoger(c )
…
…
sleep( ) sleep( ) estaVacia == false siguiente++ estaLlena==false siguiente-‐-‐ estaVacia == false siguiente++…
…
:TubTest t:Tuberia p:Productor c:Consumidor new Tuberia() new Productor(t) new Consumidor(t) start( ) start( ) run( ) run( ) lanzar(c ) lanzar(c ) recoger(c )
…
…
sleep( ) sleep( ) (estaVacia==true)? estaVacia== false…
…
…
wait() noPfy() estaLlena==false siguiente-‐-‐Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza
Lectura y Escritura de Archivos
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class LecturaEscrituraArchivos {
public staPc void main(String args[]){
copiaArchivo("c:/archivoEntrada.txt", "c:/archivoSalida.txt");
}
•
public staPc void copiaArchivo (String archivoLectura, String archivoEscritura){
try{
FileInputStream fileInput = new FileInputStream(archivoLectura);
BufferedInputStream bufferedInput = new BufferedInputStream(fileInput);
FileOutputStream fileOutput = new FileOutputStream(archivoEscritura);
BufferedOutputStream bufferedOutput = new BufferedOutputStream(fileOutput);
byte [] array = new byte [1];
int leidos= bufferedInput.read(array);
while(leidos > 0){
bufferedOutput.write(array);
leidos=bufferedInput.read(array);
}
bufferedInput.close();
bufferedOutput.close();
}catch(ExcepPon e){
e.printStackTrace();
}
}
}
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo RezaRandomAccessFile
•
Mediante los objetos de esta clase uPlizamos
archivos binarios mediante un acceso aleatorio,
tanto para lectura como para escritura.
•
Existe un índice que nos indica en qué posición
del archivo nos encontramos, y con el que se
puede trabajar para posicionarse en el archivo.
•
RandomAccessFile(String nombre, String modo)
–
nombre: cadena idenPficadora del archivo
–
modo: si será de lectura y/o escritura
Ejemplo de algunos métodos de
escritura
•
La escritura del archivo se realiza con una función que depende el Ppo de datos que se desee
escribir.
•
void write( byte b[], int ini, int len ); Escribe len caracteres del vector b.
•
void write( int i ); Escribe la parte baja de i (un byte) en el flujo.
•
void writeBoolean( boolean b ); Escribe el boolean b como un byte.
•
void writeByte( int i ); Escribe i como un byte.
•
void writeBytes( String s ); Escribe la cadena s tratada como bytes, no caracteres.
•
void writeChar( int i ); Escribe i como 1 byte.
•
void writeChars( String s ); Escribe la cadena s.
•
void writeDouble( double d ); Convierte d a long y le escribe como 8 bytes.
•
void writeFloat( float f ); Convierte f a entero y le escribe como 4 bytes.
•
void writeInt( int i ); Escribe i como 4 bytes.
•
void writeLong( long v ); Escribe v como 8 bytes.
•
void writeShort( int i ); Escribe i como 2 bytes.
•
void writeUTF( String s ); Escribe la cadenas UTF
•
Para la lectura existen métodos análogos para leer cada uno de los Ppos de datos.
Arquitecturas Cliente/Servidor, Sem 2015-‐1 M.I.Yasmine Macedo Reza