2012-05-26 10 views
7

Ich habe Signale in Linux studiert. Und ich habe ein Testprogramm gemacht, um SIGINT zu erfassen.Warum gibt Linux beim Drücken von Strg + C immer "^ C" aus?

#include <unistd.h> 
#include <signal.h> 
#include <iostream> 
void signal_handler(int signal_no); 
int main() { 
    signal(SIGINT, signal_handler); 
    for (int i = 0; i < 10; ++i) { 
    std::cout << "I'm sleeping..." << std::endl; 
    unsigned int one_ms = 1000; 
    usleep(200* one_ms); 
    } 
    return 0; 
} 
void signal_handler(int signal_no) { 
    if (signal_no == SIGINT) 
    std::cout << "Oops, you pressed Ctrl+C!\n"; 
    return; 
} 

Während die Ausgabe wie folgt aussieht:

I'm sleeping... 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
^COops, you pressed Ctrl+C! 
I'm sleeping... 
I'm sleeping... 
I'm sleeping... 

Ich verstehe, dass, wenn Sie Strg + C drücken, werden alle Prozesse im Vordergrund Prozessgruppe eine SIGINT erhält (wenn kein Prozess, es zu ignorieren wählt).

Also ist es, dass die Shell (bash) UND die Instanz des oben genannten Programms beide das Signal erhalten? Woher kommt das "^ C" vor jedem "Ups"?

Das Betriebssystem ist CentOS, und die Shell ist bash.

Antwort

9

Es ist das Terminal (Fahrer) (das Kind kann Signalverarbeitung implementieren), das den^C abfängt und übersetzt es in ein Signal auf den beigefügten Prozess gesendet (das ist die Shell) stty intr ^B würde die Endgerät anweisen, Treiber, um stattdessen ein^B abzufangen.Es ist auch der Terminal-Treiber, der die^C zurück zum Terminal echts.

Die Shell ist nur ein Prozess, der am anderen Ende der Zeile sitzt, und erhält seine Standard von Ihrem Terminal über den Terminal-Treiber (wie/dev/ttyX), und es ist stdout (und stderr) sind auch an den gleichen tty.

Beachten Sie, dass (wenn Echo ist en abgängig) sendet das Terminal die Tastenanschläge an den Prozess (Gruppe) und zurück zum Terminal. Der Befehl stty ist nur ein Wrapper um die ioctl() s für den tty-Treiber für die Prozesse, die tty steuern.

UPDATE: Um zu zeigen, dass die Shell nicht beteiligt ist, habe ich das folgende kleine Programm erstellt. Es sollte von seiner Eltern-Shell über exec ./a.out ausgeführt werden (es scheint, dass eine interaktive Shell eine Tochter-Shell abzweigt). Das Programm setzt den Schlüssel, der das SIGINTR auf^B generiert, schaltet das Echo ab und wartet 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. 
+0

Lustige Sache ist, dass die andere Antwort (die einfach falsch ist) immer noch upvotes, nur weil es Links zu den richtigen Büchern enthält. – wildplasser

+1

@WiSaGaN Wie kann die gleiche Antwort "fast richtig" (Kommentar zu mir) und "einfach falsch" (Kommentar zu OP) sein. ** Natürlich ** Gerätetreiber sind beteiligt bei der Kommunikation mit einem System über Peripheriegeräte (Wie kommt es, dass Sie den Tastaturtreiber nicht erwähnt haben?) Die Angabe von mehr Details macht eine "fast richtige" Antwort nicht "einfach falsch". Wenn ich auf die Ebene der Maschinensprache und der Nachschlagetabellen gehe, würde das Ihre Antwort falsch machen? Kaum. Ich stellte Links zu Büchern zur Verfügung, damit OP bei Bedarf mehr Details nachschlagen konnte. OP fühlte, dass meine Antwort hilfreich war. Ihr aktueller Kommentar ist bestenfalls widersprüchlich und unaufrichtig. – Levon

+0

Das Falsche ist, dass * die Shell nicht beteiligt ist *. Die Konvertierung von^C nach INTR erfolgt im Terminaltreiber. Die Shell ist nur ein Rezeptor (wie bei jedem anderen Prozess). Die Shell zielt nicht auf die Shell, sondern auf die Tastatur. – wildplasser

6

Die Shell gibt alle von Ihnen eingegebenen Zeichen zurück. Wenn Sie also ^C eingeben, wird auch das Echo ausgegeben (und in Ihrem Fall von Ihrem Signalhandler abgefangen). Der Befehl stty -echo kann für Sie nützlich sein oder auch nicht, abhängig von Ihren Anforderungen/Einschränkungen. Weitere Informationen finden Sie unter man page for stty.

Natürlich geht viel mehr auf einer niedrigeren Ebene, jederzeit Sie kommunizieren mit einem System über Peripheriegerätetreiber (wie der Tastaturtreiber, den Sie verwenden, um das^C-Signal zu generieren, und den Terminaltreiber, der alles anzeigt) beteiligt sein. Sie können noch tiefer graben auf der Ebene der Montage/Maschinensprache, Register, Nachschlagetabellen usw. Wenn Sie eine detailliertere, tiefgründige Ebene des Verständnisses der Bücher unten wollen, sind ein guter Ort zum Starten:

The Design of the Unix OS ist ein gute Referenz für diese Art von Dingen. Zwei weitere klassische Referenzen: Unix Programming Environment und Advanced Programming in the UNIX Environment

Nizza Zusammenfassung hier in diesem SO How does Ctrl-C terminate a child process?

„in Frage stellen, wenn youre ein Programm ausführen, zum Beispiel find, die Schale:

  • die Schale Gabel selbst
  • und für das Kind die Standard - Signalbehandlung
  • das Kind mit dem gegebenen Befehl ersetzen (zB mit fin d)
  • Wenn Sie STRG-C drücken, wird dieses Signal von der Parent Shell gehandhabt, aber das Kind empfängt es - mit der Standardaktion - wird beendet. „
+0

Meinen Sie, dass bash hier im Vordergrund Prozessgruppe als auch ist, so ist es das Signal vom Kernel gesendet empfangen? – WiSaGaN

+1

@WiSaGaN Ich meine, dass Sie die Tastatur verwenden, um mit Ihrem Programm zu kommunizieren, und die Shell zwischen Ihnen und dem Programm (d. H. Der * Schnittstelle *) liegt, so dass sie diese Aktion abfängt. Was Ihr Signal-Handler tut, bestimmt die * nachfolgende * Aktion, die auftritt, nachdem das^C erkannt wurde. Anstatt also das Programm zu beenden, können Sie etwas anderes tun, z. B. eine Nachricht in Ihrem Fall ausdrucken. – Levon

+0

Also ist es tatsächlich die Shell, die das Signal an die Vordergrundprozessgruppe sendet (der Kernel implementiert es tatsächlich). Und so sind die zugrunde liegenden Ereignisse, die Tastatur gefangen Ctrl + C gedrückt und signalisiert der CPU. Dann hat ein Hardware-Interrupt den Interrupt-Handler aufgerufen, der die Shell meldet, die Strg + C gedrückt hat. Dann wird ein Signal von der Shell gesendet. Also wird die Umwandlung von "Strg + C" zu "SIGINT Signal" von der Shell durchgeführt. Hab ich recht? – WiSaGaN