2. 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
3. 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
4. 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
6. 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. 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. 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. 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. 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. 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. 12
El envío de señales
Desde 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 %dn",
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 %dn", getpid(), pid );
printf("%d",kill(pid,SIGTERM) );
// el padre mata al hijo
}
return 0;
}
13. 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 –a
speed 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. 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. 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. 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. 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 mascaras
sigemptyset(&dossigs);
sigaddset(&dossigs, SIGINT);
sigaddset(&dossigs, SIGQUIT);
sigaddset(&dossigs, SIGKILL);
sigaddset(&dossigs, SIGTERM);
// aplicar mascara
if(sigprocmask(SIG_BLOCK, &dossigs, NULL))
perror("No se pudo bloquear señalesn");
alarm(25);
printf("Intenta salir de esto...n");
sleep(10);
printf("Termino de dormirn");
// desbloquear señales
if(sigprocmask(SIG_UNBLOCK, &dossigs, NULL))
perror("No se pudo desbloquear señalesn");
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. 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 manejador
int sa_flags; // banderas especiales y opciones
}
19. 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. 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-cn");
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ñaln");
printf("Para interrumpir el proceso ctrl-cn");
alarm(25);
for(;;){ // pensando...
}
printf("Continuamos con el proceson");
return 0;
}
21. 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-
cn";
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 manejador
sa.sa_handler = SIG_IGN; // ignorar señal
sigemptyset(&sa.sa_mask); // no mas
señales bloqueadas
sa.sa_flags = 0; // ninguna opcion
especial
if( sigaction(SIGINT, &sa, NULL) < 0)
perror("Error al tratar la señaln");
printf("Para interrumpir el proceso ctrl-
c!!!!!n");
alarm(15);
for(;;){ // pensando...
}
printf("Continuamos con el proceson");
return 0;
}
22. 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ñal
if(signum==SIGINT){
printf("He capturado la señal ctrl-cn");
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 capturar
perror("Error al tratar la señaln");
}
}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 senialn");
printf("Para interrumpir el proceso ctrl-cn");
for(i=1;i<5000; i++){ // pensando...
for(j=0; j<35000; j++)
printf("");
}
printf("Fin de proceson");
system("pause");
return 0;
}
22
23. 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.