Post on 29-Jul-2015
Programación de Sistemas
3.3.1 Administración de procesos:
Comunicación entre Procesos: Señales
MCC Enrique Ayala Franco
Introducción
La multiprogramación necesita que los procesos se comuniquen entre sí para poder colaborar de forma efectiva.
Ya se han mencionado algunos mecanismos utilizando hilos, pero también hay otras formas más primitivas de comunicación entre procesos, por ejemplo las señales.
El sistema utiliza señales para informar a un determinado proceso sobre alguna condición, realizar esperas entre procesos, etc.
Sin embargo, las señales no son suficientes para transmitir toda la información, por ello es necesario incorporar otros mecanismos para compartir datos entre procesos.
2
Introducción
El enfoque más obvio de todos es utilizar ficheros del sistema para poder escribir y leer de ellos, pero esto es lento, poco eficiente e inseguro, aunque muy sencillo de hacer.
El siguiente paso podría ser utilizar una tubería para intercomunicar los procesos a través de él. El rendimiento es superior respecto al enfoque anterior, pero sólo se utilizan en casos sencillos.
Como evolución de todo lo anterior llegó el sistema IPC (Inter Process Communication) de System V, con sus tres tipos de comunicación diferentes: semáforos, colas de mensajes y segmentos de memoria compartida.
3
Introducción
Actualmente IPC del System V ha sido reemplazado por otro estándar, el IPC POSIX.
Ambos implementan características avanzadas de los sistemas de comunicación entre procesos de manera bastante eficiente, por lo que convendría pensar en su empleo a la hora de realizar una aplicación multiproceso bien diseñada.
A continuación veremos algunos conceptos y ejemplos de manejo de señales y también el uso de tuberías y redireccionamiento.
4
Señales en UNIX/LINUX
5
6
Objetivos
Utilizar señales en programas para comprender su influencia y efectos sobre los procesos.
Conocer los tipos de señales POSIX 1b para llevar a cabo operaciones de forma asíncrona.
7
¿Señales?
Hay ocasiones en las que interesa manejar sucesos asíncronos, es decir, que pueden suceder en cualquier momento, no cuando nosotros los comprobemos.
La manera más sencilla de implementar esto es mediante el uso de señales.
La pérdida de la conexión con el terminal, una interrupción de teclado o una condición de error podrían desencadenar que un proceso recibiese una señal. Una vez recibida, es tarea del proceso atrapar o capturarla y tratarla.
Si una señal no se captura, el proceso muere.
8
¿Señales?
Una señal es una notificación por software para un proceso de la ocurrencia de cierto evento.
Cuando ocurre el evento la genera. La señal se deposita cuando el proceso realiza una
acción con base en ella. El tiempo de vida de la señal es desde que se genera
hasta que se deposita. La señal queda pendiente si aún no se deposita. Es
decir pueden haber colas de señales.
9
El envío de señales
En Linux/Unix las señales tienen un nombre simbólico que inicia con SIG. Están definidos en <signal.h>. El la tabla estan las requeridas por POSIX. Los nombres representan enteros mayores que 0.
Símbolo Significado
SIGABRT
SIGALRM
SIGFPE
SIGHUP
SIGILL
SIGINT
SIGKILL
SIGPIPE
SIGQUIT
SIGSEGV
SIGTERM
SIGUSR1
SIGUSR2
Terminación anormal como la iniciada por abort
Señal de espera como la iniciada por alarm
Error en operación aritmética, como la división por cero
Colgado (muerte) de la terminal de control (proceso)
Instrucción de hardware no válida
Señal de atención interactiva
Terminación (no se puede atrapar o ignorar)
Escritura en un entubamiento sin lectores
Terminación interactiva
Referencia no válida de memoria
Terminación
Señal 1 definida por el usuario
Señal 2 definida por el usuario
10
El envío de señales
Algunas señales como SIGFPE y SIGSEGV se generan al ocurrir errores. Otras se generan mediante llamadas específicas, aunque un usuario sólo
puede mandar señales a procesos que posee. Se requiere el ID del usuario real.
Todas las señales pueden ser ignoradas o bloqueadas, a excepción de SIGSTOP y SIGKILL, que son imposibles de ignorar.
La señales se generan desde el shell mediante el comando kill.
Ejemplo: kill
Sintaxis:
Kill –<señal> pid > kill –USR1 3543
Muchas señales tiene por omisión la terminación de procesos.
> kill –l Muestra la lista de señales definidas en el núcleo del sistema
11
Señales y procesos
Un proceso atrapa una señal si éste ejecuta el manejador de señal cuando se deposita una señal.
El programa instala el manejador mediante una llamada sigaction con el nombre de la función a ejecutar.
O un SIG_DFL o un SIG_IGN. SIG_DFL: realiza una acción
preestablecida. SIG_IGN: ignora la señal y se
desecha.
12
El envío de señalesDesde un programa en C:
//envio de señales con la llamada a kill// ¿quién mata a quien??#include <sys/types.h>#include <signal.h>#include <stdio.h>int main (void){int pid;pid = fork();switch (pid){ case 1: perror ("No se ha podido crear el hijo"); break; case 0: printf("Soy el hijo, mi PID es %d y mi PPID es %d\n", getpid(), getppid()); // caso siniestro, el hijo mata a su padre if( kill(getppid(), SIGTERM) == -1) // kill envía la señal al proceso.
perror("Error en kill"); break; default: printf ("Soy el padre, mi PID es %d y el PID de mi hijo es %d\n", getpid(), pid ); printf("%d",kill(pid,SIGTERM) );
// el padre mata al hijo}return 0;}
13
El envío de señales
El comando raise también manda una señal al proceso en primer plano.
raise(SIGUSR1); // enviar señal a sí mismo
Otras formas de hacer esto mismo:pthread_kill(pthread_self(), sig);kill(getpid(), sig);
El comando stty –a informa de las características del dispositivo de entrada estándar, incluyendo configuración de caracteres que generan señales.
> stty –aspeed 38400 baud; rows 24; columns 77; line = 0;intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = <undef>;eol2 = <undef>; swtch = M-^?; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R;werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0;….
14
El envío de señales
La función alarm hace que se envíe una señal SIGALRM al proceso que la invoca después de un tiempo determinado en segundos.
No se apilan, si se llama de nuevo se reinicia con el nuevo valor. Su efecto por default es terminar el proceso.
Ejemplo: programa que termina en x segundos.
#include <unistd.h>#include <signal.h>#include <stdio.h>int main (void){alarm(5);printf("Espero 5 segundos para terminar...\n");alarm(1);printf("no mejor 2 segundos ...\n");for(;;){ }printf("No creo llegar hasta aqui...\n");return 0;}
15
Máscaras de señales
Una máscara de señal también determina el tipo de acción a realizar.
La máscara contiene una lista de las señales que en determinado tiempo están bloquedas.
Las señales bloquedas no se desechan, esperan hasta que el proceso las libere y entonces serán depositadas.
Los programas utilizan: sigprocmask para cambiar la máscara y bloquear señales. O un manejador especificando a SIG_IGN como una llamada
sigaction para ignorar señales.
16
Máscaras de señales
Sirven para impedir, de forma temporal, que un proceso deposite la señal, bloqueando las señales.
La mascara de señal indica el conjunto de señales que serán bloqueadas (no ignoradas).
Es de tipo sigset_t. El proceso modifica la máscara mediante el sigprocmask de su señal. Funciones de bloqueo y desbloqueo de grupos de señales:
Int sigemptyset(sigset_t *set); // ninguna señal Int sigfillset(sigset_t *set); // todas las señales Int sigaddset(sigset_t *set, int signo); // agregar una señal al grupo Int sigdelset(sigset_t *set, int signo); // eliminar una señal del grupo Int sigismember(const sigset_t *set, int signo); // 1 si es miembro del
grupo
17
Máscaras de señales//uso de mascaras de señales#include <signal.h>#include <stdio.h>int main (void){sigset_t dossigs;// inicializar mascarassigemptyset(&dossigs);sigaddset(&dossigs, SIGINT); sigaddset(&dossigs, SIGQUIT); sigaddset(&dossigs, SIGKILL); sigaddset(&dossigs, SIGTERM); // aplicar mascaraif(sigprocmask(SIG_BLOCK, &dossigs, NULL))
perror("No se pudo bloquear señales\n");alarm(25);printf("Intenta salir de esto...\n");sleep(10);printf("Termino de dormir\n");// desbloquear señalesif(sigprocmask(SIG_UNBLOCK, &dossigs, NULL))
perror("No se pudo desbloquear señales\n");for(;;){}printf("Algo no se configuro bien...\n");return 0;}
Sintaxis: Sigprocmask(int modo, const sigset_t *set, sigset_t *pset)
El modo puede ser:
SIG_BLOCK: añade para bloquear el conjunto de señales.
SIG_UNBLOCK: borrar una colección se señales del bloqueo.
SIG_SETMASK: establece la máscara de las señales que serán bloqueadas.
18
Como atrapar señales
Se usa la función sigaction para establecer los manejadores de señales de un proceso.
La información del manejador se almacena en una estructura de tipo struct sigaction.
La llamada al sistema tiene tres parámetros: Número de señal o apuntador a función. Apuntador a la estructura de tipo sigaction del nuevo manejador Apuntador a la estructura anterior
En el llamado se llenan los valores con la información anterior de la llamada, si se coloca un apuntador NULL, nada cambia.
Sintaxis: Int sigaction(int señal, const struct sigaction *act, struct sigaction *oact);
Struct sigaction { void()*sa_handler) (); // SIG_DFL, SIG_IGN o apuntador a función sigset_t sa_mask; // señales adicionales se bloquearán durante ejecución
// del manejadorint sa_flags; // banderas especiales y opciones
}
19
Como atrapar señales: ejemplo
#include <signal.h>
static void handler(int signum) { /* Realizar acciones para la señal entregada */ }
int main() { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART;
/* reiniciar si se interrumpe por el manejador */ if (sigaction(SIGINT, &sa, NULL) == -1) /* manejo de error */; /* mas código */ }
¿Qué pasa después de ejecutar el manejador de señal?
20
Ejemplo mas detallado
#include <stdio.h>#include <stdlib.h>#include <signal.h>
static void manejador1(int signum){ // acciones si se captura señal printf("He capturado la señal ctrl-c\n"); puts("Quieres continuar?: "); char sn=getc(stdin); //leer caracter desde
stdin //fflush(stdin); // limpiar buffer
if(sn=='n' || sn=='N'){ printf("Proceso interrumpido por el usuario");exit(0);}else printf("Continuar hasta que expire mi tiempo...\n");
}
int main() {struct sigaction sa;sa.sa_handler = manejador1;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;if( sigaction(SIGINT, &sa, NULL) ==-1) perror("Error al tratar la señal\n");printf("Para interrumpir el proceso ctrl-c\n");alarm(25);for(;;){ // pensando...
}printf("Continuamos con el proceso\n"); return 0;}
21
Ignorar una señal
// Ignorar la señal ctrl-c#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <signal.h>#include <string.h>
char mensaje[]="Se encontro un ctrl-c\n";
static void catch_ctrl_c(int signum){
write(STDERR_FILENO,mensaje,strlen(mensaje));
// write es seguro para llamadas asincronas
}
int main() {struct sigaction sa;//sa.sa_handler = catch_ctrl_c; //
establecer nuevo manejadorsa.sa_handler = SIG_IGN; // ignorar señalsigemptyset(&sa.sa_mask); // no mas
señales bloqueadassa.sa_flags = 0; // ninguna opcion
especialif( sigaction(SIGINT, &sa, NULL) < 0) perror("Error al tratar la señal\n");printf("Para interrumpir el proceso ctrl-
c!!!!!\n");alarm(15);for(;;){ // pensando...}printf("Continuamos con el proceso\n"); return 0;}
Ejemplo con signal
// ejemplo signal.c con funcion signal()
#include <stdio.h>#include <stdlib.h>#include <signal.h>#include <unistd.h>#include <errno.h>
static void manejador1(int signum){// acciones si se captura señalif(signum==SIGINT){printf("He capturado la señal ctrl-c\n");puts("Quieres continuar?: ");char sn[20];fflush(stdin); // limpiar buffer gets(sn); //leer caracter desde stdin //fflush(stdin); // limpiar buffer if(sn[0]=='n' || sn[0]=='N'){printf("Proceso interrumpido por el usuario");exit(0); }else{ printf("Continuar hasta que expire mi tiempo...\n");if( signal(SIGINT, manejador1) ) // necesario volver a especificar señal a capturarperror("Error al tratar la señal\n");}
}else{fprintf(stderr,"Error: esperado=%d, recibido=%d \n", SIGINT, signal);}}
int main() {int n, i=1,j=0;
if( signal(SIGINT, manejador1) ) perror("Error al tratar la senial\n");printf("Para interrumpir el proceso ctrl-c\n");
for(i=1;i<5000; i++){ // pensando...
for(j=0; j<35000; j++)printf("");}printf("Fin de proceso\n"); system("pause");return 0;}
22
23
Ejercicio
Hacer un comando mycat , que despliegue por pantalla el contenido de un archivo de texto, pasado como argumento en la línea de comandos. El programa debe aceptar una interrupción por software en donde se pregunte si se desea continuar con el despliegue o terminar.
Variantes: Mientras se esta desplegando se acepta la señal. Si esta
fuera del despliegue no. Ignorar ctrl-d y otras interrupciones.