Introducció als Sistemes Operatius Facultat d'Informàtica de Barcelona - UPC 16 de juny de 2000 Durada: 3 hores Les notes es publicaran el dia 30 de juny a les 16:00 al WEB de la facultat. Per a demanar revisió es podrà veure l'examen el dia 3 de juliol de 10:00 a 12:00 i de 17:00 a 19:00. Les notes definitives es publicaran el dia 4 de juliol a les 16:00. Preguntes curtes (2.5 punts) a) Necessitem un mecanisme que permeti a dos processos sense parentesc comunicar-se els seus identificadors de procés. La interfície del mecanisme ha de ser int obtenir_pid_parella(), i s’encarregarà de retornar el pid de l’altre procés quan tots dos hagin invocat a la rutina. Es demana que dissenyis i implementis aquesta rutina sobre UNIX. Pots suposar que només els dos processos involucrats invocaran a la rutina. (1 punt) b) ¿En qué fase de la construcción de un fichero ejecutable intervienen los ficheros de tipo librería (por ejemplo, libc.a en UNIX)? ¿Es posible que un mismo símbolo esté definido en dos librerías diferentes? Justifica las respuestas. (0.5 punts) c) Tenemos dos posibles formas de cambiar el nombre de un fichero en UNIX: - Mediante el comando mv: alabi_% mv f1 f2 - Mediante los comandos cp y rm: alabi_% cp f1 f2 alabi_% rm f1 Teniendo en cuenta los recursos del sistema operativo utilizados para su ejecución, ¿qué diferencias hay entre ambos métodos? (0.5 punts) d) Si el código fuente del ejecutable DIR es el que se muestra a continuación, ¿cuál será el resultado de ejecutar alaba% DIR out . / /usr/bin /lib ? Justifica la respuesta (0.5 punts) void main(int argc, char *argv[]) { char s[80]; int i,status; if (argc < 3) error("Parametros incorrectos",PROPI); close(1); if (open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0644)<0) error("Open",SISTEMA); for (i = 2; i < argc; i++) { sprintf(s,"%s:\n",argv[i]); write(1, s, strlen(s)); execlp("ls","ls","-l",argv[i],(char *)0); wait(&status); } } Telefónica (3 punts) Queremos escribir un programa que emule el comportamiento de una centralita de teléfonos. Este programa, inicialmente, tendrá que crear N procesos teléfono. Cada proceso teléfono ha de ejecutar el código que se encuentra en el ejecutable línea. El ejecutable línea ya está implementado y simula el comportamiento de un teléfono. Consiste en un bucle infinito en el que en cada iteración recibe una llamada, la atiende, envía un signal SIGUSR1 a su padre y escribe por su salida estándar la información de la llamada atendida. Esta información estará en una estructura de este tipo : typedef struct { int linea; char numero[9]; int tiempo; } llamada; Queremos que la centralita sea inteligente, y por lo tanto su misión será doble: 1) Guardará en un fichero todas las informaciones de las llamadas. 2) Controlará que las líneas ni se saturen ni se aburran. Para este último objetivo, la centralita controlará cada 10 minutos cuántas llamadas ha recibido. Si este número es menor de MIN_LLAM, cancelará un telefonista mediante el signal SIGUSR2; si es mayor de MAX_LLAM, creará uno nuevo. Ten en cuenta que al menos siempre ha de haber un telefonista y que no puede haber más de MAX_TELEF. Se pide que dibujes un esquema con los procesos involucrados, cómo están comunicados y que implementes el código de la centralita. Concurrència (2 punts) Volem implementar un servidor utilitzant fluxes sobre un sistema operatiu que ens ofereix una llibreria de concurrència com la utilitzada al laboratori. Cada cop que es rebi una petició, el servidor crearà un fluxe que l’atendrà. És a dir: void *tractar_peticio(void *pet) { /* tractament d'una petició */ ... fi_fluxe(0); } main() /* codi del servidor */ { peticio *pet; inicialitzacions(); while (1) { obtenir_peticio(pet); crear_fluxe(tractar_peticio, pet); } } a) Per a evitar saturar la màquina tractant massa peticions simultàniament, volem que si s’estan tractant MAX_PETICIONS simultàniament, el servidor es bloquegi fins que acabi el tractament d'alguna petició. Indica què caldria afegir al pseudocodi anterior i a on tot justificant la resposta. b) Volem mantenir un comptador del nombre de peticions que han estat tractades completament. Indica què caldria afegir al pseudocodi anterior tot justificant la resposta. c) El servidor disposa d’un nombre màxim de fitxers temporals per a fer la seva tasca (MAX_TEMPORALS) i un camp de les peticions (pet->temporals) indica quants fitxers temporals són necessaris per a atendre cada petició (ens asseguren que pet->temporals sempre serà més petit que MAX_TEMPORALS). A tractar_petició tenim el següent codi, on temporals és una variable de tipus semàfor inicialitzat a MAX_TEMPORALS: void *tractar_peticio(peticio *pet) { int i; for (i=0; i < pet->temporals; i++) sem_wait(temporals); /* tractament d'una petició */ for (i=0; i < pet->temporals; i++) sem_signal(temporals); fi_fluxe(0); } Pot donar algun tipus de problema aquest codi? Justifica-ho. En cas afirmatiu, indica com es podria solucionar. Comecocos (2.5 punts) Queremos implementar un juego para cuatro jugadores (llamados A, B, C y D) y que utiliza un tablero de NxN posiciones sobre el sistema operativo UNIX. Cada jugador estará situado en una de las esquinas del tablero, esperará un tiempo aleatorio después del cual pondrá una de sus fichas en cada posición de una fila (o columna). Al llegar al final de la fila (o columna), el jugador volverá a esperar un tiempo aleatorio y pasará a la fila (o columna) siguiente, y así sucesivamente. Si al poner una ficha sobre una posición del tablero resulta que ya está ocupada, la ficha anterior se elimina del tablero. Ganará el juego quien, en el tiempo estipulado, tenga más fichas sobre el tablero. Representaremos el tablero mediante un fichero de NxN bytes, las fichas con los caracteres A, B, C y D, y cada jugador con un proceso. El sentido de avance de cada jugador está indicado en la siguiente figura. La duración de la partida estará parametrizada. AAAAAAAAAAA AAA CCCCCC CC CCCCCCCCCCCC BB BB BB B B B B B B B D D D D D DD DD DD DD DD Te damos el esquema del programa principal comecocos: void rut_alarma(int signum) { acabar_juego(); } main (int argc, char *argv[]) { /* Inicializaciones */ switch (pidA = fork()){ case -1: error(“fallo fork primero”); case 0: jugador_A(...); default: switch (pidB =fork()){ case -1: error(“fallo fork segundo”); case 0: jugador_B(...); default: switch (pidC = fork()){ case -1: error(“fallo fork tercero”); case 0: jugador_C(...); default: switch (pidD = fork()){ case -1: error(“fallo fork cuarto”); case 0: jugador_D(...); default: alarm(tiempo_duracion_partida); pause(); } } } } } Se pide que implementes los jugadores (rutinas jugador_A, jugador_B, jugador_C y jugador_D), las inicializaciones y que declares las variables utilizadas. Observaciones: - Todos los jugadores escriben de uno en uno los caracteres. - Dispones de la rutina espera(): espera a que pase un intervalo de tiempo aleatorio. - La ejecución del juego desde la línea de comandos se hace: alaba% comecocos fichero_tablero N tiempo_duración_partida - El tablero es lo suficientemente grande para que no llegue ningún jugador al final antes de que se cumpla la temporización.