C-Programmierung

Wie man Signalhandler in der Sprache C verwendet?

Wie man Signalhandler in der Sprache C verwendet?
In diesem Artikel zeigen wir Ihnen, wie Sie Signalhandler in Linux mit der Sprache C verwenden. Aber zuerst werden wir besprechen, was Signal ist, wie es einige allgemeine Signale erzeugt, die Sie in Ihrem Programm verwenden können, und dann werden wir sehen, wie verschiedene Signale von einem Programm verarbeitet werden können, während das Programm ausgeführt wird. So lass uns anfangen.

Signal

Ein Signal ist ein Ereignis, das generiert wird, um einem Prozess oder Thread mitzuteilen, dass eine wichtige Situation eingetreten ist. Wenn ein Prozess oder Thread ein Signal empfangen hat, stoppt der Prozess oder Thread seine Tätigkeit und ergreift eine Aktion. Signal kann für die Kommunikation zwischen Prozessen nützlich sein.

Standardsignale

Die Signale sind in der Headerdatei definiert defined Signal.ha als Makrokonstante. Der Signalname hat mit einem „SIG“ begonnen, gefolgt von einer kurzen Beschreibung des Signals. Jedes Signal hat also einen eindeutigen numerischen Wert. Ihr Programm sollte immer den Namen der Signale verwenden, nicht die Signalnummer. Der Grund dafür ist, dass die Signalnummer je nach System unterschiedlich sein kann, aber die Bedeutung der Namen ist Standard.

Das Makro NSIG ist die Gesamtzahl der definierten Signale. Der Wert von NSIG ist um eins größer als die Gesamtzahl der definierten Signale (Alle Signalnummern werden fortlaufend vergeben).

Nachfolgend die Standardsignale:

Signalname Beschreibung
SEUFZEND Beenden Sie den Vorgang. Das Signal SIGHUP wird verwendet, um die Trennung des Endgeräts des Benutzers zu melden, möglicherweise weil eine Remote-Verbindung unterbrochen wurde oder aufhängt.
UNTERSCHRIFT Unterbrechen Sie den Vorgang. Wenn der Benutzer das INTR-Zeichen eingibt (normalerweise Strg + C), wird das SIGINT-Signal gesendet.
SIGQUIT Beenden Sie den Prozess. Wenn der Benutzer das QUIT-Zeichen eingibt (normalerweise Strg + \), wird das SIGQUIT-Signal gesendet.
Siegel Illegaler Unterricht. Wenn versucht wird, einen Garbage- oder privilegierten Befehl auszuführen, wird das SIGILL-Signal generiert. Außerdem kann SIGILL generiert werden, wenn der Stack überläuft oder das System Probleme beim Ausführen eines Signalhandlers hat.
SIGTRAP Spurenfalle. Ein Breakpoint-Befehl und ein anderer Trap-Befehl erzeugen das SIGTRAP-Signal. Der Debugger verwendet dieses Signal.
SIGABRT Abbrechen. Das SIGABRT-Signal wird generiert, wenn die Funktion abort() aufgerufen wird. Dieses Signal weist auf einen Fehler hin, der vom Programm selbst erkannt und vom Funktionsaufruf abort() gemeldet wird.
SIGFPE Gleitkomma-Ausnahme. Bei einem schwerwiegenden Rechenfehler wird das SIGFPE-Signal generiert.
SIGUSR1 und SIGUSR2 Die Signale SIGUSR1 und SIGUSR2 können beliebig verwendet werden. Für eine einfache Interprozesskommunikation ist es sinnvoll, für sie im Programm einen Signalhandler zu schreiben, der das Signal empfängt.

Standardaktion von Signalen

Jedes Signal hat eine Standardaktion, eine der folgenden:

Begriff: Der Prozess wird beendet.
Ader: Der Prozess wird beendet und eine Core-Dump-Datei erstellt.
Zündung: Der Prozess ignoriert das Signal.
Halt: Der Prozess wird gestoppt.
Fortsetzung: Der Prozess wird fortgesetzt, nachdem er gestoppt wurde.

Die Standardaktion kann mit der Handler-Funktion geändert werden. Die Standardaktion einiger Signale kann nicht geändert werden. SIGKILL und SIGABRT Die Standardaktion des Signals kann nicht geändert oder ignoriert werden.

Signalverarbeitung

Wenn ein Prozess ein Signal empfängt, hat der Prozess eine Aktionsauswahl für diese Art von Signal. Der Prozess kann das Signal ignorieren, eine Handler-Funktion angeben oder die Standardaktion für diese Art von Signal akzeptieren.

