Rheinwerk Computing < openbook > Rheinwerk Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger

 << zurück
Linux-UNIX-Programmierung von Jürgen Wolf
Das umfassende Handbuch – 2., aktualisierte und erweiterte Auflage 2006
Buch: Linux-UNIX-Programmierung

Linux-UNIX-Programmierung
1216 S., mit CD, 49,90 Euro
Rheinwerk Computing
ISBN 3-89842-749-8
gp Kapitel 8 Signale
  gp 8.1 Grundlage zu den Signalen
    gp 8.1.1 Signalmaske
    gp 8.1.2 Signale und fork()
    gp 8.1.3 Signale und exec
    gp 8.1.4 Übersicht zu den Signalen
  gp 8.2 Das neue Signalkonzept
    gp 8.2.1 Wozu ein »neues« Signalkonzept?
  gp 8.3 Signalmenge initialisieren
  gp 8.4 Signalmenge hinzufügen oder löschen
  gp 8.5 Signale einrichten oder erfragen
    gp 8.5.1 Einen Signalhandler einrichten, der zurückkehrt
  gp 8.6 Signal an den eigenen Prozess senden – raise()
  gp 8.7 Signale an andere Prozesse senden – kill()
  gp 8.8 Zeitschaltuhr einrichten – alarm()
  gp 8.9 Prozesse stoppen, bis ein Signal eintritt – pause()
  gp 8.10 Prozesse für eine bestimmte Zeit stoppen – sleep() und usleep()
  gp 8.11 Signalmaske erfragen oder ändern – sigprocmask()
  gp 8.12 Prozess während einer Änderung der Signalmaske stoppen – sigsuspend()
  gp 8.13 Prozesse synchronisieren


Rheinwerk Computing

8.5 Signale einrichten oder erfragen  downtop

Die Funktion sigaction() hat im Prinzip denselben Effekt wie die »alte« signal()-Funktion, nur dass Sie mit dieser Funktion mehr Kontrolle und Handlungsmöglichkeiten mit den Signalen haben. Sie können mit dieser Funktion ein Signal mit einem Signalhandler einrichten oder den eingerichteten Signalhandler abfragen – oder auch beides zusammen.

Die Struktur sigaction wird für die gleichnamige Funktion verwendet und beinhaltet Informationen, wie das Signal behandelt wird. In dieser Struktur sind folgende Strukturvariablen enthalten:

gp  sighandler_t sa_handler – Hier befindet sich die Adresse der Funktion (Signalhandler), die beim Auftreten des Signals aufgerufen werden soll. Dabei können aber auch die konstanten Werte SIG_DFL (für Standardaktion) und SIG_IGN (zum Ignorieren des Signals) verwendet werden.
gp  sigset_t sa_mask – Hier werden die Signale angegeben, die blockiert werden sollen, während der Signalhandler ausgeführt wird.
gp  sa_flags – Hier können Sie einige zusätzliche Flags, so genannte Signaloptionen, angeben.

Hierzu die Syntax der Funktion sigaction():

int sigaction (int sig_nr,
               const struct sigaction *handler_neu,
               struct sigaction *handler_alt); 

Mit dem ersten Argument geben Sie das Signal an, wozu Sie einen Signalhandler einrichten oder erfragen wollen. Mit dem zweiten Argument geben Sie den neu einzurichtenden Signalhandler an. Wird hierbei NULL angegeben, wird kein neuer Signalhandler eingerichtet. Mit dem dritten Argument können Sie den aktuellen Signalhandler erfragen. Sind Sie daran nicht interessiert, können Sie NULL angeben.

Hier noch einige Angaben, die Sie bei der Strukturvariablen sa_flags machen können. Es können mit dem bitweisen ODER-Operator mehrere Optionen miteinander verknüpft werden. Folgende gängige Optionen können für den Signalhandler gesetzt werden:

