práctica 2 - escuela técnica superior de ingeniería de ... · pdf...

25
ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18 PRÁCTICA 2 Llamadas al sistema Página - 0

Upload: lycong

Post on 26-Feb-2018

215 views

Category:

Documents


3 download

TRANSCRIPT

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

PRÁCTICA 2

Llamadas al sistema

Página - 0

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

ÍNDICE

1 OBJETIVOS......................................................................................................................................................2

2 LLAMADAS RELACIONADAS CON PROCESOS......................................................................................2

3 LLAMADAS RELACIONADAS CON FICHEROS.......................................................................................8

4 LLAMADAS PARA COMUNICAR PROCESOS........................................................................................10

5 LLAMADAS RELACIONADAS CON SEÑALES.......................................................................................12

6 PUNTUACIÓN...............................................................................................................................................24

Página - 1

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

1 OBJETIVOS

Los principales objetivos de esta práctica son:

Uso de llamadas relacionadas con procesos: fork, getpid, getppid, wait, exit y exec

Uso de llamadas relacionadas con ficheros: open, read, write y close

Uso de llamadas para comunicar procesos: pipe y dup2

Uso de llamadas relacionadas con señales: sigaction, kill, alarm y pause

2 LLAMADAS RELACIONADAS CON PROCESOS

Para comprender cómo se invocan, así como la funcionalidad de algunas de las llamadas alsistema operativo relacionadas con procesos, vamos a compilar y ejecutar los programas quese indican a continuación:

1. fork1.c Observar PID’s

2. fork2.c Observar PID’s retrasando al hijo

3. fork3.c Observar PID’s esperando que termine el hijo

4. fork4.c Observar PID’s con terminación explícita del hijo

5. fork5.c Multiplexación algo extraña de la CPU

6. fork6.c Multiplexación más aparente de la CPU

7. ejecutor.c Una mini shell

Todos estos programas están accesibles en el servidor del departamento en la carpetasisope00.

Encender el puesto de trabajo y entrar en Linux.

Copiarse al escritorio la imagen Minix-qemu.img desde la carpeta de sisope00.

Copiarse al escritorio la imagen disquete-p2.img que contendrá todos los ficherosnecesarios para la realización de esta práctica.

Arrancar MINIX: qemu -fda disquete-p2.img Minix-qemu.img

Entrar al sistema, crearse una carpeta “practica2” [mkdir practica2] y copiarse en ellatodos los ficheros del disquete [mtools copy a:* practica2]. Comprobar que secopian 21 archivos.

Echar un vistazo al programa fork1.c cuyo aspecto se muestra en la página siguiente.

Anotar qué llamadas al sistema operativo utiliza este programa:

