GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Linux-Signale – Beispiel-C-Programm zum Abfangen von Signalen (SIGINT, SIGKILL, SIGSTOP usw.)

In Teil 1 der Reihe „Linux-Signale“ haben wir die grundlegenden Konzepte hinter Linux-Signalen kennengelernt.

Aufbauend auf dem vorherigen Teil lernen wir in diesem Artikel, wie man Signale in einem Prozess abfängt. Wir werden die praktische Seite des Signalhandlings anhand von C-Programmcodeschnipseln darstellen.

Ein Signal empfangen

Wie bereits im vorherigen Artikel besprochen, muss der Prozess, wenn ein Prozess bestimmte Signale verarbeiten möchte, im Code eine Signalverarbeitungsfunktion beim Kernel registrieren.

Das Folgende ist der Prototyp einer Signalverarbeitungsfunktion:

void <signal handler func name> (int sig)

Die Signal-Handler-Funktion hat den Rückgabetyp void und akzeptiert eine Signalnummer, die dem zu verarbeitenden Signal entspricht.

Um die Signal-Handler-Funktion im Kernel zu registrieren, wird der Signal-Handler-Funktionszeiger als zweites Argument an die „Signal“-Funktion übergeben. Der Prototyp der Signalfunktion ist :

void (*signal(int signo, void (*func )(int)))(int);

Dies scheint eine komplizierte Erklärung zu sein. Wenn wir versuchen, es zu entschlüsseln:

  • Die Funktion erfordert zwei Argumente.
  • Das erste Argument ist eine Ganzzahl (signo), die die Signalnummer oder den Signalwert darstellt.
  • Das zweite Argument ist ein Zeiger auf die Signal-Handler-Funktion, die eine Ganzzahl als Argument akzeptiert und nichts zurückgibt (void).
  • Während die 'Signal'-Funktion selbst einen Funktionszeiger zurückgibt, dessen Rückgabetyp ungültig ist.

Nun, um die Dinge einfacher zu machen, verwenden wir typedef :

typedef void sigfunc(int)

Hier haben wir also einen neuen Typ ‚sigfunc‘ erstellt. Verwenden Sie nun diese Typdefinition, wenn wir den Prototyp des Signalhandlers neu entwerfen:

sigfunc *signal(int, sigfunc*);

Jetzt sehen wir, dass es einfacher zu verstehen ist, dass die Signal-Handler-Funktion eine ganze Zahl und einen Funktionszeiger vom Typ sigfunc akzeptiert, während sie einen Funktionszeiger vom Typ sigfunc zurückgibt.

Beispiel C Programm zum Empfangen eines Signals

Die meisten Linux-Benutzer verwenden die Tastenkombination Strg+C, um Prozesse unter Linux zu beenden.

Haben Sie schon einmal darüber nachgedacht, was sich dahinter verbirgt? Nun, immer wenn Strg+C gedrückt wird, wird ein Signal SIGINT an den Prozess gesendet. Die Standardaktion dieses Signals besteht darin, den Prozess zu beenden. Aber auch dieses Signal kann verarbeitet werden. Der folgende Code demonstriert dies:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
  if (signo == SIGINT)
    printf("received SIGINT\n");
}

int main(void)
{
  if (signal(SIGINT, sig_handler) == SIG_ERR)
  printf("\ncan't catch SIGINT\n");
  // A long long wait so that we can easily issue a signal to this process
  while(1) 
    sleep(1);
  return 0;
}

Im obigen Code haben wir einen lang andauernden Prozess mit einer unendlichen While-Schleife simuliert.

Eine Funktion sig_handler wird als Signalhandler verwendet. Diese Funktion wird beim Kernel registriert, indem sie als zweites Argument des Systemaufrufs „signal“ in der main()-Funktion übergeben wird. Das erste Argument der Funktion „signal“ ist das Signal, das der Signal-Handler handhaben soll, in diesem Fall SIGINT.