gp  SA_NOCLDSTOP (POSIX, SVR4, BSD) – Wird ein Kindprozess angehalten, soll das Signal SIGCHLD nicht generiert werden, sondern nur, wenn der Kindprozess sich beendet.
gp  SA_RESTART (SVR4, BSD) – Wird ein Systemaufruf (Systemcall) durch dieses Signal unterbrochen, wird dieser automatisch neu gestartet.
gp  SA_ONESHOT – Der Signalhandler wird nach dessen Ausführung wieder zurückgesetzt. Dies entspricht dem SYSV-Stil.

Es gibt noch mehr dieser Signaloptionen, die aber zum einen Teil nur für SVR4 und zum anderen Teil nur für Linux vorhanden sind. Dies wären SA_ONSTACK (SVR4, BSD), SA_NOCLDWAIT (SVR4), SA_NODEFER (SVR4), SA_RESETHAND (SVR4), SA_SIGINFO (SVR4), SA_NOMASK (Linux).

Das folgende Beispiel demonstriert Ihnen die meisten hier vorgestellten Funktionen. Dabei wurde außerdem zur Demonstration die Funktion signal() mit der Funktion sigaction() nachgebildet. Mit einer weiteren Funktion können Sie einzelne Signale der Menge hinzufügen (signal_add()), und mit einer anderen Funktion können einzelne Signale entfernt (signal_del()) werden. Ebenso wurde eine Funktion hinzugefügt, mit der Sie den Status eines Signals ermitteln können (check_signal()).

/* sig.c */
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd. h.>
typedef void (*sighandler_t)(int);
static sighandler_t 
my_signal(int sig_nr, sighandler_t signalhandler) {
   struct sigaction neu_sig, alt_sig;
   neu_sig.sa_handler = signalhandler;
   sigemptyset (&neu_sig.sa_mask);
   neu_sig.sa_flags = SA_RESTART;
   if (sigaction (sig_nr, &neu_sig, &alt_sig) < 0)
      return SIG_ERR;
   return alt_sig.sa_handler;
}
static sighandler_t 
signal_add (int sig_nr, sighandler_t signalhandler) {
   struct sigaction add_sig;
   if (sigaction (sig_nr, NULL, &add_sig) < 0)
      return SIG_ERR;
   add_sig.sa_handler = signalhandler;
   sigaddset (&add_sig.sa_mask, sig_nr);
   add_sig.sa_flags = SA_RESTART;
   if (sigaction (sig_nr, &add_sig, NULL) < 0)
      return SIG_ERR;
   return add_sig.sa_handler;
}
static sighandler_t signal_del (int sig_nr) {
   struct sigaction del_sig;
   if (sigaction (sig_nr, NULL, &del_sig) < 0)
      return SIG_ERR;
   del_sig.sa_handler = SIG_DFL;
   sigdelset (&del_sig.sa_mask, sig_nr);
   del_sig.sa_flags = SA_RESTART;
   if (sigaction (sig_nr, &del_sig, NULL) < 0)
      return SIG_ERR;
   return del_sig.sa_handler;
}
static void check_signal (int sig_nr) {
   struct sigaction check_sig;
   if (sigaction (sig_nr, NULL, &check_sig) < 0)
      return;
   printf("Aktion beim Auftreten von Signal %d: ", sig_nr);
   if (check_sig.sa_handler == SIG_DFL)
      printf ("Standardaktion (default)\n");
   else if (check_sig.sa_handler == SIG_IGN)
      printf ("wird ignoriert\n");
   else
      printf ("Eine Routine wird aufgerufen\n");
}
static void check_sig (int signr) {
   int nr;
   if (signr == SIGINT) {
      printf ("SIGINT ausgelöst\n");
      printf ("-1- Signal aus Maske entfernen\n");
      printf ("-2- Signal ignorieren\n");
      printf ("-3- SIGALRM in 5 Sekunden auslösen\n");
      printf ("-4- Programm beenden\n");
      printf ("Ihre Wahl : ");
      scanf ("%d", &nr);
      switch (nr) {
      case 1:
         signal_del (SIGINT);
         printf ("SIGINT wurde entfernt\n");
         break;
      case 2:
         return;
      case 3:
         alarm (5);
         break;
      case 4:
         raise (SIGKILL);
         break;
      default:
         printf ("Unbekannte Eingabe\n");
         break;
      }
   }
   else if (signr == SIGALRM) {
      printf ("SIGALRM ausgelöst\n");
      raise (SIGKILL);
   }
}
int main (void) {
   my_signal (SIGINT, check_sig);
   signal_add (SIGALRM, check_sig);
   signal_add (SIGABRT, SIG_IGN);
   check_signal (SIGINT);
   check_signal (SIGALRM);
   check_signal (SIGABRT);
   check_signal (SIGFPE); /* Standardaktion von SIGFPE */
   while (1) ;
   return EXIT_SUCCESS;
}

