practica no 3

7
UNAN-Leon Sistemas Operativos Msc. Rina Arauz Practica No 3 Señales Contenido Introducción Envío de una señal a otros procesos Tratamiento de una señal SIGALARM para crear temporizadores en nuestros programas Ejemplo del uso de Señales entre procesos padre e hijo Introducción En muchas situaciones los programas deben de estar preparados para tratar situaciones inesperadas o impredecibles, como: error en operación en punto flotante, aviso de un reloj de alarma, la muerte de un proceso hijo, solicitud de terminación por parte del usuario (Control-C), solicitud de suspensión por parte del usuario (Control-Z), etc. Cuando una de estas situaciones se produce, el kernel envía una señal al proceso correspondiente. Además, cualquier proceso puede enviar una señal a otro proceso, si tiene permiso. En System V hay definidas 19 señales, mientras que BSD define 11 señales más. Cuando un proceso recibe una señal puede tratarla de tres formas diferentes: Ignorar la señal, con lo cual es inmune a la misma. Invocar a una rutina de tratamiento por defecto. Esta rutina la posee el kernel. Invocar a una rutina propia para tratar la señal. La rutina de tratamiento por defecto de una señal realiza una de las siguientes acciones: Termina el proceso y genera un fichero core, que contiene un volcado de memoria del contexto del proceso (dump). Termina el proceso sin generar un fichero core (quit). Ignora la señal (ignore). Suspende el proceso (suspend). Reanuda la ejecución del proceso. Envío de señal a otros procesos: kill() int kill ( int pid,int sig ) ; - kill() envía la señal con valor sig al proceso cuyo PID es pid. - La señal se envía de forma satisfactoria si el proceso que envía y el que recibe son del mismo usuario, o bien si el proceso que envía es del superusuario. - kill() funciona de forma diferente dependiendo del valor de pid:

Upload: lening-garcia

Post on 12-Dec-2015

9 views

Category:

Documents


5 download

DESCRIPTION

señales

TRANSCRIPT

Page 1: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

Practica No 3

Señales

Contenido

Introducción

Envío de una señal a otros procesos

Tratamiento de una señal

SIGALARM para crear temporizadores en nuestros programas

Ejemplo del uso de Señales entre procesos padre e hijo

Introducción

En muchas situaciones los programas deben de estar preparados para tratar situaciones inesperadas o impredecibles,

como:

error en operación en punto flotante,

aviso de un reloj de alarma,

la muerte de un proceso hijo,

solicitud de terminación por parte del usuario (Control-C),

solicitud de suspensión por parte del usuario (Control-Z),

etc.

Cuando una de estas situaciones se produce, el kernel envía una señal al proceso correspondiente. Además, cualquier

proceso puede enviar una señal a otro proceso, si tiene permiso. En System V hay definidas 19 señales, mientras que

BSD define 11 señales más. Cuando un proceso recibe una señal puede tratarla de tres formas diferentes:

Ignorar la señal, con lo cual es inmune a la misma.

Invocar a una rutina de tratamiento por defecto. Esta rutina la posee el kernel.

Invocar a una rutina propia para tratar la señal.

La rutina de tratamiento por defecto de una señal realiza una de las siguientes acciones:

Termina el proceso y genera un fichero core, que contiene un volcado de memoria del contexto del proceso

(dump).

Termina el proceso sin generar un fichero core (quit).

Ignora la señal (ignore).

Suspende el proceso (suspend).

Reanuda la ejecución del proceso.

Envío de señal a otros procesos:

kill() int kill ( int pid,int sig ) ;

- kill() envía la señal con valor sig al proceso cuyo PID es pid.

- La señal se envía de forma satisfactoria si el proceso que envía y el que recibe son del mismo usuario, o bien si el

proceso que envía es del superusuario.

- kill() funciona de forma diferente dependiendo del valor de pid:

Page 2: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

Si pid > 0 la señal se envía al proceso cuyo PID es pid.

Si pid = 0 la señal se envía a todos los procesos que pertenecen al mismo grupo del proceso

Si pid = -1 la señal se envía a todos procesos cuyo UID real es igual al UID efectivo del proceso que la envía. Si el

proceso que la envía tiene UID efectivo de superusuario, la señal es enviada a todos los procesos, excepto al proceso 0

(swapper) y 1 (init).

Si pid < -1 la señal es enviada a todos los procesos cuyo ID de grupo coincide con el valor absoluto de pid.

Para mostrar las señales que nos proporciona nuestro núcleo y su identificativo numérico asociado, usaremos

el siguiente comando:

~$ kill -l

1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL

5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE

9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2

13) SIGPIPE 14) SIGALRM 15) SIGTERM 17) SIGCHLD

18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN

22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ

26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO

30) SIGPWR 31) SIGSYS 32) SIGRTMIN 33) SIGRTMIN+1

34) SIGRTMIN+2 35) SIGRTMIN+3 36) SIGRTMIN+4 37) SIGRTMIN+5

38) SIGRTMIN+6 39) SIGRTMIN+7 40) SIGRTMIN+8 41) SIGRTMIN+9