Wir können mit dem Signal umgehen mit Signal oder sigaction Funktion. Hier sehen wir, wie am einfachsten Signal() Funktion wird zur Behandlung von Signalen verwendet.

int signal () (int signum, void (*func)(int))

Das Signal() werde die anrufen func Funktion, wenn der Prozess ein Signal empfängt signum. Das Signal() gibt einen Zeiger auf die Funktion zurück func wenn erfolgreich oder es gibt einen Fehler an errno und -1 andernfalls zurück.

Das func Zeiger kann drei Werte haben:

  1. SIG_DFL: Es ist ein Zeiger auf die Standardfunktion des Systems SIG_DFL(), deklariert in ha Header-Datei. Es wird verwendet, um die Standardaktion des Signals auszuführen.
  2. SIG_IGN: Es ist ein Zeiger auf die Funktion zum Ignorieren des Systems SIG_IGN(),deklariert in ha Header-Datei.
  3. Benutzerdefinierter Handler-Funktionszeiger: Der benutzerdefinierte Handler-Funktionstyp ist void(*)(int), bedeutet Rückgabetyp ist void und ein Argument vom Typ int.

Beispiel für einen einfachen Signalhandler

#einschließen
#einschließen
#einschließen
void sig_handler(int signum)
//Der Rückgabetyp der Handlerfunktion sollte void sein
printf("\nInnere Handler-Funktion\n");

int main()
signal(SIGINT,sig_handler); // Signalhandler registrieren
for(int i=1;;i++)    //Unendliche Schleife
printf("%d : Innerhalb der Hauptfunktion\n",i);
Schlaf (1); // Verzögerung für 1 Sekunde

0 zurückgeben;

Im Screenshot der Ausgabe von Beispiel1.c, wir können sehen, dass in der Hauptfunktion die Endlosschleife ausgeführt wird. Wenn der Benutzer Strg+C eingibt, stoppt die Ausführung der Hauptfunktion und die Handlerfunktion des Signals wird aufgerufen. Nach Abschluss der Handlerfunktion wird die Ausführung der Hauptfunktion wieder aufgenommen. Wenn der Benutzer Strg+\ eintippt, wird der Vorgang beendet.

Beispiel für Ignorieren von Signalen

#einschließen
#einschließen
#einschließen
int main()
signal(SIGINT,SIG_IGN); // Signalhandler zum Ignorieren des Signals registrieren
for(int i=1;;i++)    //Unendliche Schleife
printf("%d : Innerhalb der Hauptfunktion\n",i);
Schlaf (1); // Verzögerung für 1 Sekunde

0 zurückgeben;

Hier ist die Handlerfunktion registrieren für SIG_IGN() Funktion zum Ignorieren der Signalaktion. Wenn der Benutzer also Strg+C . eintippt,  UNTERSCHRIFT Signal wird generiert, aber die Aktion wird ignoriert.

Beispiel für einen Signalhandler zur Neuregistrierung

#einschließen
#einschließen
#einschließen
void sig_handler(int signum)
printf("\nInnere Handler-Funktion\n");
signal(SIGINT,SIG_DFL); // Signalhandler für Standardaktion neu registrieren

int main()
signal(SIGINT,sig_handler); // Signalhandler registrieren
for(int i=1;;i++)    //Unendliche Schleife
printf("%d : Innerhalb der Hauptfunktion\n",i);
Schlaf (1); // Verzögerung für 1 Sekunde

0 zurückgeben;

Im Screenshot der Ausgabe von Beispiel3.c können wir sehen, dass die Handler-Funktion aufgerufen wurde, wenn der Benutzer zum ersten Mal Strg+C eintippte. In der Handler-Funktion registrieren sich die Signal-Handler neu bei SIG_DFL für Standardaktion des Signals. Wenn der Benutzer zum zweiten Mal Strg+C eingibt, wird der Prozess beendet, was die Standardaktion von . ist UNTERSCHRIFT Signal.

Senden von Signalen:

Ein Prozess kann auch explizit Signale an sich selbst oder an einen anderen Prozess senden. Die Funktionen raise() und kill() können zum Senden von Signalen verwendet werden. Beide Funktionen sind in signal . deklariert.h-Header-Datei.

int erhöhen (int signum)

Die Funktion raise() zum Senden des Signals signum zum aufrufenden Prozess (selbst). Es gibt Null zurück, wenn es erfolgreich ist, und einen Wert ungleich Null, wenn es fehlschlägt.

int kill(pid_t pid, int signum)

Die Kill-Funktion zum Senden eines Signals signum zu einem Prozess oder einer Prozessgruppe, spezifiziert durch pid.