Página - 2

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/*------------------------------------------------------*//* fork1.c: Observar PID's *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>

main(void) { int pid; pid = fork(); if (pid==0) { printf("[H] MiPadre = %5d Yo = %5d\n", getppid(), getpid());

} else { printf("[P] MiPadre = %5d Yo = %5d MiHijo = %5d\n", getppid(), getpid(), pid); }}

Si se duda sobre la semántica de alguna de estas llamadas al SO, consultar la ayuda enlínea “utilidad man” o comentarlo con el profesor. Escribir a continuación la salidaque creéis que aparecerá por pantalla al ejecutar este programa (inventarse losidentificadores de proceso):

Compilar este programa: cc fork1.c -o fork1

Ejecutar este programa [./fork1]. ¿Ha salido lo que se esperaba? Ejecutar varias veceshasta que se observe que la salida es distinta de la actual (¡Fijarse en los PID’s!).Escribir la conclusión que puede sacarse de la ejecución de este programa:

Echar ahora un vistazo al programa fork2.c cuyo aspecto se muestra en la páginasiguiente.

Observar que se ha introducido un retardo “sleep(1)” en el código del proceso hijo.

Compilar y ejecutar este programa.

¿Qué se observa al ejecutar este programa?

Página - 3

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/*------------------------------------------------------*//* fork2.c: Observar PID's retrasando al Hijo *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>#include <sys/wait.h>

main(void) { int pid; pid = fork(); if (pid==0) { sleep(1); printf("[H] MiPadre = %5d Yo = %5d\n", getppid(), getpid());

} else { printf("[P] MiPadre = %5d Yo = %5d MiHijo = %5d\n", getppid(), getpid(), pid); }}

Echar ahora un vistazo al programa fork3.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* fork3.c: Observar PID's: esperando que termine Hijo *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>#include <sys/wait.h>

main(void) { int pid, estado; pid = fork(); if (pid==0) { sleep(1); printf("[H] MiPadre = %5d Yo = %5d\n", getppid(), getpid());

} else { printf("[P] MiPadre = %5d Yo = %5d MiHijo = %5d\n", getppid(), getpid(), pid); pid = wait(&estado); printf("[P]: Termino el hijo %d con estado %d\n", pid, estado); }}

Observar que se ha introducido una espera “wait” del padre a que termine el hijo.

Compilar y ejecutar este programa.

¿Qué se observa al ejecutar este programa –especialmente en relación al estado determinación-?

Página - 4

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Para acabar con esta primera aproximación a la comprensión de las llamadas fork ywait, vamos a utilizar la llamada exit cuya sinopsis (ejecutar “man -s 2 exit”) es:

void _exit (int status)

Ahora podemos comprender todavía mejor el comportamiento del programa anterior.Cuando un proceso padre hace wait, la variable “estado”, donde se recibe el estado determinación del hijo, tiene el significado siguiente:

Las macros _HIGH( ) y _LOW( ) definidas en /usr/include/sys/wait.h nos permitenacceder de forma cómoda a cada uno de los octetos representativos del estado segúnque el proceso haya terminado por sí mismo o haya sido matado.

Echar ahora un vistazo al programa fork4.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* fork4.c: Observar PID's: Terminacion explicita Hijo *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>#include <sys/wait.h>

main(void) { int pid, estado; pid = fork(); if (pid==0) { sleep(1); printf("[H] MiPadre = %5d Yo = %5d\n", getppid(), getpid()); _exit(3);

} else { printf("[P] MiPadre = %5d Yo = %5d MiHijo = %5d\n", getppid(), getpid(), pid); pid = wait(&estado); printf("[P]: Termino el hijo %d con estado %d\n", pid, _HIGH(estado)); }}

Observar que se ha introducido una terminación explícita del hijo que devuelve elestado “3” al padre.

Compilar y ejecutar este programa.

Si queda alguna duda en relación con fork, wait o exit, preguntar al profesor.

Página - 5

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Ahora vamos a observar cómo puede apreciarse que el sistema operativo es capaz de dar lailusión de que se están ejecutando dos o más procesos de forma “concurrente”. Para ello,utilizaremos un programa en el que el padre creará un proceso (hijo) y ambos se pondrán aescribir caracteres por la pantalla: el padre Pes y el hijo Haches.

Echar un vistazo al programa fork5.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* fork5.c: Multiplexa la CPU algo raro *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>

void imprimirLetras(char laLetra) { int i;

for (i=1; i<1000; i++) { printf ("%c", laLetra); if ((i%60) == 0) printf ("\n"); } printf (“\n”);}

main(void) { int pid;

pid = fork(); if (pid==0) { imprimirLetras ('H'); } else { imprimirLetras ('P'); }}

Compilar y ejecutar este programa.

¿Qué se observa al ejecutar este programa? Ejecutarlo varias veces si se creeconveniente.

Echar ahora un vistazo al programa fork6.c cuyo aspecto puede verse en la páginasiguiente.

Compilar y ejecutar este programa.

¿Por qué ahora el comportamiento parece más apropiado?

Página - 6

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/*------------------------------------------------------*//* fork6.c: Ver multiplexar la CPU *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>

void imprimirLetras(char laLetra) { int i, j, basura;

for (i=1; i<1000; i++) { printf ("%c", laLetra); if ((i%60) == 0) printf ("\n"); basura = 0; for (j=0; j<10000; j++) basura++; } printf (“\n”);}

main(void) { int pid;

pid = fork(); if (pid==0) { imprimirLetras ('H'); } else { imprimirLetras ('P');} }

Para terminar este apartado, vamos a probar el ejemplo de un ejecutor de comandos muysimple, como el contado en clases de teoría, cuyo aspecto se muestra a continuación:

/*------------------------------------------------------*//* ejecutor.c: Mini shell *//*------------------------------------------------------*/#include <stdio.h>#include <unistd.h>#include <sys/wait.h>

main(void) { int pid, estado; char programa[20];

printf (">"); scanf("%s", programa); while (programa[0] != '0') { if (fork() == 0) { estado = execl(programa, 0); printf ("Error en %s => %d\n", programa, estado); _exit (1); } else { pid = wait(&estado); printf (">"); scanf("%s", programa);} } }

Página - 7

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Compilar y ejecutar este programa. Cuando aparezca el “prompt” –indicador denuestra minishell de que desea atendernos-, teclear fork1. Deberá ejecutarse elprograma fork1 tal y como sucedió al inicio de esta práctica.

Intentar ejecutar con nuestro comando el programa ps. ¿Por qué no funciona?

Intentar, a pesar de todo, ejecutar el comando ps desde nuestro ejecutor de comando.Si no se sabe hacerlo, consultar con el profesor. Anotar cómo se consigue esto:

