ein Kapitel zurück                                           ein Kapitel weiter

Sie finden unter Linux etwa 30 verschieden Signale. Jedes Signal stellt dabei einen Ganzzahligen Wert da. Jedes dieser Signale hat dabei Intern ein bestimmte Wirkung. Ein Signalmechanismus besteht aus folgenden Bestandteilen....

  • Festlegung und Bedeutung von Signalen in Form von Ganzzahligen Werten
  • Merker im Prozeßtabelleneintrag für angekommene Signale
  • Tabelle mit Funktionsadressen, die bestimmen, wie auf Eintreffende Signale reagiert werden soll.

Wollen sie sich schon mal einen Überblick verschaffen welche Signale es gibt, geben sie in der Konsole folgendes ein.....

kill -l  

Zur Erklärung der einzelnen Signale kommen wir dann noch. Diese Signale werden weiterhin in drei verschiedene Klassen unterteilt.....

  • Systemsignale (Hardwarefehler, Systemfehler....)
  • Gerätesignale
  • Benutzerdefinierte Signale

Trifft ein Signal ein wird die im Prozeßtabelleneintrag hinterlegt. Sobald der Prozeß dann an der Reihe ist für den diese Signal bestimmt ist (genauer: der Prozeß befindet sich im TASK_RUNNING - Zustand) so wird in einer Tabelle von Zeigern auf Funktionen in der Task-Struktur nachgesehen, wie auf diese Signal reagiert werden soll. Die Signalnummer dient dabei als Index in dieser Tabelle.

Folgende Möglichkeiten haben sie dabei, wie auf Signale regiert werden soll....




Sie haben also folgende 3 Möglichkeiten......

  • Eintragen einer selbstgeschriebenen Funktion
  • Ignorieren des Signals (geht aber nicht mit SIGKILL)
  • Die voreingestellte default-Funktion verwenden

Um auf diese und andere Signale zu reagieren benötigen wir ein Signalkonzept. Bei diesem Konzept richtet ein Prozess sogenannte Signalhandler ein. Diese Signalhandler sagen dann dem Systemkern wenn das Signal auftritt tue dies! Dafür haben wir die Funktion signal(). Der Syntax von signal.....

#include <signal.h>

void(*signal(int signr, void(*sighandler)(int)))(int);  

Ein solchen Prototypen zu lesen ist fast schon untragbar. Aus diesem Grund wollen wir zu Beginn gleich mal die Funktion vereinfachen....

typedef void signalfunktion(int);  

Somit sieht unser Prototyp folgendermaßen aus...

signalfunktion *signal(int signr,signalfunktion *sighandler);  

Somit lässt sich die Funktion schon etwas leichter lesen. Kommen wir zur ersten Variablen in unserer Funktion. Mit signr legen sie die Nummer des Signals fest, für die ein Signalhandler eingerichtet werden soll. In der Headerdatei <signal.h> sind einige Signale definiert....

Name Beschreibung UNIX DOS Default-Aktion
SIGABRT abnormale Beendigung (abort()) X X Beendigung
SIGALRM Ablauf einer Zeitschaltuhr X   Beendigung
SIGBUS Hardware-Fehler X   Beendigung
SIGCHLD Statusänderung in Kindprozess X   Ignorieren
SIGCONT Fortsetzen eines angehaltenen Programms  X   Fortsetzen/Ignorieren
SIGEMT Hardware-Fehler X   Beendigung
SIGFPE Arithmetischer Fehler X X Beendigung
SIGILL Unerlaubter Hardware-Befehl X X Beendigung
SIGINT Unterbrechungstaste am Terminal X X Beendigung
SIGIO Asynchrone I/O X   Ignorieren
SIGKILL Beendigung X   Beendigung
SIGPIPE Schreiben in Pipes ohne Leser X   Beendigung
SIGPWR Stromausfall X   Ignorieren
SIGQUIT Unterbrechungstaste am Terminal X   Beendigung
SIGSEGV Unerlaubte Speicheradressierung X X Beendigung
SIGSTOP Prozess anhalten X   Prozess anhalten
SIGTTIN Lesewunsch von Hintergrundprozess X   Prozess anhalten
SIGTTOU Schreibwunsch von Hintergrundprozess X   Prozess anhalten
SIGUSR1 Benutzerdefiniert X   Beendigung
SIGUSR2 Benutzerdefiniert X   Beendigung
SIGXCPU Überschreitung des CPU-Limits X   Beendigung
SIGXFSZ Überschreitung der maximalen Dateigröße (4GB) X   Beendigung
SIGURG dringendes Ereignis X   Ignorieren
SIGWINCH Änderung der Windows-Größe X   Ignorieren


Folgende Befehle sind laut ANSI-C vorgeschrieben......

Name Bedeutung
SIGABRT Dieses Signal signalisiert das das Programm abnormal beendet wurde (abort()).
SIGFPE Diese Signal wird angezeigt z.B. bei einer Division durch 0 oder einem Überlauf einer Zahl.
SIGILL Dies wird angezeigt wenn ein illegaler Hardware-Befehl ausgeführt wird.
SIGINT Dies Signal wird an alle Prozesse geschickt wenn die Tasten-Kombination STRG-C gedrückt wurde.
SIGSEGV Wird dies Angezeigt wurde versucht auf eine unerlaubte Speicherstelle zu schreiben oder zu lesen.
SIGTERM Voreingestelltes Signal, das das kill-Kommando an einen Prozess schickt, das beendet werden soll.