Das Programm bei seiner Ausführung:

$ gcc -o sig sig.c
$ ./sig
Für Signal 2 wurde eine Routine eingerichtet
Für Signal 14 wurde eine Routine eingerichtet
Signal 6 wird ignoriert
Für Signal 8 ist die Standardaktion eingerichtet
STRG+C
SIGINT ausgelöst
-1- Signal aus Maske entfernen
-2- Signal ignorieren
-3- SIGALRM in 5 Sekunden auslösen
-4- Programm beenden
Ihre Wahl : 3
SIGALRM ausgelöst
Getötet
$ ./sig
Für Signal 2 wurde eine Routine eingerichtet
Für Signal 14 wurde eine Routine eingerichtet
Signal 6 wird ignoriert
Für Signal 8 ist die Standardaktion eingerichtet
STRG+C
SIGINT ausgelöst
-1- Signal aus Maske entfernen
-2- Signal ignorieren
-3- SIGALRM in 5 Sekunden auslösen
-4- Programm beenden
Ihre Wahl : 2
STRG+C
SIGINT ausgelöst
-1- Signal aus Maske entfernen
-2- Signal ignorieren
-3- SIGALRM in 5 Sekunden auslösen
-4- Programm beenden
Ihre Wahl : 1
SIGINT wurde entfernt
STRG+C

Rheinwerk Computing

8.5.1 Einen Signalhandler einrichten, der zurückkehrt  toptop

Stellen Sie sich vor, Ihr Programm befindet sich in einer Schleife und arbeitet gerade wichtige Befehle ab – z. B. das Abspeichern wichtiger Daten. Plötzlich tritt ein Signal auf, der Signalhandler wird aufgerufen und bricht das Programm mitsamt Schreibvorgang ab. Also sollten Sie den Signalhandler so einrichten, dass dieser zur Ausführung des kritischen Codeausschnitts zurückkehren kann, und erst wenn alle Befehle in dem Schleifendurchgang abgelaufen sind, soll der Schleifendurchlauf abgebrochen werden.

Um einen Handler zu realisieren, der wieder normal zum Programm zurückkehrt, benötigen Sie eine globale Variable. Diese Variable sollte dann beim Aufrufen des Signalhandlers entsprechend verändert und im Programmablauf eben entsprechend überprüft werden. Als Datentyp sollten Sie für einen solchen Fall sig_atomic_t verwenden, eine atomare Variable.


Atomare Variable oder atomare Operation   Wenn Sie z. B. eine Variable auf einem System um eins erhöhen wollen, wo zwei CPUs arbeiten, kann es passieren, dass ein Wert um den Wert zwei anstatt wie beabsichtigt um den Wert eins erhöht wird, wenn der Code auf beiden Prozessoren ausgeführt wird. Auf der CPU-Ebene läuft dieser Vorgang ja wie folgt ab: Wert vom Speicher, in den Prozessorregister kopieren; Wert erhöhen; Wert wieder vom Register in den Speicher schieben. Nun stellen aber die Prozessoren einen Mechanismus (sofern der Systemkern dies auch unterstützt) zur Verfügung, mit dem die Operationen atomar bearbeitet werden. Das bedeutet, dass bei Verwendung einer atomaren Operation verhindert wird, dass andere Prozessoren im System arbeiten, solange der aktuelle Prozessor mit der Ausführung der atomaren Operation beschäftigt ist. Dies wird durch spezielle Lock-Anweisungen erreicht. Es bleibt allerdings anzumerken, dass atomare Operationen auf manchen Systemen wesentlich langsamer ausgeführt werden als »normale« Operationen.