Ejecutar ahora el ejecutor desde el propio ejecutor. ¿Qué pasa? Comentar el efecto:

Modificar ligeramente el programa ejecutor.c para que informe justo antes del promptdel identificador del proceso ejecutor y así tener la certeza de quién nos estáatendiendo.

3 LLAMADAS RELACIONADAS CON FICHEROS

En esta sección tan sólo probaremos el ejemplo contado en clase de teoría que hace unaimplementación elemental del comando “cp”.

Entrar en MINIX y echar un vistazo al programa copiar.c cuyo aspecto es el siguiente:

/* copiar.c: Una aproximacion al comando cp */#include <stdio.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>

#define BLOQUE 4096 /* Luego probar con 1 */

main(int argc, char **argv) { char buffer[BLOQUE]; int dfOrg, dfDst, leidos, escritos;

dfOrg = open (argv[1], O_RDONLY); dfDst = open (argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0600); do { leidos = read (dfOrg, buffer, BLOQUE); write (dfDst, buffer, leidos);

Página - 8

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

} while (leidos == BLOQUE); close(dfOrg); close(dfDst);}

Observar el uso que hacemos de la llamada al sistema open cuya sinopsis es lasiguiente:

int open (const char *path, int flags [, mode_t mode])

Esta función abre el fichero cuyo nombre de ruta (path) se indica, devolviendo elmenor descriptor disponible, detalle este último importante como veremos en elejemplo de la sección siguiente cuando trabajemos con pipes.

Los valores que pueden utilizarse como “flags” son los siguientes:

O_RDONLY Sólo lecturaO_WRONLY Sólo escrituraO_RDWR Lectura y escrituraO_APPEND Se sitúa al final del ficheroO_CREAT Crea el fichero si no existeO_TRUNC Trunca el tamaño del fichero a ceroO_EXCL Con O_CREAT, si no existe el fichero, falla

El parámetro “mode” indica el modo de protección del fichero si es de nueva creación.

Compilar y ejecutar el programa para observar su correcto funcionamiento. Probar conel comando “./copiar copiar.c copiarbis.c”.

Comprobar que se ha copiado correctamente (utilizar comandos: ls -l; cat y diff).

Ahora vamos a comprobar la diferencia entre escribir BLOQUE a BLOQUE frente acarácter a carácter. Para ello, vamos a copiar un fichero de mayor tamaño. Teclear elcomando “time ./copiar /usr/log/messages mensajes” y anotar el tiempo que tarda:

Ahora editar el programa copiar.c y cambiar el tamaño de BLOQUE de 4096 a 1.Compilarlo y teclear “time ./copiar /usr/log/messages mensajesbis”. Anotar eltiempo para constatar la diferencia.

Página - 9

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

4 LLAMADAS PARA COMUNICAR PROCESOS

Una forma de comunicar procesos pesados (que no pueden hacerlo a través de memoriacomún), es el uso de ficheros especiales denominados “pipes” que se crean con la llamada alsistema pipe cuya sinopsis es la siguiente:

int pipe (int fildes[2])

Esta llamada crea un mecanismo especial de entrada/salida de tal forma que se dispone de dosdescriptores de fichero:

fildes[0] De sólo lectura fildes[1] De sólo escritura

La escritura en un pipe (a través de fildes[1]) permite ir almacenando octetos en el pipe (hastaun máximo de 7.168 en MINIX 3) antes de que se bloquee al proceso. Posteriores lecturas delpipe (a través de fildes[0]), leerán los caracteres previamente almacenados por las escrituras.

Para que dos procesos se comuniquen por este mecanismo, lo que nos falta es conseguir queun proceso “Pa” utilice el extremo de escritura y el otro proceso “Pb” el extremo de lecturade ese mismo pipe. Vamos a utilizar este mecanismo para que un proceso Padre le comuniqueal Hijo –a través de un pipe común- quién es su abuelo.

Echar un vistazo al programa miabuelo.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* miabuelo.c: Padre informa de abuelo al hijo por pipe *//*------------------------------------------------------*/

#include <stdio.h>#include <unistd.h>

main(void) { int pid, miAbuelo, miPadre, tubo[2];

pipe(tubo); pid = fork(); if (pid==0) { read (tubo[0], &miAbuelo, sizeof(miAbuelo)); printf("[H] MiPadre = %5d Yo = %5d MiAbuelo = %5d\n", getppid(), getpid(), miAbuelo);

} else { miPadre = getppid(); write (tubo[1], &miPadre, sizeof(miAbuelo)); printf("[P] MiPadre = %5d Yo = %5d MiHijo = %5d\n", getppid(), getpid(), pid); }}

