Es ist das Terminal (Treiber), das das ^C abfängt und es in ein Signal übersetzt, das an den angehängten Prozess (das ist die Shell) stty intr ^B
gesendet wird würde den Terminaltreiber anweisen, stattdessen ein ^B abzufangen. Es ist auch der Terminaltreiber, der das ^C an das Terminal zurückgibt.
Die Shell ist nur ein Prozess, der sich am anderen Ende der Leitung befindet und seine stdin von Ihrem Terminal über den Terminaltreiber (z. B. /dev/ttyX) erhält, und seine stdout (und stderr) sind ebenfalls an dasselbe tty angehängt .
Beachten Sie, dass (wenn das Echo aktiviert ist) das Terminal die Tastenanschläge an beide sendet Prozess (Gruppe) und zurück zum Terminal. Der stty-Befehl ist nur ein Wrapper um die ioctl()s für den tty-Treiber für die Prozesse, die tty "steuern".
UPDATE:Um zu demonstrieren, dass die Shell nicht beteiligt ist, habe ich das folgende kleine Programm erstellt. Es sollte von seiner übergeordneten Shell über exec ./a.out
ausgeführt werden (Es sieht so aus, als würde eine interaktive Shell sowieso eine Tochter-Shell forken.) Das Programm setzt den Schlüssel, der das SIGINTR erzeugt, auf ^B, schaltet das Echo aus und wartet dann auf die Eingabe von 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.
Die Shell wiederholt alles, was Sie eingeben, also wenn Sie ^C
eingeben , auch das wird wiederholt (und in Ihrem Fall von Ihrem Signalhandler abgefangen). Der Befehl stty -echo
abhängig von Ihren Bedürfnissen/Einschränkungen für Sie nützlich sein kann oder auch nicht, finden Sie weitere Informationen auf der Manpage für stty.
Natürlich passiert viel mehr auf einer niedrigeren Ebene, jederzeit Sie kommunizieren mit einem System über Peripheriegerätetreiber (z. B. den Tastaturtreiber, mit dem Sie das ^C-Signal erzeugen, und den Terminaltreiber, der alles anzeigt). Sie können noch tiefer in die Ebene der Assembler-/Maschinensprache, Register, Nachschlagetabellen usw. eintauchen. Wenn Sie ein detaillierteres, tiefgreifenderes Verständnis wünschen, sind die folgenden Bücher ein guter Ausgangspunkt:
Das Design des Unix-Betriebssystems ist eine gute Referenz für solche Dinge. Zwei weitere klassische Referenzen:Unix Programming Environment und Advanced Programming in the UNIX Environment
Schöne Zusammenfassung hier in dieser SO-Frage Wie beendet Strg-C einen untergeordneten Prozess?
"wenn Sie ein Programm ausführen, zum Beispiel find
, die Schale:
- der Shell-Fork selbst
- und für das Kind die Standard-Signalbehandlung festlegen
- ersetze das untergeordnete Element durch den angegebenen Befehl (z. B. durch find)
- Wenn Sie STRG-C drücken, verarbeitet die übergeordnete Shell dieses Signal, aber die untergeordnete Shell erhält es - mit der Standardaktion - Terminierung. (das Kind kann auch die Signalverarbeitung implementieren)"