42) SIGRTMIN+10 43) SIGRTMIN+11 44) SIGRTMIN+12 45) SIGRTMIN+13

46) SIGRTMIN+14 47) SIGRTMIN+15 48) SIGRTMAX-15 49) SIGRTMAX-14

50) SIGRTMAX-13 51) SIGRTMAX-12 52) SIGRTMAX-11 53) SIGRTMAX-10

54) SIGRTMAX-9 55) SIGRTMAX-8 56) SIGRTMAX-7 57) SIGRTMAX-6

58) SIGRTMAX-5 59) SIGRTMAX-4 60) SIGRTMAX-3 61) SIGRTMAX-2

62) SIGRTMAX-1 63) SIGRTMAX

Algunas señales importantes <signal.h>:

SIGTERM = Finalización controlada. Se envía para indicarle a un proceso que debe acabar su ejecución. Puede ser

ignorada.

SIGKILL = Finalización abrupta. No se puede ignorar

SIGINT = Interrupción. Se envía cuando se pulsa la tecla de interrupción (Ctrl+C). Por defecto, se interrumpe el

programa.

SIGCLD = Terminación de algún proceso hijo. Se envía al proceso padre. Ignorada por defecto

SIGCHLD = Cuando un proceso termina o para, el proceso envía esta señal a su padre.

Señales disponibles para el programador:

SIGUSR1 y SIGUSR2: Su significado es el que quiera definir el programador en su aplicación.

Ejemplo envío de una señal a un proceso hijo mediante el uso de kill:

Programa que crea un proceso hijo que imprime cada segundo su pid. El proceso padre duerme 10 segundos y luego

mandará una señal al hijo para que termine.

#include <stdio.h>

#include <sys/types.h>

#include <signal.h>

main()

{

int pid;

if ((pid = fork()) = = 0)

{

while(1){

Page 3: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

printf(“HIJO. PID = %d\n”,getpid());

sleep(1);

}

}

sleep(10);

printf(“ PADRE. Terminacion del Proceso %d \n”, pid);

kill (pid, SIGTERM);

exit(0);

}

Tratamiento de una señal

Para tratar una señal se utiliza la función: signal().

Cuando un proceso recibe una señal puede tratarla de tres formas diferentes: Ignorar la señal.

Invocar a una rutina de tratamiento por defecto. Esta rutina la posee el kernel.

Invocar a una rutina creada por el usuario para tratar la señal.

#include <signal.h>

void (*signal (int sig, void (*acción) ())) ();

- signal() permite a un proceso especificar la acción a tomar cuando reciba una señal en particular.

- sig especifica el número de la señal a tratar.

- acción puede tomar:

SIG_DFL: indica que se use el manejador por defecto del kernel.

SIG_IGN: indica que la señal se debe ignorar.

Cuando queremos que un proceso espere a que le llegue una señal, usaremos la función pause(). Esta función

provoca que el proceso (o thread) en cuestión “duerma” hasta que le llegue una señal. Para capturar esa señal, el

proceso deberá haber establecido un tratamiento de la misma con la función signal(). La función pause() no recibe

ningún parámetro y retorna –1 cuando la llamada a la función que captura la señal ha terminado.

#include <unistd.h>

int pause(void);

Page 4: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

Ejemplo: Ignorar la señal. Ejemplo: Invocar a una rutina de

tratamiento por defecto.

Ejemplo: Invocar a una rutina propia

para tratar la señal

#include<stdio.h>

#include<stdlib.h>

#include<signal.h>

main()

{

int pid;

pid=fork();

if (pid==0)

{

/*codigo hijo*/

signal(SIGTERM,SIG_IGN);

while(1)

{

printf("soy el hijo\n");

sleep(1);

}

}

else

{

sleep(5);

printf("\n Proceso Padre..\n");

kill(pid,SIGTERM);

printf("\n FIN DEL PADRE \n");

}

}

#include<stdio.h>

#include<stdlib.h>

#include<signal.h>

main()

{

int pid;

pid=fork();

if (pid==0)

{

/*codigo hijo*/

signal(SIGTERM,SIG_DFL);

while(1)

{

printf("soy el hijo\n");

sleep(1);

}

}

else

{

sleep(5);

printf("\n Proceso Padre...\n");

kill(pid,SIGTERM);

printf("\n FIN DEL PADRE \n");

}

}

#include<signal.h>

#include<stdlib.h>

#include<stdio.h>

#include<sys/types.h>

void mifuncion()