Página - 10

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Compilar y ejecutar el programa anterior y anotar lo que sale por pantalla paracomprobar que el funcionamiento es el correcto:

Por último, vamos a utilizar la llamada al sistema dup2 cuya sinopsis es la siguiente:

int dup2 (int oldd, int newd)

Esta llamada duplica el descriptor de fichero “oldd” devolviendo como nuevo descriptor defichero el indicado como “newd”. Si “newd” se corresponde con un fichero previamenteabierto, lo cierra antes de hacer la duplicación. Al ejecutarse el dup2, es indistinto utilizar“oldd” o “newd” para acceder al fichero que inicialmente sólo manipulábamos a través de“oldd”.

Con esta facilidad y cierta argucia, como veremos más adelante, se consigue redirigir la E/Sde un proceso.

Lo que vamos a hacer es un programa que se comporte como el comando: ls -l | wc. Paraello, el proceso padre creará un proceso hijo de tal forma que ambos queden conectadosmediante un pipe. El padre ejecutará el primer comando “ls -l” y el hijo el segundo “wc”. Loúnico que falta es conseguir que la salida estándar (descriptor 1) del padre –por donde escribeel comando ls- se redirija a la entrada estándar (descriptor 0) del hijo –por donde lee elcomando wc-.

Echar un vistazo al programa lswc.c cuyo aspecto es el siguiente:

/* lswc.c: Uso de pipe y dup para comando "ls -l | wc" */#include <assert.h>#include <stdio.h>#include <unistd.h>

int tubo[2];

main(void) { int idHijo, estado;

assert(pipe(tubo)==0); if (fork() != 0) { /* El Padre ejecuta el comando ls */ assert (dup2 (tubo[1], 1) == 1); /* Salida estandar => tubo[1] */ assert (close(tubo[0]) == 0); /* Cerramos extremo lectura */ execl("/bin/ls", "ls", "-l", NULL); } else { /* El Hijo ejecuta el comando wc */ assert (dup2 (tubo[0], 0) == 0); /* Entrada estandar => tubo[0] */ assert (close(tubo[1]) == 0); /* Cerramos extremo escritura */ execl("/usr/bin/wc", NULL);} }

Página - 11

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Intentar comprender el programa anterior y consultar con el profesor en caso de dudas.

Ejecutar el comando “ls -l” y observar su salida.

Ejecutar ahora “ls -l | wc” y anotar lo que sale por pantalla:

Compilar y ejecutar el programa lswc.c comprobando que funciona correctamente.

5 LLAMADAS RELACIONADAS CON SEÑALES

Para comprender cómo se invocan, así como la funcionalidad de algunas de las llamadas alsistema operativo relacionadas con señales, vamos a compilar y ejecutar los programas que seindican a continuación:

1. sigint1.c Observar tratamiento por defecto de SIGINT

2. sigint2.c Capturar señal SIGINT

3. sigint3.c Capturar dos SIGINT para matar al proceso

4. sigint4.c Ignorar la señal SIGINT

5. sigkill.c Enviar la señal SIGKILL a un proceso

6. mitime.c Aproximación al comando time

7. unminuto.c Segundero corriendo unos 60 segundos

8. cifra.c Cifra con crypt una clave de tres letras

9. descifra.c Descifra por fuerza bruta una clave encriptada

10. despara1.c Descifra en paralelo con dos procesos

11. despara2.c Descifra en paralelo usando SIGUSR1

Las señales son interrupciones software que pueden llegarle a un proceso comunicando unevento asíncrono (por ejemplo el usuario ha tecleado <Ctrl-C>).

Las señales que gestiona el sistema operativo están definidas en el archivo/usr/include/signal.h cuyo contenido es el siguiente:

#define SIGHUP 1 /* hangup */#define SIGINT 2 /* interrupt (DEL) */#define SIGQUIT 3 /* quit (ASCII FS) */#define SIGILL 4 /* illegal instruction */#define SIGTRAP 5 /* trace trap (not reset when caught) */#define SIGABRT 6 /* IOT instruction */#define SIGBUS 7 /* bus error */#define SIGFPE 8 /* floating point exception */#define SIGKILL 9 /* kill (cannot be caught or ignored) */#define SIGUSR1 10 /* user defined signal # 1 */#define SIGSEGV 11 /* segmentation violation */#define SIGUSR2 12 /* user defined signal # 2 */#define SIGPIPE 13 /* write on a pipe with no one to read it */#define SIGALRM 14 /* alarm clock */#define SIGTERM 15 /* software termination signal from kill */#define SIGEMT 16 /* EMT instruction */#define SIGCHLD 17 /* child process terminated or stopped */

Página - 12

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

#define SIGWINCH 21 /* window size has changed */

