Ich habe mein Bestes versucht, dies selbst herauszufinden, aber ich möchte wirklich nicht weiter an Dingen herumspielen, die ich nicht vollständig verstehe. Für eine Programmieraufgabe, die ich in C ausführen muss, muss ich ein Programm beenden, wenn der Benutzer die Tastenkombination STRG + D über ein Terminal eingibt. Ich habe versucht, diese Funktionalität in einer kleineren Testfunktion zu isolieren, aber jetzt verhält sich meine STRG + D wie meine STRG + C kbd> und STRG + C hat auch außerhalb des Programms keine Auswirkungen, wenn die Ausführung abgeschlossen ist. Dies ist das Programm, das diese Änderung verursacht hat:
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
printf("\n");
signal(SIGINT, SIG_DFL);
exit(0);
}
int main(){
signal(SIGINT, ctrlD);
while(1) {
printf("Hello\n");
sleep(5);
}
}
Die Zeile signal(SIGINT, SIG_DFL);
wurde später hinzugefügt, als festgestellt wurde, dass meine STRG + C nicht mehr funktionierte. Ich dachte, es würde die Tastenanschläge auf ihre ursprünglichen Funktionen zurücksetzen, aber ohne Erfolg. Was kann ich tun, um die ursprünglichen Funktionen wiederherzustellen und gleichzeitig dafür zu sorgen, dass dieses Programm mit STRG + D funktioniert?
*** EDIT: Diese Frage scheint ein wenig von den Schienen geraten zu sein. Ich verstehe jetzt, dass Strg + D kein Signal ist. Trotzdem habe ich nicht mehr die Funktionalität von Strg + C , wenn ich versuche, es in meinem MAC OS-Terminal zu verwenden, und stattdessen Strg + < kbd> D scheint genau diese Funktionalität zu haben. WIE genau kann ich jeden zurückgeben, um die Funktionalität zu haben, die er hatte, bevor ich mich auf diese zufällige Reise begab?
4 Antworten
Vielen Dank an alle, die zu dieser Frage beigetragen haben. Die bereitgestellten / verknüpften Ressourcen waren enorm hilfreich, um mehr über Signale zu erfahren (und dass EOF kein Signal ist), neben der anderen Fülle an bereitgestellten Informationen.
Nach weiteren Recherchen stellte ich fest, dass ich entweder durch einen fehlgeschlagenen versehentlichen Bash-Befehl oder durch das in meiner ursprünglichen Frage selbst veröffentlichte Programm die Tastenzuordnungen für die Stty-Einstellungen meines Terminals geändert hatte. Wenn sich in Zukunft jemand in dieser seltsam spezifischen Situation befindet, hoffe ich, dass dies hilfreich sein kann, da dies mein Problem behoben hat:
Geben Sie den Befehl $ stty -a
ein, um alle Einstellungen Ihrer Terminals anzuzeigen, insbesondere den Abschnitt "cchars".
Ich sah dann die Umkehrung und reparierte sie so:
$ stty intr ^C
$ stty eof ^D
Anschließend können Sie $ stty -a
erneut ausführen, um festzustellen, ob die Änderungen ordnungsgemäß wirksam wurden. Nochmals vielen Dank an alle.
Wenn Sie beabsichtigen, das Standardverhalten des Signals nach dem Ausführen des Handlers wiederherzustellen, übergeben Sie das SA_RESETHAND-Flag an sa_flags, während Sie die Signalaktion registrieren. Zum Beispiel.
struct sigaction act;
memset(&act, 0, sizeof(struct sigaction));
act.sa_flags = SA_RESETHAND;
act.sa_handler = some_handler;
sigaction(SIGINT, &act, NULL);
Von sigaction () man
SA_RESETHAND
Stellen Sie die Signalaktion beim Aufrufen des Signalhandlers auf den Standardwert zurück. Dieses Flag ist nur beim Einrichten eines Signalhandlers von Bedeutung.
Wenn Sie ein Programm schreiben, um Signale zu untersuchen, ist es viel besser, es sorgfältig unter Verwendung geeigneter POSIX-Schnittstellen (sigaction()
anstelle von signal()
) und Vermeidung von undefiniertem Verhalten (unter Verwendung von non- sichere Funktionen für asynchrone Signale in einem Signalhandler).
Betrachten Sie zum Beispiel das folgende Programm:
#define _POSIX_C_SOURCE 200809L
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include <stdio.h>
#include <time.h>
#include <errno.h>
static volatile sig_atomic_t sigint_count = 0;
static void catch_sigint(int signum)
{
if (signum == SIGINT)
sigint_count++;
}
static int install_sigint(void)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = catch_sigint;
act.sa_flags = 0;
if (sigaction(SIGINT, &act, NULL) == -1)
return errno;
return 0;
}
static int install_default(const int signum)
{
struct sigaction act;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = SIG_DFL;
act.sa_flags = 0;
if (sigaction(signum, &act, NULL) == -1)
return errno;
return 0;
}
int main(void)
{
struct timespec duration;
int result;
if (install_sigint()) {
fprintf(stderr, "Cannot install SIGINT handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
duration.tv_sec = 5;
duration.tv_nsec = 0; /* 1/1000000000ths of a second. Nine zeroes. */
printf("Sleeping for %d seconds.\n", (int)duration.tv_sec);
fflush(stdout);
while (1) {
result = nanosleep(&duration, &duration);
if (!result)
break;
if (errno != EINTR) {
fprintf(stderr, "nanosleep() failed: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
/* nanosleep was interrupted by a delivery of a signal. */
if (sigint_count >= 3) {
/* Ctrl+C pressed three or more times. */
if (install_default(SIGINT) == -1) {
fprintf(stderr, "Cannot revert SIGINT to the default handler: %s.\n", strerror(errno));
return EXIT_FAILURE;
}
printf("SIGINT has been reverted to the default handler.\n");
fflush(stderr);
}
}
if (sigint_count > 0)
printf("You pressed Ctrl+C %d time%s.\n", (int)sigint_count, (sigint_count > 1) ? "s" : "");
else
printf("You did not press Ctrl+C at all.\n");
return EXIT_SUCCESS;
}
Das #define
teilt Ihrer C-Bibliothek (insbesondere glibc) mit, dass Sie POSIX.1-2008-Funktionen (und höher) von ihr erhalten möchten.
Der INT
Signalhandler erhöht nur einen volatile sig_atomic_t
Zähler. Beachten Sie, dass dieser Typ möglicherweise einen sehr kleinen Bereich hat, den er darstellen kann. 0 bis einschließlich 127 sollten sicher sein.
Das Hauptprogramm wartet mit POSIX nanosleep()
Funktion. Auf einigen Systemen kann sleep()
über die Funktion SIGALRM
implementiert werden, sodass es besser vermieden wird, wenn Signale anderweitig verwendet werden. nanosleep()
stört solche Signale überhaupt nicht. Außerdem kann nanosleep()
die verbleibende Zeit zurückgeben, wenn sie durch eine Signalübertragung unterbrochen wird.
In der Hauptschleife gibt nanosleep()
0 zurück, wenn das gesamte Intervall geschlafen hat (beachten Sie jedoch, dass die verbleibende Zeit in diesem Fall möglicherweise nicht auf 0 aktualisiert wird). Wenn es durch die Übermittlung eines Signals unterbrochen wird, gibt es -1
mit errno == EINTR
zurück und die verbleibende Zeit wird aktualisiert. (Der erste Zeiger bezieht sich auf die Dauer des Schlafes und der zweite darauf, wo die verbleibende Zeit gespeichert werden soll. Sie können für beide dieselbe Struktur verwenden.)
Normalerweise führt die Hauptschleife nur eine Iteration durch. Es kann mehr als eine Iteration ausführen, wenn es durch die Übermittlung eines Signals unterbrochen wird.
Wenn die Hauptschleife erkennt, dass sigint_count
mindestens drei ist, d. H. Mindestens drei INT
Signale empfangen hat, setzt sie den Signalhandler auf die Standardeinstellungen zurück.
(Beachten Sie, dass sowohl memset()
als auch sigemptyset()
wichtig sind, wenn Sie die struct sigaction
-Struktur löschen. Das memset()
stellt sicher, dass zukünftiger Code abwärtskompatibel mit älterem Code ist, indem ein gleichmäßiges Auffüllen sichergestellt wird Felder werden gelöscht. Und sigemptyset()
ist der sichere Weg, um die Signalmaske zu löschen (Satz von Signalen, die blockiert werden, während der Handler ausgeführt wird).)
(Theoretisch ist memset()
nicht asynchron für Signale geeignet, während dies sowohl sigemptyset()
als auch sigaction()
sind. Deshalb habe ich den Signalhandler im Hauptprogramm und nicht im Signal zurückgesetzt Handler.)
Wenn Sie von einem Signalhandler aus drucken möchten, müssen Sie Low-Level-E / A verwenden, da <stdio.h>
-Funktionen nicht asynchron signalensicher sind. Mit der folgenden Funktion können Sie beispielsweise Zeichenfolgen in die Standardausgabe drucken:
static int wrerr(const char *p)
{
const int saved_errno = errno;
int retval = 0;
if (p) {
const char *q = p;
ssize_t n;
while (*q)
q++;
while (p < q) {
n = write(STDERR_FILENO, p, (size_t)(q - p));
if (n > 0)
p += n;
else
if (n != -1) {
retval = EIO;
break;
} else
if (errno != EINTR) {
retval = errno;
break;
}
}
}
errno = saved_errno;
return retval;
}
Die obige wrerr()
-Funktion ist async-signal-sicher (da sie selbst nur async-signal-sichere Funktionen verwendet) und behält sogar errno
unverändert bei. (Viele Anleitungen vergessen zu erwähnen, dass es für einen Signalhandler sehr wichtig ist, errno
unverändert zu lassen. Andernfalls, wenn eine Funktion von einem Signalhandler unterbrochen wird und dieser Signalhandler errno
ändert, wird die ursprüngliche Funktion geändert gibt -1
zurück, um einen Fehler anzuzeigen, aber dann ist errno
nicht mehr EINTR
!)
Sie können einfach wrerr("INT signal!\n")
verwenden, wenn Sie möchten. Der Rückgabewert von wrerr()
ist Null, wenn der Schreibvorgang erfolgreich war, andernfalls ein Fehlercode. Es ignoriert Interrupts selbst.
Beachten Sie, dass Sie die stderr
Ausgabe nicht über fprintf()
oder andere <stdio.h>
Funktionen mit den oben genannten Funktionen mischen sollten (außer möglicherweise zum Drucken von Fehlermeldungen, wenn das Programm abgebrochen wird). Das Mischen ist kein undefiniertes Verhalten, sondern kann nur zu überraschenden Ergebnissen führen, z. B. zur Ausgabe von wrerr()
inmitten einer Ausgabe von fprintf(stderr,...)
.
Es liegt an der Anweisung exit(0)
im Handler, wenn SIGINT
ausgelöst wird, wird der Handler strlD
aufgerufen und Sie denken vielleicht, warum signal(SIGINT,SIG_DFL)
nicht funktioniert hat? Eigentlich funktioniert es. Ihr Hauptprozess a.out
wird dort jedoch selbst erfolgreich beendet, indem Sie exit(0)
aufrufen. Entfernen Sie exit(0)
, wenn Sie das Verhalten von SIGINT
wiederherstellen möchten.
#include <unistd.h>
#include <stdio.h>
#include <termios.h>
#include <signal.h>
#include <stdlib.h>
void ctrlD(int sig){
//printf("CTRL+C pressed\n");/* just to observe I added one printf
statement, Ideally there shouldn't be any printf here */
signal(SIGINT, SIG_DFL);/*restoring back to original action */
}
int main(){
signal(SIGINT, ctrlD);/*1st time when CTRL+C pressed, handler ctrlD gets called */
while(1) {
printf("Hello\n");
sleep(5);
}
return 0;
}
Es ist auch ratsam, sigaction()
anstelle von signal()
zu verwenden, wie hier angegeben. Was ist der Unterschied zwischen Sigaction und Signal?. Lesen Sie man 2 sigaction
und man 2 exit
, um zu überprüfen, was exit(0)
bedeutet.
Auch dies Wie vermeide ich die Verwendung von printf in einem Signalhandler? a>
Bearbeiten :
void ctrlD(int sig){
/* printf("CTRL+C pressed \n"); */
signal(SIGINT, SIG_DFL); /* only one time CTRL+C works
after that SIG_DFL will terminate whole process */
}
int main(){
signal(SIGINT, ctrlD); /* if you press CTRL+C then it will go to handler
and terminate */
int ch;
while( ((ch = getchar())!=EOF) ) { /* wait or read char until CTrl+D is not pressed */
printf("Hello : %d \n",ch);/* ASCII equivalent of char */
}
return 0;
}