È il terminale (driver) che intercetta il ^C e lo traduce in un segnale inviato al processo collegato (che è la shell) stty intr ^B
indicherebbe invece al driver del terminale di intercettare un ^B. È anche il driver del terminale che rimanda ^C al terminale.
La shell è solo un processo che si trova dall'altra parte della linea e riceve il suo stdin dal tuo terminale tramite il driver del terminale (come /dev/ttyX), e anche il suo stdout (e stderr) sono collegati allo stesso tty .
Si noti che (se l'eco è abilitato) il terminale invia le sequenze di tasti a entrambi il processo (gruppo) e ritorno al terminale. Il comando stty è solo un wrapper attorno a ioctl() per il driver tty per i processi che "controllano" tty.
AGGIORNAMENTO:per dimostrare che la shell non è coinvolta, ho creato il seguente programmino. Dovrebbe essere eseguito dalla sua shell madre tramite exec ./a.out
(sembra che una shell interattiva eseguirà comunque il fork di una shell figlia) Il programma imposta la chiave che genera SIGINTR su ^B, disattiva l'eco e quindi attende l'input da 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.
La shell fa eco a tutto ciò che digiti, quindi quando digiti ^C
, anche questo viene echeggiato (e nel tuo caso intercettato dal tuo gestore di segnale). Il comando stty -echo
può o non può esserti utile a seconda delle tue esigenze/vincoli, vedi la pagina man di stty per maggiori informazioni.
Ovviamente molto di più accade a un livello inferiore, in qualsiasi momento comunichi con un sistema tramite i driver dei dispositivi periferici (come il driver della tastiera che usi per generare il segnale ^C e il driver del terminale che visualizza tutto) sono coinvolti. Puoi scavare ancora più a fondo a livello di linguaggio assembly/macchina, registri, tabelle di ricerca, ecc. Se desideri un livello di comprensione più dettagliato e approfondito, i libri seguenti sono un buon punto di partenza:
Il design del sistema operativo Unix è un buon riferimento per questo genere di cose. Altri due riferimenti classici:Unix Programming Environment e Advanced Programming in the UNIX Environment
Bel riassunto qui in questa domanda SO In che modo Ctrl-C termina un processo figlio?
"quando esegui un programma, ad esempio find
, la shell:
- lo stesso fork della shell
- e per il bambino imposta la gestione del segnale predefinita
- sostituisci il figlio con il comando dato (ad es. con find)
- quando premi CTRL-C, la shell genitore gestisce questo segnale ma il figlio lo riceverà - con l'azione predefinita - termina. (anche il bambino può implementare la gestione del segnale)"