Puede observarse que una señal también puede enviarse por errores de ejecución“excepciones hardware” como SIGILL –intento de ejecutar una instrucción ilegal- oSIGSEGV –intento de acceder a una dirección inválida-. La expiración de una temporización(puesta por la llamada alarm que veremos más adelante), también provoca que se envíe unaseñal (SIGALRM), en este caso al proceso que puso la alarma. Además, hay dos señales quepueden ser utilizadas (enviadas y recibidas) por los procesos de usuario.

Un proceso puede seleccionar qué hacer si le llega una señal concreta, optando entre:

a. Dejar el tratamiento por defecto (matar al proceso y generar un volcado del contextodel proceso “core dumped” si procede)

b. Ignorar la señal (salvo para SIGKILL)

c. Capturar la señal y tratarla de forma específica (escribiendo un procedimiento)

Vamos ahora a empezar a ver el comportamiento de las señales con unos cuantos programasde prueba.

Echar un vistazo al programa sigint1.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* sigint1.c: Obervar tratamiento por defecto SIGINT *//*------------------------------------------------------*/

#include <stdio.h>

main(void) { int i,j;

for (i=0; i<50000; i++) for (j=0; j<10000; j++); printf ("Fin de sigint1\n");

}

Compilar y ejecutar este programa tecleando: time ./sigint1.

Anotar la salida que aparece por la pantalla:

Volver a ejecutar el mismo comando, pero ahora pulsar <Ctrl-C> antes de quetermine. Anotar la salida (debe entenderse qué es lo que ha pasado):

Página - 13

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Volver a ejecutar el programa ahora sin time y pulsando también <Ctrl-C>. Anotar lasalida explicando la diferencia que se observa:

Ahora vamos a capturar la señal SIGINT. Para ello, disponemos de la llamada sigaction cuyasinopsis es:

int sigaction (int sig, const struct sigaction *act, struct sigaction *oact)

donde:

sig Indica la señal que queremos tratar

act Apunta a una estructura que define el nuevo comportamiento que queremospara cuando llegue la señal “sig”

oact Apunta a una estructura donde el sistema nos dejará el comportamiento quetenía la señal “sig” por si más adelante queremos restaurarle

La estructura “sigaction” que permite definir el nuevo comportamiento ante la llegada de unaseñal, está definida en el fichero /usr/include/signal.h y es la siguiente:

struct sigaction { __sighandler_t sa_handler; /* SIG_DFL, SIG_IGN, or pointer to function */ sigset_t sa_mask; /* signals to be blocked during handler */ int sa_flags; /* special flags */};

Echar un vistazo al programa sigint2.c cuyo aspecto es el siguiente:

/* sigint2.c: Capturar senial SIGINT */

#include <stdio.h>#define _POSIX_SOURCE#include <signal.h>

struct sigaction trataSenial;

void capturaSigInt (int i) { printf ("Capturada la senial SIGINT\n");}

main(void) { int i,j;

trataSenial.sa_handler = capturaSigInt; sigaction (SIGINT, &trataSenial, NULL); for (i=0; i<50000; i++) for (j=0; j<10000; j++); printf ("Fin de sigint2\n");}

Página - 14

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Compilar y ejecutar este programa tecleando <Ctrl-C> tres o cuatro veces antes de quetermine el programa. Anotar lo que sale por pantalla y si el resultado es el que seesperaba.

Ahora vamos a probar un programa similar a sigint2.c, llamado sigint3.c, cuya ejecución va arequerir que se tecleen dos veces <Ctrl-C> para matar al proceso. Con el primer <Ctrl-C> seinformará que se ha capturado la señal y se restaurará el tratamiento de la señal al que teníapor defecto para que el siguiente <Ctrl-C>, si se teclea antes de que termine el proceso, lemate.

Echar un vistazo al programa sigint3.c cuyo aspecto puede apreciarse en la páginasiguiente.

Comprobar cómo se restaura el tratamiento antiguo de la señal.

Compilar y ejecutar el nuevo programa para comprobar su correcto funcionamiento.

/* sigint3.c: Forzar dos SIGINT para matar proceso */

#include <stdio.h>#define _POSIX_SOURCE#include <signal.h>

struct sigaction trataSenial, trataAnteriorSenial;

void capturaSigInt (int i) { printf ("Capturada la senial SIGINT\n"); sigaction (SIGINT, &trataAnteriorSenial, NULL);}

main(void) { int i,j;

trataSenial.sa_handler = capturaSigInt; sigaction (SIGINT, &trataSenial, &trataAnteriorSenial); for (i=0; i<50000; i++) for (j=0; j<10000; j++); printf ("Fin de sigint3\n");}

