MaxPar Solución

Anuncio
MaxPar
Solución
Se quiere escribir un programa en C que, dada una lista de números enteros calcule el
valor máximo de todos ellos. Los números se pasarán en un fichero como parámetro al
proceso y los valores estarán almacenados como enteros en formato interno de la máquina.
La ejecución del programa se hará desde el intérprete de comandos:
alaba> maximo N lista_entrada
donde N es el tamaño de la lista y será un número entre 1.000 y 5.000.
1- (3,5 ptos)El esquema de trabajo será: el programa principal creará dos procesos hijos que
colaborarán con el padre en el cálculo del máximo de modo que, mientras haya
números, cada uno de los hijos irá cogiendo uno e irá actualizando su máximo. Cuando
se haya terminado la lista, cada uno de los hijos comunicará al padre su máximo y
terminará. El padre, calculará el número máximo de entre estos dos valores y escribirá
por pantalla:
Los valores maximos han sido: maximo_hijo1 y maximo_hijo2
El maximo total ha sido maximo
donde maximo_hijo1, maximo_hijo2 y maximo son los valores máximos calculados por
cada uno de los hijos y por el padre, respectivamente.
Observaciones:
Sólo se pueden utilizar las llamadas al sistema vistas en clase.
Se valorará la máxima concurrencia.
POSIBLE SOLUCIÓN Y COMENTARIOS
Para que se diera la máxima concurrencia con la máxima simplicidad, teniendo en
cuenta que no se puede predecir nada sobre la velocidad y orden de ejecución de dos
procesos concurrentes (es decir, partir el trabajo por la mitad no es correcto, porque quiá
hay uno que corre más o ha empezado antes a trabajar), la mejor solución es compartir el
mismo puntero de L/E del fichero abierto.
Se podía suponer que el fichero era de tamaño N o no, puesto que el enunciado no lo
concretaba.
Se podía leer del fichero directamente, o que el padre fuera enviando por una pipe y
cada uno la fuera vaciando.
No había necesidad de ninguna sincronización vía signals.
Los espacios de direcciones son disjuntos, por lo que el resultado se ha de enviar al
padre: se podía hacer vía pipe (la misma o distinta, puesto que tampoco se especificaba en
el enunciado si el padre diferenciaba entre el resultado de cada hijo) o a través de exit().
Los enteros por definición pueden ser negativos, por lo que inicializar la variable max
a 0 no es correcto (puede distorsionar el resultado).
Las variables declaradas en el interior del main, así como los parámetros argc y argv[],
no son visibles desde otras funciones.
Una posible solución, es la siguiente:
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include <string.h>
int pi[2];
main (int argc , char* argv[]) {
int n,N,hijo1,hijo2,cont,fd,i,pi[2];
if ( argc < 3 ) perror("parametres incorrectes");
if ( pipe(pi) < 0) perror("pipe") ;
fd = open (argv[2],O_RDONLY);
switch( fork())
{
case -1 : _exit(1);
case 0: /* hijo 1*/
close(pi[1]);
cont = read(pi[0],&n, sizeof(int));
while(read(pi[0],&n, sizeof(int))>0) if(cont<n) cont=n;
exit(cont);
default:
switch( fork())
{
case -1 : _exit(1);
case 0: /* hijo 2*/
close(pi[1]);
cont = read(pi[0],&n, sizeof(int));
while(read(pi[0],&n, sizeof(int))>0) if(cont<n) cont=n;
exit(cont);
default: /* padre */
close(pi[0]);
fd = open (argv[2],O_RDONLY);
N = atoi(argv[1]);
for (i = 1; i <= N; i++)
{
read(fd,&n,sizeof(int));
write(pi[1],&n,sizeof(int));
}
close(pi[1]);
close(fd);
}
wait(&hijo1);
hijo1 = hijo1 >> 8;
wait(&hijo2);
hijo2 = hijo2 >> 8;
printf("Los valores finales han sido: %d y %d\n", hijo1, hijo2);
if (hijo1> hijo2)
printf("El maximo total ha sido: %d\n ", hijo1);
else printf("El maximo total ha sido: %d\n ", hijo2);
}
}
2- (3,5 ptos) En una segunda versión del programa, éste creará dos threads, utilizando las
funciones de la librería fluxes vista en clase. El thread principal (main) inicializará un
vector en memoria a partir de los valores pasados en el fichero como parámetro. Cada
uno de los threads calculará el máximo de los números a los que vaya accediendo de ese
vector. A partir de estos dos valores finales, el programa principal calculará el valor
máximo del total y sacará por pantalla el mismo mensaje que en el caso 1. La ejecución
del programa se mantiene igual:
alaba> maximo N lista_entrada
POSIBLE SOLUCIÓN Y COMENTARIOS
Lo mismo que en el caso anterior, para que se diera la máxima concurrencia, los flujos
tenían que ir accediendo al vector, no es la mejor solución partir el vector por la mitad y
asignarlo a cada hijo.
Al trabajar con threads, la memoria es compartida y se accede a todas las variables
globales del programa desde cualquier thread. No son visibles desde una rutina las
variables locales a otra (son locales las variables declaradas en el interior del main)
Para no recorrer repetidamente las posiciones del vector, el puntero de lectura es
global. Incrementar el puntero y acceder al elemento son dos operaciones que se han de
hacer en exclusión mutua para no saltarnos ningún elemento.
No tiene sentido utilizar pipes ni ningún otro tipo de mecanismo con llamadas al
sistema, puesto que acceden a variables compartidas.
Lo correcto es trabajar en exclusión mutua y no con sincronización.
Otra opción era que cada thread comenzara por un extremo del vector hasta cruzarse.
El vector podía inicializarse en una sola lectura o elemento a elemento, pero siempre
antes del inicio de los otros dos threads.
Un semáforo ha de inicializarse a algún valor antes de ser utilizado, y para poder
utilizarlo ha de ser visible: declarado como variable global.
3- (3 ptos) Comenta las diferencias principales entre ambas soluciones, en cuanto a la
manera de comunicarse y sincronizarse los dos "trabajadores" y los posibles problemas
que se han de solucionar (o no) en cada uno de los casos.
POSIBLE SOLUCIÓN Y COMENTARIOS
Los comentarios de este apartado han de ser coherentes con las respuestas dadas a los
dos anteriores y sobre todo, relacionados con lo que se pregunta: comunicación y
sincronización.
En el caso de los procesos, estamos en un modelo de espacios disjuntos y, por tanto, la
comunicación se hace vía mensajes, en este caso a través del sistema de ficheros (pipes) o
bien de la finalización de cada hijo. Como las llamadas al sistema garantizan la atomicidad
de las operaciones, no hay ningún problema que controlar desde la aplicación. Trabajar con
pipes, en lugar de acceder directamente al fichero (que también era correcto) da más visión
de paso de mensajes.
En el caso de los threads, la memoria es compartida, visible por todos los threads, y se
ha de controlar el acceso al puntero del vector con mecanismos de exclusión mutua.
Descargar