Señales en C: Contador de CTRL+C (ejemplo de SIGINT)

Os dejo aquí un sencillo ejemplo de señales que cuenta las veces que el usuario pulsa las teclas CTRL+C. El comportamiento normal al pulsar estas teclas es cerrar el programa, pero aquí asociamos la señal SIGINT (la que se produce cuando pulsamos CTRL+C) a la función contar():

#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
 
int num_pulsaciones = 0;  /* Contador de pulsaciones de  CTRL-C */
int bucle = 1;            /* Controlador de salida del bucle de espera */
void terminar_bucle ();   /* Captura la señal de alarma SIGALRM */
void contar ();           /* Captura la señal de interrupción SIGINT */
 
int main () {
    /* Asociamos la señal SIGINT a la función cortar(). La señal SIGINT la recibe el programa cuando
       se pulsa CONTROL+C. */
    signal (SIGINT, contar);
 
    /* Asocia la señal SIGALRM a la función terminar_bucle(). Cuando el programa reciba la señal alarma
       se ejecutará la función terminar_bucle. */
    signal (SIGALRM, terminar_bucle);
 
    printf ("Pulsa varias veces CTRL-C durante 15 segundos.\n");
 
    /* Programamos una alarma para dentro de 15 segundos.
       Cuando pasen 15 segundos este programa
       recibirá una señal SIGALRM. */
    alarm (15);
 
    /* Entramos en un bucle infinito del que solo saldremos 
       cuando la variable bucle cambie su valor. Esto sucederá
       cuando se ejecute la función terminar_bucle(). */
    while (bucle==1);
 
    /* Desactivamos la señal SIGINT porque ya no vamos a contar
       más veces cuando el usuario pulse CTRL+C. */
    signal (SIGINT, SIG_IGN);
 
    printf ("Has pulsado CTRL+C %d veces.\n", num_pulsaciones);
 
    return 0;
    }
 
/* Esta función se ejecutará cuando el proceso reciba la señal SIGALRM al de 15 segundos. */
void terminar_bucle () {
      // Vamos a desactivar la alarma SIGALRM. SIG_IGN hace que se ignore la señal SIGALRM.
      signal (SIGALRM, SIG_IGN);
 
      // Queremos que el bucle infinito que había en la función main se termine. Como hacemos bucle=1
      // el bucle ya no cumple la condición bucle==1 y se termina.
      bucle=0;
      printf ("¡Alarma!\n");
}
 
/* Esta función se ejecuta cada vez que se pulsa CTRL+C (señal SIGINT).
   Cuando se termine esta función el programa seguirá en el punto que se había quedado. */
void contar ()
{
    /* Primero desactivamos la señal SIGINT por si se pulsa CTRL+C 
       mientras se está ejecutando esta función. */
    signal (SIGINT, SIG_IGN);
    printf ("Has pulsado CTRL-C\n");
    num_pulsaciones++;
    // Volvemos a asociar SIGINT con la función cortar() para la próxima vez que el usuario pulse CTRL+C.
    signal (SIGINT, contar);
}

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Procesos en C: Ejemplo de un sencillo cronómetro con SIGALRM

Continuando con el artículo de la semana pasada sobre procesos en C hoy os dejo aquí un sencillo ejemplo de un cronómetro:

// Para las funciones pause y alarm:
#include <unistd.h>
// Para las constantes SIGALRM y similares
#include <signal.h>
 
#include <stdio.h>
 
// Esta es la función que se va a ejecutar cada vez que se reciba la
// señal SIGALRM
void contar_segundos() {
	// Usamos static para que se conserve el valor de "segundos"
	// entre cada llamada a la función
	static int segundos=0;
 
	segundos++;
	printf("Han pasado %d segundos.\n", segundos);
}
 
int main() {
	// Asociamos la señal SIGALRM a la función contar_segundos
	signal(SIGALRM, contar_segundos);
 
	// Ponemos en marcha un bucle
	while(1) {
		// Establecemos una alarma para dentro de un segundo
		alarm(1);
		// Pausamos la ejecución del programa para que 
		// se quede esperando a recibir una señal.
		pause();
	}
}

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados

Procesos en C: Señales (SIGINT)

Desde hace un tiempo tengo la idea de escribir sobre el tema de procesos y señales en C.

Las señales se usan para la comunicación entre procesos y manipularlos. Un ejemplo muy conocido de señal es la señal SIGINT, que se envía cuando el usuario pulsa CTRL+C durante la ejecución de un programa. Cuando el programa que estamos ejecutando recibe esta señal finalizará su ejecución.

En el siguiente ejemplo vamos a ver cómo podemos hacer para que el programa realice alguna acción especial cuando el usuario pulse CTRL+C. La acción a ejecutar va a ser mostrar el mensaje: “¿Por qué me interrumpes?”:

// Para las funciones pause y alarm:
#include <unistd.h>
// Para las constantes SIGALRM y similares
#include <signal.h>
 
#include <stdio.h>
 
// Esta función es la que vamos a usar como controlador de la señal SIGINT
void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	raise(SIGTERM);
}
 
int main() {
 
	// Asociamos la señal SIGINT con la funcion "senal"
	signal(SIGINT, despedida);
 
	// Comenzamos un bucle que hará que el programa muestre sin
	// parar el mensaje "Nada nuevo por aquí"
	while(1) {
		printf("Nada nuevo por aquí.\n");
	}
}

En este programa, cuando el usuario pulse CTRL+C, en lugar de cerrarse directamente, se ejecutará la función que hemos asociado con esta señal (la funcion despedida).


La función despedida muestra el mensaje “¿Por qué me interrumpes?” y genera la señal SIGTERM (“raise” envía una señal al propio proceso). Si no hiciéramos esto el proceso no se detendría nunca (hasta que lo “matemos” con kill). Si te aburres haz la prueba con esta funcion:

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
}

Otra posibilidad es usar la función signal de nuevo para indicar al proceso que use la acción por defecto de SIGINT (SIG_DFL – Signal Default):

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	signal(SIGINT, SIG_DFL); // Indicamos al programa que use la acción por defecto
	raise(SIGINT);
}

¿Pero por qué no podemos simplemente llamar a raise(SIGINT);?

Se podría pensar que bastaría con llamar a raise(SIGINT):

void despedida() {
	printf("------------------------\n");
	printf("¿Por qué me interrumpes?\n");
	printf("------------------------\n");
	raise(SIGINT);
}

El problema es que la señal SIGINT va a ser procesada por la función “despedida”. De esta forma, cuando pulsemos CTRL+C, se llamará a la función despedida. La función despedida genera de nuevo la señal SIGINT, que va a ser procesada de nuevo por ella misma. El resultado es que cuando pulsamos CTRL+c el proceso comenzará a ejecutar una y otra vez la función despedida mostrando el mensaje “¿Por qué me interrumpes?” sin parar.

¿Por qué? Cada vez que se pulse CTRL+C se ejecuta la función “despedida” en lugar de ejecutarse la acción por defecto (cerrar el programa).

Curso de programación en C para principiantes

Ejercicios de C resueltos y comentados