En un sistema operativo, los procesos que se ejecutan simultáneamente interactúan entre sí, incluso cuando son independientes. Esta interacción ocurre cuando varios procesos acceden a los mismos recursos, y el SO dispone de un gestor de procesos para determinar el orden de acceso.
Señales
Las señales son un tipo de mensajes que actúan como mecanismo de comunicación entre procesos. Comparado con otros medios de comunicación (sockets, pipes, etc.), resultan un mecanismo más limitado porque no permiten transmitir datos, pero proporcionan dos servicios fundamentales:
-
Defensa del proceso frente a incidencias del kernel: Si las señales no son gestionadas, ignoradas o capturadas por el proceso destinatario, este concluye inmediatamente, causando pérdida irrecuperable de datos.
-
Comunicación entre procesos para eventos excepcionales: Por ejemplo, cuando un usuario desea interrumpir un proceso de impresión enviado por error.
Las señales pueden llegar en cualquier momento, por lo que los procesos no pueden simplemente verificar una variable; deben lanzar una rutina de tratamiento que gestione automáticamente su recepción.

Región crítica y sincronización
La región crítica es el trozo de código de un proceso que puede interferir con otro proceso. La sincronización entre dos procesos permite que uno ejecute un conjunto de instrucciones cuando otro se lo indique, o que se paralice la actividad hasta que se cumpla una condición determinada.
Secuencia de ejecución padre-hijo
La ejecución se realiza en paralelo y puede repetirse indefinidamente:
- El proceso padre crea un proceso hijo
- El padre ejecuta un conjunto de acciones
- Si no hay error:
- El padre envía señal
SIGUSR1al hijo para que comience - El hijo realiza sus acciones
- El hijo devuelve señal
SIGUSR1al padre - Se repite desde el paso 2
- El padre envía señal
- En caso de error:
- El padre envía
SIGTERMal hijo para que termine - El hijo termina
- El padre termina
- El padre envía
El comando kill
El comando kill en Linux envía una señal a un proceso, indicando primero la señal y luego el PID. Por ejemplo, kill -9 PID termina el proceso con ese identificador.
Funciones en C para manejo de señales
signal()
void (*signal(int señal, void(*Func)(int)))(int);
Envía una señal invocando un manejador por puntero para que la reciba y la trate.
pause()
int pause(void);
Detiene el proceso hasta recibir una señal.
sleep()
unsigned int sleep(unsigned int seconds);
Duerme el proceso durante los segundos indicados. Se interrumpe si recibe una señal.
kill()
int kill(int pid, int señal);
Envía una señal para terminar un proceso.
Ejemplo: padre enviando señales a hijo
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
void manejador (int segnal){
printf("Hijo recibe señal... %d \n", segnal);
}
int main(){
int pid_hijo;
pid_hijo = fork(); // creamos hijo
switch(pid_hijo){
case -1:
printf("ERROR AL CREAR EL PROCESO HIJO... \n");
exit(-1);
break;
case 0: // HIJO
signal(SIGUSR1, manejador); // Invocamos al puntero al que referencia la función
while(1){};
break;
default: // PADRE
sleep(1);
kill(pid_hijo, SIGUSR1);
sleep(1);
kill(pid_hijo, SIGUSR1);
sleep(1);
break;
}
return 0;
}
En este ejemplo el proceso padre crea un hijo con fork(). El hijo registra un manejador para SIGUSR1 y queda en un bucle infinito esperando señales. El padre envía SIGUSR1 al hijo dos veces con un segundo de espera entre cada envío, y el hijo imprime un mensaje cada vez que la recibe.