Ahora vamos a ignorar el tratamiento de la señal SIGINT de forma que el usuario nopueda matarnos tecleando <Ctrl-C>. Echar un vistazo al programa sigint4.c que semuestra a continuación:

Página - 15

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/* sigint4.c: Ignorar senial SIGINT */

#include <stdio.h>#define _POSIX_SOURCE#include <signal.h>

struct sigaction trataSenial;

main(void) { int i,j;

trataSenial.sa_handler = SIG_IGN; sigaction (SIGINT, &trataSenial, NULL); for (i=0; i<50000; i++) for (j=0; j<10000; j++); printf ("Fin de sigint4\n");}

Observar cómo se consigue ignorar la señal.

Compilar y ejecutar el programa. Comprobar que no hay forma de abortar la ejecucióncon <Ctrl-C>.

Ahora vamos a utilizar la llamada al sistema operativo kill cuya sinopsis es la siguiente:

int kill (pid_t pid, int sig)

Esta llamada envía la señal “sig” al proceso “pid”, mecanismo por el que podríamosmandarnos SIGUSR1 y SIGUSR2 entre procesos. Nosotros la vamos a utilizar para matar aun proceso enviándole la señal SIGKILL.

Echar un vistazo al programa sigkill.c cuyo aspecto aparece en la página siguiente.

Observar cómo se llama a kill para que el proceso padre mate al proceso hijo. Si nosfijamos bien en el código, veremos que el hijo ejecuta un bucle relativamente grandemientras que el padre está esperando una orden nuestra (bastará con teclear la teclaRETURN/INTRO) para matar al hijo.

Compilar y ejecutar el programa pulsando la tecla RETURN en cuanto veamosaparecer los caracteres ‘H’ característicos de la ejecución del hijo. Anotar lo queaparece en las dos últimas líneas de la pantalla:

¿Podemos asegurar que hemos matado al proceso hijo?

Página - 16

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/* sigkill.c: Enviar senial Kill a un proceso hijo */

#include <stdio.h>#include <unistd.h>#define _POSIX_SOURCE#include <signal.h>#include <sys/types.h>#include <sys/wait.h>

void hijo (void) { int i=1;

do { printf ("%c", 'H'); if ((i%60)==0) printf ("\n"); } while (i++ < 1000000); printf ("Fin del proceso hijo\n"); _exit(3);}

main(void) { int pid, resultado, estado; char ch;

pid = fork(); if (pid==0) { hijo(); } else { scanf("%c", ch); resultado = kill (pid, SIGKILL); printf ("Enviado SIGKILL al hijo con resultado %d\n", resultado); resultado = wait(&estado); if (estado > 255) printf ("Fin hijo %d con estado %d\n", resultado, _HIGH(estado)); else printf ("Terminado hijo %d con estado %d\n", resultado, _LOW (estado)); }}

Ejecutar otra vez el programa pero esperando ahora a que el proceso hijo termine:dejarán de salir ‘H’ y se indicará “Fin del proceso hijo”. En ese momento –no hacefalta correr-, pulsar la tecla RETURN y anotar lo que aparece en las dos últimas líneasde la pantalla:

Página - 17

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

¿Cuál es la diferencia entre ambas ejecuciones y a qué es debido?

Ahora vamos a utilizar la llamada al sistema operativo alarm cuya sinopsis es la siguiente:

unsigned int alarm (unsigned int seconds)

Esta llamada provoca que el sistema operativo le envíe la señal SIGALRM al proceso al cabode los segundos indicados en el parámetro “seconds”. Vamos a utilizarla para escribir unprogramita que se parezca al comando time que dice el tiempo que tarda en ejecutarse unprograma.

Echar un vistazo al programa mitime.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* mitime.c: Aproximacion al comando time *//*------------------------------------------------------*/#include <stdio.h>#define _POSIX_SOURCE#include <signal.h>#include <unistd.h>

struct sigaction trataSenial;int segundos;

void tic (int i) { segundos++; alarm(1);}

main(void) { int i,j;

segundos = 0; trataSenial.sa_handler = tic; sigaction (SIGALRM, &trataSenial, NULL); alarm(1); for (i=0; i<50000; i++) for (j=0; j<10000; j++); printf ("Tardo = %d segundos\n", segundos);}

Observar cómo se utiliza alarm en conjunción con sigaction para ir anotando(asíncronamente con la ejecución del proceso), el paso del tiempo.

Página - 18

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Compilarlo y ejecutarlo. Anotar lo que sale por pantalla:

Volver a ejecutarlo pero ahora de la forma siguiente: time ./mitime. Anotar lo quesale por pantalla y explicar la diferencia:

Para terminar con esta sección, vamos a utilizar la llamada al sistema operativo pause cuyasinopsis es la siguiente:

