Es el terminal (controlador) que intercepta el ^C y lo traduce a una señal enviada al proceso adjunto (que es el shell) stty intr ^B
indicaría al conductor de la terminal que intercepte un ^B en su lugar. También es el controlador de la terminal el que hace eco de ^C en la terminal.
El shell es solo un proceso que se encuentra en el otro extremo de la línea, y recibe su entrada estándar de su terminal a través del controlador de la terminal (como /dev/ttyX), y su salida estándar (y stderr) también están adjuntos al mismo tty .
Tenga en cuenta que (si el eco está habilitado) el terminal envía las pulsaciones de teclas a ambos el proceso (grupo) y de vuelta a la terminal. El comando stty es simplemente un envoltorio alrededor de las ioctl() para el controlador tty para los procesos que "controlan" tty.
ACTUALIZACIÓN:para demostrar que el shell no está involucrado, creé el siguiente pequeño programa. Debe ser ejecutado por su shell principal a través de exec ./a.out
(Parece que un shell interactivo bifurcará un shell secundario, de todos modos) El programa establece la clave que genera SIGINTR en ^B, apaga el eco y luego espera la entrada de stdin.
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
int thesignum = 0;
void handler(int signum);
void handler(int signum)
{ thesignum = signum;}
#define THE_KEY 2 /* ^B */
int main(void)
{
int rc;
struct termios mytermios;
rc = tcgetattr(0 , &mytermios);
printf("tcgetattr=%d\n", rc );
mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */
mytermios.c_lflag &= ~ECHO ; /* Dont echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
printf("Setting handler()\n" );
signal(SIGINT, handler);
printf("entering pause()\n... type something followed by ^%c\n", '@'+THE_KEY );
rc = pause();
printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum );
// mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */
mytermios.c_lflag |= ECHO ; /* Do echo */
rc = tcsetattr(0 , TCSANOW, &mytermios);
printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc );
return 0;
}
intr.sh:
#!/bin/sh
echo $$
exec ./a.out
echo I am back.
El shell repite todo lo que escribes, así que cuando escribes ^C
, eso también se repite (y en su caso es interceptado por su controlador de señal). El comando stty -echo
puede o no serle útil dependiendo de sus necesidades/restricciones, consulte la página del manual de stty para obtener más información.
Por supuesto, sucede mucho más en un nivel inferior, en cualquier momento se comunica con un sistema a través de controladores de dispositivos periféricos (como el controlador de teclado que utiliza para generar la señal ^C y el controlador de terminal que muestra todo). Puede profundizar aún más en el nivel de lenguaje ensamblador/máquina, registros, tablas de búsqueda, etc. Si desea un nivel de comprensión más detallado y profundo, los libros a continuación son un buen lugar para comenzar:
El diseño del sistema operativo Unix es una buena referencia para este tipo de cosas. Dos referencias más clásicas:Entorno de Programación Unix y Programación Avanzada en el Entorno UNIX
Buen resumen aquí en esta pregunta SO ¿Cómo Ctrl-C finaliza un proceso secundario?
"cuando está ejecutando un programa, por ejemplo find
, la concha:
- la propia bifurcación de la concha
- y para el niño establezca el manejo de señal predeterminado
- reemplace al niño con el comando dado (por ejemplo, con buscar)
- cuando presiona CTRL-C, el shell principal maneja esta señal, pero el hijo la recibirá, con la acción predeterminada, terminar. (el niño también puede implementar el manejo de señales)"