{

printf("\n SOY LA RUTINA DE

TRATAMIENTO POR EL

USUARIO\n");

exit(0);

}

main()

{

int pid;

pid=fork();

if (pid==0)

{

/*codigo hijo*/

signal(SIGTERM,mifuncion);

while(1)

{

printf(" soy el hijo\n");

sleep(1);

}

}

else

{

sleep(10);

printf(" Fin del Padre\n");

kill(pid,SIGTERM);

exit(0);

}

}

Page 5: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

Ejemplo (Captura la señal cuyo Id asociado esta entre 1 y 64 ):

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

void trapper(int);

main()

{

int i;

for(i=1;i<=64;i++)

signal(i, trapper);

printf("Proceso \n");

pause();

printf("Continuando...\n");

return 0;

}

void trapper(int sig)

{

signal(sig, trapper);

printf("Recibida la señal: %d\n", sig);

}

Explicación del programa:

Inicialmente declaramos una función que va a recibir un entero como parámetro y se encargará de capturar una

señal (trapper() ). Seguidamente capturamos todas las señales de 1 a 64 haciendo 64 llamadas a signal(), pasando como

primer parámetro el número de la señal (i) y como segundo parámetro la función que se hará cargo de dicha señal

(trapper). Seguidamente el programa espera a que le llegue una señal con la función pause(). El programa esperará

indefinidamente la llegada de esa señal, y cuando le enviemos una (por ejemplo, pulsando Control+C), la función

encargada de gestionarla (trapper() ) será invocada. Lo primero que hace trapper() es volver a enlazar la señal en

cuestión a la función encargada de gestionarla, es decir, ella misma, y luego saca por la salida estándar la señal

recibida. Al terminal la ejecución de trapper(), se vuelve al punto donde estábamos ( pause() ) y se continua:

SIGALARM para crear temporizadores en nuestros programas

Una utilización bastante potente de las señales es el uso de SIGALARM para crear temporizadores en nuestros

programas. Con la función alarm() lo que conseguimos es que nuestro proceso se envíe a sí mismo una señal

SIGALARM en el número de segundos que especifiquemos. El prototipo de alarm() es el siguiente:

unsigned int alarm(unsigned int seconds);

En su único parámetro indicamos el número de segundos que queremos esperar desde la llamada a alarm() para recibir

la señal SIGALARM.

La llamada a la función alarm() generará una señal SIG_ALARM hacia el mismo proceso que la invoca.

El valor devuelto es el número de segundos que quedaban en la anterior alarma antes de fijar esta nueva alarma. Esto es

importante: sólo disponemos de un temporizador para usar con alarm(), por lo que si llamamos seguidamente otra vez a

alarm(), la alarma inicial será sobrescrita por la nueva.

Page 6: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

Ejemplo de su utilización:

#include <stdio.h>

#include <signal.h>

#include <unistd.h>

void trapper(int);

main()

{

int i;

signal(14, trapper);

printf("Identificativo de proceso: %d\n", getpid() );

alarm(5);

pause();

alarm(3);

pause();

for(;;)

{

alarm(1);

() pause();

}

return 0;

}

void trapper(int sig)

{

signal(sig, trapper);

printf("RIIIIIIIIING!\n");

}

Explicación del programa:

Este programa es bastante similar al que hemos diseñado antes para capturar señales, sólo que ahora en lugar

de capturarlas todas, capturará únicamente la 14, SIGALARM. Cuando reciba una señal SIGALARM, sacará

“RIIIIIIIIING” por pantalla. El cuerpo del programa indica que se fijará una alarma de 5 segundos y luego se esperará

hasta recibir una señal, luego la alarma se fijará a los 3 segundos y se volverá a esperar, y finalmente se entrará en un

bucle en el que se fije una alarma de 1 segundo todo el rato. El resultado es que se mostrará un mensaje “RIIIIIIIIING”

a los 5 segundos, luego a los 3 segundos y después cada segundo:

Ejemplo del uso de Señales entre procesos padre e hijo

En este ejemplo, el hijo envía varias señales SIGUSR1 a su padre y al final termina por matarlo, enviándole la señal

SIGKILL:

#include <sys/types.h>

#include <stdio.h>

#include <unistd.h>

#include <signal.h>

void trapper(int sig)

{

signal(sig, trapper);

printf("SIGUSR1\n");

}

Page 7: Practica No 3

UNAN-Leon

Sistemas Operativos

Msc. Rina Arauz

main()

{

pid_t padre, hijo;

padre = getpid();

signal( SIGUSR1, trapper );

if ( (hijo=fork()) == 0 )

{ /* hijo */

sleep(1);

kill(padre, SIGUSR1);

sleep(1);

kill(padre, SIGUSR1);

sleep(1);

kill( padre, SIGUSR1);

sleep(1);

kill(padre, SIGKILL);

exit(0);

}

else

{ /* padre */

for (;;);

}

return 0;

}

EJERCICIOS

1. Realice un programa que cree un proceso hijo, deberá entrar en un bucle infinito y esperar. El proceso padre

deberá matar a su proceso hijo después de 10 segundos. Y aparecerá por pantalla el PID del padre, el PID del

hijo y un mensaje indicando la muerte del hijo y la finalización del padre.

2. Cree un proceso que nos informe de la cantidad de veces que se pulsa (CTRL+C) en un periodo de 25

segundos.

3. Realice un programa que mediante el uso de la señal SIGALARM realice lo siguiente. Un programa espera por

una cadena introducida por teclado. Si pasados 20 segundos esta acción no se realiza, el proceso deberá

decir”Termino el tiempo de espera… FIN”.

4. Implementar un programa que cree un proceso hijo, el cual deberá imprimir su PID, y PPID. El padre deberá

esperar su finalización y capturar la señal de muerte del proceso hijo e imprimir el mensaje “MI HIJO HA

MUERTO”.