int pause (void)

Esta llamada suspende al proceso hasta que llegue una señal a través de kill o del timer debidoa un alarm que vence su plazo. Nosotros vamos a utilizarlo para implementar unpseudosegundero.

Echar un vistazo al programa unminuto.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* unminuto.c: Segundero corriendo unos 60 segundos *//*------------------------------------------------------*/#include <stdio.h>#define _POSIX_SOURCE#include <signal.h>#include <unistd.h>

struct sigaction trataSenial;

void tic (int i) {}

main(void) { int segundos;

trataSenial.sa_handler = tic; sigaction (SIGALRM, &trataSenial, NULL); for (segundos=1; segundos<61; segundos++) { alarm(1); pause(); printf ("%2d\n", segundos); }}

Compilar y ejecutar el programa para observar su correcto funcionamiento.

Página - 19

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Ahora vamos a intentar utilizar la señal SIGUSR1 para comunicación entre procesos. Paraello, desarrollaremos de forma muy simplificada un programa de reventar (crackear) unaclave (password) que ejecutaremos tanto en versión secuencial como en versión paralelautilizando sólo dos procesos. Esta parte la haremos sobre Linux que permite utilizar lamáquina real que tiene un procesador de 4 núcleos.

Salirse de MINIX y qemu. Crear la carpeta p2Linux en el escritorio y copiarse losficheros que residan en dicha carpeta en el usuario sisope00.

Las claves que utilizaremos serán tan sólo de cinco letras ya que reventar una clave de másletras mediante fuerza bruta (probar todas las combinaciones posibles) nos llevaría muchotiempo. Las claves se cifran con el servicio “crypt” que puede consultarse con el manual enlínea de Linux.

Echar un vistazo al programa cifra.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* cifra.c: Cifrar claves *//*------------------------------------------------------*/#define X_OPEN_SOURCE#include <stdio.h>#include <unistd.h>

int main(int argc, char **argv) {

printf("%s cifrada = %s\n", argv[1], crypt(argv[1], "aa")); return (0);º}

Compilar este programa “make cifra” y ejecutarlo para cifrar las claves “esesi” y“ojosi” anotando los valores de las claves cifradas en la Tabla que aparece acontinuación:

Clave cruda Clave cifrada T. secuencial T. Paralelo1 T. Paralelo2

esesi

ojosi

Tabla 1: Tiempos de reventar claves cifradas con crypt

Echar un vistazo al programa descifra.c cuyo aspecto es el siguiente:

/*------------------------------------------------------*//* descifra.c: Descifra una clave de 5 letras *//*------------------------------------------------------*/#define X_OPEN_SOURCE#include <stdio.h>#include <unistd.h>

#define NUM_LETRAS 26 /* a .. z */#define LONG_CLAVE_CRUDA 5 /* aaaaa .. zzzzz */#define NUM_COMBINACIONES 11881376 /* 26 elevado a 5 */

Página - 20

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

void siguientePassword(char *laPassword) { int i, letraTope = 'a'+NUM_LETRAS;

i = LONG_CLAVE_CRUDA-1; do { laPassword[i]++; if (laPassword[i] == letraTope) { laPassword[i] = 'a'; i--; } else break; } while (i >= 0);}

int main(int argc, char **argv) {char password[LONG_CLAVE_CRUDA+1] = "aaaaa\0";int i;

for (i=0; i<NUM_COMBINACIONES; i++) { if ((i%456976) == 0) printf("%s\n", password); if (strcmp(argv[1], crypt(password, "aa")) == 0) { printf ("La password es: %s\n", password); break; } siguientePassword(password); }}

Puede comprobarse que este programa empieza a buscar desde la clave “aaaaa” hasta la“zzzzz” haciendo “crypt” de cada una de ellas y finalizando cuando encuentra coincidenciacon la clave cifrada pasada como argumento.

Compilar “make descifra” y ejecutar este programa “time ./descifraaaxxxxxxxxxxx” para descifrar las claves cifradas correspondientes a “esesi” y“ojosi”. Comprobar que localiza las claves y anotar en la Tabla 1 los tiemposempleados en la búsqueda de las claves.

Ahora vamos a realizar la búsqueda en paralelo con dos procesos, uno buscará en laprimera parte del recorrido de búsqueda (desde “aaaaa” hasta “mzzzz”) y el otroproceso buscará en la segunda mitad (desde “naaaa” hasta “zzzzz”). Echar un vistazoal programa despara1.c cuyo aspecto puede consultarse en la página siguiente.

Compilar y ejecutar este programa para descifrar en paralelo las claves cifradascorrespondientes a “esesi” y “ojosi”. Comprobar que localiza las claves y anotar en laTabla 1 los tiempos empleados en la búsqueda paralela de las claves. ¿Qué problemase hace evidente en esta forma de búsqueda?