Hierzu ein einfaches Beispiel: Es werden ein Signalhandler für das Signal SIGINT und eine globale Variable eingerichtet. Im Programm selbst wird in zwei rechenintensiven Schleifen jeweils Ping und Pong ausgegeben. Wird das Signal SIGINT ausgelöst, während Ping ausgegeben wird, so wird der Signalhandler aufgerufen, und der dekrementiert die globale Variable. Die dekrementierte globale Variable wird als Bedingung der Schleife verwendet – ist diese 0, wird die Schleife im nächsten Durchlauf abgebrochen.

/* ping_pong.c */
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd. h.>
typedef void (*sighandler_t)(int);
/* Flag kontrolliert die Beendigung der Hauptschleife */
volatile sig_atomic_t sigint = 1;
static sighandler_t 
my_signal (int sig_nr, sighandler_t signalhandler) {
   struct sigaction neu_sig, alt_sig;
   neu_sig.sa_handler = signalhandler;
   sigemptyset (&neu_sig.sa_mask);
   neu_sig.sa_flags = SA_RESTART;
   if (sigaction (sig_nr, &neu_sig, &alt_sig) < 0)
      return SIG_ERR;
   return alt_sig.sa_handler;
}
/* Der Signalhandler */
static void catch_sigint (int sig_nr) {
   sigint--;
   /* brauchen wir nicht unbedingt, der Kernel arbeitet */
   /* neuerdings nach dem BSD-Schema statt SYSV.        */
   /* Der Handler bleibt eingerichtet.                  */
   my_signal (sig_nr, catch_sigint); 
}
int main (void) {
   int i = 90000000, j, k;
   my_signal (SIGINT, catch_sigint);
   /* Damit ist es möglich, dass die Anweisungen in der */
   /* Schleife noch fertig arbeiten, ohne dass bei      */
   /* Eintreffen des Signals SIGINT das Programm abrupt */
   /* unterbrochen wird. Im Beispiel wird kein Ping     */ 
   /* ohne seinen Pong ausgegeben.                      */
   while (sigint) {
      j = i;
      k = i;
      while (--j) ;
      while (--k) ;
      printf ("Ping-");
      j = i;
      k = i;
      while (--j) ;
      while (--k) ;
      printf ("Pong\n");
   }
   return EXIT_SUCCESS;
}
 << zurück
  
  Zum Rheinwerk-Shop
Neuauflage: Linux-UNIX-Programmierung
Neuauflage:
Linux-UNIX-
Programmierung

bestellen
 Ihre Meinung?
Wie hat Ihnen das Openbook gefallen?
Ihre Meinung

 Buchtipps
Zum Rheinwerk-Shop: Linux-Server






 Linux-Server


Zum Rheinwerk-Shop: Das Komplettpaket LPIC-1 & LPIC-2






 Das Komplettpaket
 LPIC-1 & LPIC-2


Zum Rheinwerk-Shop: Linux-Hochverfügbarkeit






 Linux-
 Hochverfügbarkeit


Zum Rheinwerk-Shop: Shell-Programmierung






 Shell-
 Programmierung


Zum Rheinwerk-Shop: Linux Handbuch






 Linux Handbuch


 Lieferung
Versandkostenfrei bestellen in Deutschland, Österreich und der Schweiz
Info





Copyright © Rheinwerk Verlag GmbH 2006
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das Openbook denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt.
Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


Nutzungsbestimmungen | Datenschutz | Impressum

Rheinwerk Verlag GmbH, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, service@rheinwerk-verlag.de

Cookie-Einstellungen ändern