Kommen wir wieder zurück zu unserer Erklärung des Prototypen.....

signalfunktion *signal(int signr,signalfunktion *sighandler);  

Jetzt benötigen wir noch die Variable sighandler. Hierbei sind auch zwei davon in der Headerdatei <signal.h> definiert. SIG_DFL und SIG_IGN. Mit SIG_DFL wird die Default-Aktion ausgeführt die meist das Beenden des Prozesses bedeutet. z.B....

signal(SIGINT,SIG_DFL);  

Falls das bei dem Programm die Tastenkombination STRG-C gedrückt wurde wird die Default-Einstellung von SIGINT ausgeführt und das bedeutet das das Programm beendet würde. Als zweite Möglichkeit können sie eingeben....

signal(SIGINT,SIG_IGN);  

Wenn sie nun die Tastenkombination STRG-C drücken würde nichts passieren da das Signal SIGINT ignoriert wird. Als dritte Möglichkeit können sie das Signal SIGINT abfangen übergeben die Adresse einer eigenen Funktion die das Programm ausführen soll wenn die Taste STRG-C gedrückt wurde z.B....

signal(SIGINT,funktionsaufruf);  

Jetzt wird es mal Zeit um zu sehen wie sie die Funktion signal() einsetzen können....

/*Download:signal1.c*/
#include <stdio.h> #include <stdlib.h> #include <signal.h> void sigfunc(int sig) { char c; if(sig != SIGINT) return; else { printf("\nWollen sie das Programm beenden (j/n) : "); while((c=getchar()) != 'n') return; exit (0); } } int main() { int i; signal(SIGINT,sigfunc); while(1) { printf("Die Endlosschleife koennen sie mit STRG-C beenden"); for(i=0;i<=48;i++) printf("\b"); } return 0; }

Mit...

signal(SIGINT,sigfunc);  

...richten wir einen Signalhandler für das Signal SIGINT ein der die Funktion sigfunc aufrufen soll.

Nun wollen wir ein Programm schreiben das Zahlen dividiert. Das Programm soll eine Funktion aufrufen die, falls versucht durch 0 zu teilen, ausgibt das dies nicht möglich ist. Zusätzlich wollen wir zur Demonstration die Tastenkombination STRG-C ignorieren....

/*Download:signal2.c*/
#include <stdio.h> #include <stdlib.h> #include <signal.h> void sigfunc(int sig) { if(sig != SIGFPE) return; else printf("Sie koennen keinen Wert durch 0 teilen!!\n"); } int main() { int wert,wert2; signal(SIGFPE,sigfunc); /*Floating Point Error*/ signal(SIGINT,SIG_IGN); /*STRG - C ignorieren*/ printf("Programm zum Dividieren von Zahlen!\n"); printf("Zahl eingeben : "); scanf("%d",&wert); while(1) { printf("geteilt durch > "); scanf("%d",&wert2); printf("Gesamt = %d\n",wert/=wert2); } }

Ein weiters Beispiel mit SIGABRT.....

/*Download:signal3.c*/
#include <stdio.h> #include <stdlib.h> #include <signal.h> void sigfunc(int sig) { if(sig==SIGABRT) printf("Demonstration von SIGABRT ausgeloest mit abort()\n"); } int main() { signal(SIGABRT,sigfunc); abort(); return 0; }

Ich glaube Ihnen dürfte es jetzt klar sein wie man die Signale richtig einsetzt. Um zu testen ob der Aufruf von signal überhaupt erfolgreich war gibt es in der Headerdatei <signal.h> den Fehlercode SIG_ERR der mit dem Wert -1 definiert ist. Wollen sie also signal(..) auf Fehler überprüfen könnte es etwa so aussehen.....

if( signal(SIGINT,sigfunc) == SIG_ERR)
   { ..........Fehler beim beim Aufruf von signal.......  

Für Linux/Unix gilt außerdem noch das die beiden Signal SIGKILL und SIGSTOP nicht mit SIG_IGN ignoriert werden können damit der Superuser immer die Möglichkeit hat das Programm zu killen oder stoppen.

Signale mit fork und exec

Der neue Prozeß den sie mit fork erzeugen, erbt natürlich auch alle Signalhandler des aufrufenden Prozesses.

Durch Überlagerung eines Prozesses mit den exec-Funktion, verlieren auch die eingerichteten Signalhandler Ihre Gültigkeit. Alle Signale werden wieder auf Ihren Ursprünglichen Zustand (default-Zustand) gesetzt und reagieren auch so.

Ein Hinweis zur Verwendung von Signalen: Verwenden sie für Signale stets den symbolischen Namen, da die nummerischen Werte von System zu System varieren können. Also besser....

signal(SIGINT, function);  

...als...

signal(2,function);  

ein Kapitel zurück          nach oben           ein Kapitel weiter


© 2001,2002 Jürgen Wolf