Nebenbei bemerkt, die Verwendung der Funktion sleep(1) hat einen Grund. Diese Funktion wurde in der While-Schleife verwendet, damit die While-Schleife nach einiger Zeit ausgeführt wird (dh in diesem Fall eine Sekunde). Dies ist wichtig, da ansonsten eine unendliche While-Schleife, die wild läuft, den größten Teil der CPU verbrauchen und den Computer sehr, sehr langsam machen könnte.

Wie auch immer, kommen wir zurück, wenn der Prozess ausgeführt wird und wir versuchen, den Prozess mit Strg+C:

zu beenden
$ ./sigfunc
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT
^Creceived SIGINT

Wir sehen in der obigen Ausgabe, dass wir die Tastenkombination Strg + C mehrmals versucht haben, aber jedes Mal wurde der Prozess nicht beendet. Dies liegt daran, dass das Signal im Code behandelt wurde und dies durch den Druck bestätigt wurde, den wir auf jeder Zeile erhielten.

SIGKILL, SIGSTOP und benutzerdefinierte Signale

Abgesehen von der Behandlung der verfügbaren Standardsignale (wie INT, TERM usw.). Wir können auch benutzerdefinierte Signale haben, die gesendet und verarbeitet werden können. Es folgt der Code, der ein benutzerdefiniertes Signal USR1 behandelt:

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
    if (signo == SIGUSR1)
        printf("received SIGUSR1\n");
    else if (signo == SIGKILL)
        printf("received SIGKILL\n");
    else if (signo == SIGSTOP)
        printf("received SIGSTOP\n");
}

int main(void)
{
    if (signal(SIGUSR1, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGUSR1\n");
    if (signal(SIGKILL, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGKILL\n");
    if (signal(SIGSTOP, sig_handler) == SIG_ERR)
        printf("\ncan't catch SIGSTOP\n");
    // A long long wait so that we can easily issue a signal to this process
    while(1) 
        sleep(1);
    return 0;
}

Wir sehen, dass wir im obigen Code versucht haben, ein benutzerdefiniertes Signal USR1 zu verarbeiten. Außerdem wissen wir, dass zwei Signale KILL und STOP nicht behandelt werden können. Daher haben wir auch versucht, diese beiden Signale zu handhaben, um zu sehen, wie der Systemaufruf „Signal“ in diesem Fall reagiert.

Wenn wir den obigen Code ausführen:

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP

Die obige Ausgabe macht also deutlich, dass, sobald der Systemaufruf „signal“ versucht, einen Handler für KILL- und STOP-Signale zu registrieren, die Signalfunktion fehlschlägt, was darauf hinweist, dass diese beiden Signale nicht abgefangen werden können.

Nun versuchen wir mit dem kill-Befehl das Signal USR1 an diesen Prozess zu übergeben:

$ kill -USR1 2678

und auf dem Terminal, auf dem das obige Programm läuft, sehen wir :

$ ./sigfunc

can't catch SIGKILL

can't catch SIGSTOP
received SIGUSR1

Wir sehen also, dass das benutzerdefinierte Signal USR1 im Prozess empfangen und ordnungsgemäß verarbeitet wurde.


Linux
  1. Linux – Kann Strg+c das Sigint-Signal an mehrere Prozesse senden?

  2. Dateiberechtigungen unter Linux mit Beispiel

  3. Smalltalk Hello World Beispiel:Wie man ein Smalltalk-Programm unter Linux OS schreibt und ausführt

  4. Fortran Hello World Beispiel:Wie man ein Fortran-Programm unter Linux OS schreibt und ausführt

  5. Beispiel für XQuery Hello World:Wie man ein XQuery-Programm unter Linux schreibt und ausführt

5 Tipps zum Beherrschen von Linux

Linux-Signale – Beispiel-C-Programm zum Abfangen von Signalen (SIGINT, SIGKILL, SIGSTOP usw.)

Grundlagen zu Linux-Signalen – Teil I

So erstellen Sie Threads in Linux (mit einem C-Beispielprogramm)

Rufen Sie eine Funktion auf, wenn das Programm beendet ist, mit ctrl c

IPC mit Signalen unter Linux