Página - 21

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

/*------------------------------------------------------*//* despara1.c: Descifra paralelo clave de 5 letras *//* Primera vesion sin usar SIGUSR1 *//*------------------------------------------------------*/

#define _XOPEN_SOURCE#include <stdio.h>#include <unistd.h>#include <sys/wait.h>

#define NUM_LETRAS 26 /* a .. z */#define LONG_CLAVE_CRUDA 5 /* aaaaa .. zzzzz */#define NUM_COMBINACIONES 11881376 /* 26 elevado a 5 */

void siguientePassword(char *laPassword) { int i, letraTope = 'a'+NUM_LETRAS;

i = LONG_CLAVE_CRUDA-1; do { laPassword[i]++; if (laPassword[i] == letraTope) { laPassword[i] = 'a'; i--; } else break; } while (i >= 0);}

void buscar(char *cifrada, char *laPassword) {int i;

for (i=0; i<(NUM_COMBINACIONES/2); i++) { if ((i%456976) == 0) printf("%s\n", laPassword); if (strcmp(cifrada, crypt(laPassword, "aa")) == 0) { printf ("%d la password es: %s\n", getpid(), laPassword); break; } siguientePassword(laPassword); }}

int main(int argc, char **argv) {int v, estado;char laPassword[LONG_CLAVE_CRUDA+1] = " aaaa\0";

v = fork(); if (v == 0) { laPassword[0] = 'n'; buscar(argv[1], laPassword); } else { laPassword[0] = 'a'; buscar(argv[1], laPassword); wait(&estado); } printf ("%d termina\n", getpid());}

Página - 22

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Para intentar resolver el problema del programa “despara1.c” vamos a utilizar la señalSIGUSR1 para que el primer proceso que localice la clave le avise al otro proceso paraque termine la búsqueda (ya estéril). A continuación se muestra un esqueleto delprograma que, con las modificaciones adecuadas, deberá mejorar los resultadosobtenidos anteriormente. Echar un vistazo al programa para entender dónde insertarcódigo para enviar y tratar la señal SIGUSR1:

/* despara2.c: Descifra paralelo clave de 5 letras */#define _XOPEN_SOURCE#include <stdio.h>#include <unistd.h>#include <signal.h>#include <sys/types.h>#include <sys/wait.h>

#define NUM_LETRAS 26 /* a .. z */#define LONG_CLAVE_CRUDA 5 /* aaaaa .. zzzzz */#define NUM_COMBINACIONES 11881376 /* 26 elevado a 5 */

void siguientePassword(char *laPassword) { int i, letraTope = 'a'+NUM_LETRAS;

i = LONG_CLAVE_CRUDA-1; do { laPassword[i]++; if (laPassword[i] == letraTope) { laPassword[i] = 'a'; i--; } else break; } while (i >= 0);}

void buscar(char *cifrada, char *laPassword, int pidDelOtro) {int i;

for (i=0; i<(NUM_COMBINACIONES/2); i++) { if ((i%456976) == 0) printf("%s\n", laPassword); if (strcmp(cifrada, crypt(laPassword, "aa")) == 0) { printf ("%d la password es: %s\n", getpid(), laPassword); break; } siguientePassword(laPassword);} }

int main(int argc, char **argv) {int v, estado;char laPassword[LONG_CLAVE_CRUDA+1] = " aaaa\0";

v = fork(); if (v == 0) { laPassword[0] = 'n'; buscar(argv[1], laPassword, getppid()); } else { laPassword[0] = 'a'; buscar(argv[1], laPassword, v); wait(&estado); } printf ("%d termina\n", getpid());}

Página - 23

ETSISI-UPM Sistemas Operativos [SW y SI] Curso 17/18

Compilar y ejecutar este programa para descifrar en paralelo las claves cifradascorrespondientes a “esesi” y “ojosi”. Comprobar que localiza las claves y anotar en laTabla 1 los tiempos empleados en la búsqueda paralela de las claves. ¿Qué podemosconcluir viendo ahora todos los tiempos de la Tabla 1?

6 PUNTUACIÓN

El apartado 2 “Llamadas relacionadas con procesos” supone 0,3 puntos, el apartado 3“Llamadas relacionadas con ficheros” aporta 0,1 puntos, el apartado 4 “Llamadas paracomunicar procesos” aporta 0,2 puntos y el último apartado “Llamadas relacionadas conseñales” supone los restantes 0,4 puntos. Los apartados deben irse completando de formasecuencial. La puntuación total de esta práctica, por lo tanto, es de un punto (1,0).

Página - 24