SIGUSR1 Signalhandler-Beispiel

#einschließen
#einschließen
void sig_handler(int signum)
printf("Innere Handler-Funktion\n");

int main()
signal(SIGUSR1,sig_handler); // Signalhandler registrieren
printf("In der Hauptfunktion\n");
erhöhen(SIGUSR1);
printf("In der Hauptfunktion\n");
0 zurückgeben;

Hier sendet der Prozess mit der Funktion raise() das SIGUSR1-Signal an sich selbst.

Erhöhen mit Kill-Beispielprogramm

#einschließen
#einschließen
#einschließen
void sig_handler(int signum)
printf("Innere Handler-Funktion\n");

int main()
pid_t pid;
signal(SIGUSR1,sig_handler); // Signalhandler registrieren
printf("In der Hauptfunktion\n");
pid=getpid(); //Prozess-ID von sich selbst
kill(pid,SIGUSR1); // SIGUSR1 an sich selbst senden
printf("In der Hauptfunktion\n");
0 zurückgeben;

Hier wird der Prozess gesendet SIGUSR1 Signal an sich selbst mit töten() Funktion. getpid() wird verwendet, um die Prozess-ID von sich selbst zu erhalten.

Im nächsten Beispiel werden wir sehen, wie Parent- und Child-Prozesse kommunizieren (Inter Process Communication) mit töten() und Signalfunktion.

Eltern-Kind-Kommunikation mit Signalen

#einschließen
#einschließen
#einschließen
#einschließen
void sig_handler_parent(int signum)
printf("Elternteil: Antwortsignal von Kind erhalten \n");

void sig_handler_child(int signum)
printf("Kind : Ein Signal vom Elternteil empfangen \n");
Schlaf (1);
kill(getppid(),SIGUSR1);

int main()
pid_t pid;
if((pid=gabel())<0)
printf("Fork fehlgeschlagen\n");
Ausgang(1);

/* Kindprozess */
sonst if(pid==0)
signal(SIGUSR1,sig_handler_child); // Signalhandler registrieren
printf("Kind: wartet auf Signal\n");
Pause();

/* Elternprozess */
sonst
signal(SIGUSR1,sig_handler_parent); // Signalhandler registrieren
Schlaf (1);
printf("Elternteil: Signal an Kind senden\n");
kill(pid,SIGUSR1);
printf("Elternteil: auf Antwort warten\n");
Pause();

0 zurückgeben;

Hier, Gabel() Funktion erstellt untergeordneten Prozess und gibt null an untergeordneten Prozess und untergeordnete Prozess-ID an übergeordneten Prozess zurück. pid wurde also überprüft, um den übergeordneten und untergeordneten Prozess zu entscheiden. Im Elternprozess wird 1 Sekunde geschlafen, damit der Kindprozess die Signalhandlerfunktion registrieren und auf das Signal vom Elternprozess warten kann. Nach 1 Sekunde Elternprozess senden SIGUSR1 signalisieren Sie dem Kindprozess und warten Sie auf das Antwortsignal des Kindes. Im untergeordneten Prozess wartet er zuerst auf ein Signal vom übergeordneten Prozess und wenn ein Signal empfangen wird, wird die Handler-Funktion aufgerufen. Von der Handler-Funktion sendet der Kindprozess ein weiteres SIGUSR1 Signal an die Eltern. Hier getppid() Funktion wird verwendet, um die ID des übergeordneten Prozesses zu erhalten.

Fazit

Signal in Linux ist ein großes Thema. In diesem Artikel haben wir gesehen, wie man Signale von Grund auf behandelt und wie das Signal erzeugt wird, wie ein Prozess ein Signal an sich selbst und andere Prozesse senden kann, wie Signale für die Kommunikation zwischen Prozessen verwendet werden können.

So ändern Sie Mauszeiger- und Cursorgröße, Farbe und Schema unter Windows 10
Der Mauszeiger und der Cursor in Windows 10 sind sehr wichtige Aspekte des Betriebssystems. Dies kann auch für andere Betriebssysteme gesagt werden, a...
Kostenlose und Open-Source-Spiele-Engines für die Entwicklung von Linux-Spielen
Dieser Artikel behandelt eine Liste von kostenlosen und Open-Source-Spiele-Engines, die für die Entwicklung von 2D- und 3D-Spielen unter Linux verwend...
Shadow of the Tomb Raider für Linux Tutorial
Shadow of the Tomb Raider ist die zwölfte Erweiterung der Tomb Raider-Reihe – ein Action-Adventure-Franchise von Eidos Montrealdos. Das Spiel wurde vo...