Examen final de USO (20-6

Anuncio
Examen final de USO (20-6-2007)
Nom i Cognoms:...............................................................................................................................
Qüestions de laboratori (2.5 punts)
Contesteu justificadament les següents preguntes a l’espai proporcionat (a no ser que s’indiqui el
contrari, les preguntes es refereixen al treball sobre el sistema operatiu Linux).
1.- De quina forma utilitza el shell el contingut de la variable PATH?
S'utilitza per determinar la ubicació dels fitxers executables corresponents a les comandes. Cada cop
que l'usuari escriu un nom de comanda sense especificar el directori on es troba l'executable, el shell
recorre la llista de directoris que conté la variable PATH buscant un fitxer executable amb aquest nom.
Si en troba un, l'executarà i suspendrà la búsqueda; altrament, mostrarà l'error Command not found.
2.- Sigui prova un programa que escriu per la sortida estàndar el valor del paràmetre argc de la rutina
main. Suposant que el directori actual conté els fitxers a, a.c, b i b.c, indiqueu el valor que s'escriurà
per la sortida estàndar si executeu prova *.c > out &
El shell interpreta els metacaràcters abans de començar l'execució del procés. És a dir, el shell fa que
s'executi en background (&), amb la stdout redireccionada a un fitxer (> out) i unicament amb els
paràmetres a.c i b.c (els fitxers coincidents amb *.c). Com argc també comptabilitaza el nom de
l'executable, el valor d'argc serà 3; aquest valor serà escrit a stdout (fitxer out).
3.- El directori dir té proteccions r-xr-xr-x. El directori conté els fitxers w, r i rw amb proteccions
-w--w--w-, r--r--r-- i rw-rw-rw- respectivament.
a) Quins fitxers del directori es podran esborrar amb la comanda rm?
b) Quins fitxers del directori es podran editar amb vim o emacs?
c) Quins fitxers del directori es podran sobreescriure utilitzant el metacaràcter > ?
a) Cap perquè no podem escriure sobre el directori. b) r i rw (tot i que únicament podrem guardar les
modificacions que fem a rw) perquè els editors només necessiten llegir i escriure sobre el fitxer. c) w i
rw perquè per modificar un fitxer no cal el bit w al directori, només cal al fitxer..
4.- Seria possible utilitzar un fitxer Makefile per crear dos fitxers executables en una única invocació
a make sense especificar cap paràmetre? En cas afirmatiu, poseu un exemple de fitxer Makefile amb
aquest comportament.
Sí. Només cal que la primera regla del Makefile (és a dir, la regla que s'executa quan no especifiquem
cap paràmetre) crei dos executables. Per exemple:
all: file1 file2
# Primera regla
file1: file1.c
# æAnàleg per a file2
gcc -o file file1.c
5.- Cal ser l'usuari root per poder modificar les proteccions d'un fitxer?
No. L'usuari propietari d'un fitxer sempre pot modificar les proteccions dels seus fitxers.
6.- A un programa escrit en llenguatge C es declara el vector char v[100] con a variable global i es
realitza la inicialització for (i=0; i<1000; i++) v[i]=3;. En executar-lo, es produeix un
Segmentation fault. A l'analitzar el fitxer core amb el debugger s'observa que l'error s'ha produit a
l'assignació v[i]=3 quan i val 555. Com és que la i no té el valor 100?
L'explicació és que les posicions de memòria immediatament posteriors a la posició on finalitza el vector
també pertanyen a l'espai logic del procés (per estar assignades a altres variables o per fragmentació
interna). En canvi, la direcció de l'hipotètic element 555 ja no pertany a l'espai logic del procés.
7.- Quines són les diferències més notables entre les crides al sistema de Unix i de Win32 relatives a la
sincronització d'un procés amb la mort d'un procés fill?
A Unix, la crida wait sincronitza amb la mort d'un fill i obté el seu codi d'acabament. En canvi, a Win32
hi ha una crida per a cada tasca (WaitForSingleObject i GetExitCode). A més, a Win32 és
possible sincronitzar-se simultaniàment amb la mort de varis fills (crida WaitForMultipleObjects).
Examen final de USO (20-6-2007)
Nom i Cognoms:...............................................................................................................................
Qüestions de teoria (2.5 punts)
Contesteu justificadament les següents preguntes a l'espai proporcionat:
1.- Indique qué significa que una biblioteca pueda ser compartida. ¿Es posible tener bibliotecas
estáticas compartidas?
Una librería es compartida si puede ser utilizada al mismo tiempo por múltiples programas. Para el caso
de librerías estáticas, las librerías serán "linkadas" o montadas en tiempo de compilación. Por lo que si
bien resulta que es utilizada por múltiples programas, cada ejecutable tiene su propia copia del código.
2.- ¿Cómo se genera un proceso zombie? ¿Es posible hacer que un proceso deje de ser zombie? En
caso afirmativo, describa cómo.
Un proceso zombie es aquél que ha finalizado su ejecución pero todavía tiene una entrada en la tabla
de procesos porque su proceso padre no ha leído aún su status. El proceso zombie dejará de existir
cuando su padre invoque a la llamada sl sistema wait.
3.- Comente brevemente tres diferencias entre los mecanismos de comunicación entre procesos: pipes,
named pipes y sockets.
Dos procesos que se comunican mediante pipes tiene que estar relacionados jerárquicamente,
mientras que con named pipes y sockets no es necesario. Pipes y named pipes son mecanismos para
comunicar procesos en forma local unicamente, mientras que mediante sockets la comunicación puede
ser remota. Pipes y named pipes son orientados a conexión, mientras que sockets puede ser no
orientado a conexión también.
4.- ¿Qué tipos de respuestas se obtiene de un proceso al enviarle una señal (signal)? ¿Es posible
modificar la respuesta? ¿Siempre?
Acciones por defecto que se toman al recibir un signal: terminar la ejecución de un proceso, terminarla
y generar un fichero imagen (core dump), suspender un proceso, continuar la ejecución en caso de que
estuviera suspendido. Es posible reprogramar la acción (incluso inhibirla) de cualquier signal excepto
de: SIGKILL y SIGSTOP
5.- ¿Qué tipo de RAID utilizaría para una aplicación que necesita velocidad en el acceso a los datos
pero no es crítica en caso de fallo? ¿Y para otra que es crítica en caso de fallo pero la velocidad no es
tan importante?
En el primer caso es más eficiente paralelizar el acceso y evitar el cuello de botella en los accesos a la
paridad: RAID-0, datos distribuídos y sin cálculo de paridad. En el segundo caso, según los tipos de
RAID vistos en clase, el RAID-6 (doble paridad y stripping)sería el más adecuado.
6.- Indique en qué casos se ejecuta la sentencia posterior a una invocación a la llamada execl?.
La sentencia a continuación de execl se ejecutará unicamente en el caso de que produzca un fallo en
dicha llamada al sistema (por ejemplo, no existe el fichero o las protecciones no nos permiten accder).
7.- ¿Qué mostrará la ejecución del siguiente código? Si repetimos la ejecución varias veces, ¿habrá
alguna variación en el valor numérico mostrado?
int d;
int *p;
p = &d;
printf("%d\n", p);
Mostrará la dirección lógica de la variable d. Para diferentes ejecuciones el valor será el mismo ya que
la dirección lógica es independiente de las direcciones físicas donde se cargue el programa.
Examen Final de USO (20-6-2007)
Analitzar shellscript (1 punt)
Indiqueu justificadament quina és la
funció del següent shellscript, indicant
quin és el significat més lògic dels
paràmetres.
#!/bin/bash
while read nom
do
if test -f $nom
then
if test ‘wc -c < $nom‘ -gt $1
then
compress $nom
fi
fi
done < $2
$1 ha de ser un enter perquè es fa servir en el contexte d'una comparació numèrica.
$2 ha de ser un nom de fitxer perquè l'entrada estàndar del bucle while està direccionada a $2.
El shellscript llegeix una a una les línies del fitxer $2. Si el contingut de la línia representa un nom de
fitxer existent, es comprova si la seva mida (en bytes) és més gran que $1. En cas afirmatiu, s'executa
la comanda compress passant-li com a paràmetre el nom del fitxer.
El shellscript finalitza quant totes les línies del fitxer $2 hagin estat llegides.
Per tant, el contingut del fitxer $2 hauria de ser noms de fitxers (un per línia).
Escriure shellscript (1.5 punts)
Escriviu un shellscript tal que, cada N segons, comprovi quants processos hi ha en execucio a la
màquina. Si el nombre de processos supera el valor M, caldrà afegir al fitxer F la data actual i el nombre
de processos en execució. Si, a més a més, el nombre de processos supera el valor P (on P > M), caldrà
executar la comanda comm en modalitat background. N, M, P i F seran paràmetres del shellscript (i en
aquest ordre); per exemple, 60 70 100 /tmp/log
Observacions: Si no recordeu el nom d’algun paràmetre d’una comanda, podeu inventar-vos-el; en
aquest cas, haureu d’indicar el significat que assigneu a aquest paràmetre inventat per tal que pugui
entendre la vostra solució i pugui avaluar la seva correctesa.
#!/bin/bash
if (test $# -ne 4)
then
echo Nombre de parametres incorrecte
exit
fi
N=$1
M=$2
P=$3
F=$4
while (sleep $N)
do
procs=`ps -aux|wc -l`
if (test $procs -gt $M)
then
echo ‘date‘ $procs >> $F
if (test $procs -gt $P)
then
comm &
fi
fi
done
Examen Final de USO (20-6-2007)
Analitzar codi (1 punt)
Indiqueu justificadament quin serà el resultat d’executar el
següent programa (nombre de processos creats, relació
pare/fill entre els processos creats, informació escrita,...).
Podeu assumir que les crides mai retornaran error.
main() {
int p1, i;
for (i=0; i<100; i++) {
p1 = fork();
if (p1 == 0)
fork();
fork();
printf("%d\n", i);
}
}
Cada execució del codi intern del bucle crea 5 nous processos (2 fills, 2 nets i 1 besnet).
Per tant, quan l'executi el procés inicial, en acabar la primera iteració (i=0) tindrem 6 (1 + 5) processos
que escriuran el valor "0".
Tots 6 processos entraran a la segona iteració (i=1). Cadascun d'ells crearà 5 nous processos amb el
que tindrem un total de 6 + 5*6 = 6^2 processos que escriuran el valor "1".
Tots 6^2 processos entraran a la tercera iteració (i=2). Cadascun d'ells crearà 5 nous processos amb el
que tindrem un total de 6^2 + 5*6^2 = 6^3 processos que escriuran el valor "2".
...
Finalment, tots 6^99 processos entraran a la darrera iteració (i=99). Cadascun d'ells crearà 5 nous
processos amb el que tindrem un total de 6^99 + 5*6^99 = 6^100 processos que escriuran el valor "99".
No podem garantir en quin ordre s'escriuran tots aquests valors perquè no es fa servir cap crida al
sistema que restringeixi l'execució dels processos.
Escriure codi (1.5 punts)
Escribid el código C que calcule el producto de dos matrices. Los coeficientes de la matriz resultado han
de ser calculados en paralelo, para ello se deberán crear tantos procesos hijo como elementos tenga
la matriz resultado.
Cada hijo, una vez hecho su cálculo, transferirá el resultado a un proceso servidor mediante un socket
TCP. El resultado irá acompañado de las coordenadas del elemento de la matriz, todo ello en una
estructura del tipo struct tupla_resultado.
struct tupla_resultado {
int i, j, value;
};
El servidor se ejecuta en la dirección IP 147.83.12.90, escucha peticiones de conexión en el port 1234
y espera que todas las respuestas relativas a un mismo cálculo le llegen por la misma conexión.
Observaciones: Es preciso especificar todos los argumentos de las llamadas al sistema. No es preciso
hacer el tractamiento de errores en las llamadas ni especificar los includes. No es preciso implementar
el programa en código C sintácticamente correcto, podéis utilizar un pseudo-código “razonable”.
No es preciso que implementéis el código del servidor.
Podéis asumir que las matrices de entrada están almacenadas en el área de datos del proceso padre
y que están ya inicializadas. El algoritmo secuencial para el cálculo del producto de matrices es:
int mat1[M][N], mat2[N][P], res[M][P], i, j, k;
for (i=0; i<M; i++) {
for (j=0; j<P; j++) {
res [i][j] = 0;
for (k=0; k<N; k++) {
res [i][j] = res [i][j] + mat1[i][k] * mat2[k][j];
}
}
}
Examen Final de USO (20-6-2007)
int mat1[M][N], mat2[N][P];
int i, j, k, res=0, sock, status;
struct tupla_resultado {
int i, j, value;
} t;
/* inicializacion_cliente: Rutina similar a la utilizada en laboratorio.
Crea el socket e invoca a la llamada connect
para establecer la conexion con el servidor
*/
sock = inicializacion_cliente("147.83.12.90", 1234);
for (i=0; i<M; i++) {
for (j=0; j<P; j++) {
if (fork()==0) {
for (k=0; k<N; k++)
res = res + mat1[i][k] * mat2[k][j];
t.i= htonl(i);
t.j= htonl(j);
t.value= htonl(res);
write (sock, &t, sizeof (t));
exit(0);
}
}
}
for (i=0; i<M*P; i++)
wait (&status);
Descargar