Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, SystemprogrammierungC C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Unix Prozess signal Prozess Prozesse Signale IPC raise kill alarm pause Linuxsystemprogrammieren signal Prozess Prozesse Signale IPC raise kill alarm pause 4. Signale

4.1. signal - Einrichten von Signalhandlern           zurück zum Inhaltsverzeichnis

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

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. Die einzelnen Signale werden weiterhin in folgende drei verschiedene Klassen unterteilt ...

Tritt ein Signal auf, wird dies im Prozesstabelleneintrag hinterlegt. Sobald der Prozess dann an der Reihe ist, für den dieses Signal bestimmt ist, (genauer: der Prozess befindet sich im TASK_RUNNING - Zustand) so wird in einer Tabelle von Zeigern auf Funktionen in der Task-Struktur nachgesehen, wie auf dieses 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 ...

Signale und wie Sie darauf reagieren können

Sie haben also folgende 3 Möglichkeiten auf Signale zu reagieren ...

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

#include <signal.h>

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

Ein solchen Prototypen zu lesen ist fast schon untragbar. Aus diesem Grund können Sie zu Beginn gleich 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. 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


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

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

Jetzt benötigen Sie noch die Variable sighandler. Hierbei sind auch zwei Angaben 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 bei dem Programm die Tastenkombination STRG+C gedrückt wurde, wird die Default-Einstellung des Signals SIGINT ausgeführt und dies bedeutet, dass 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 und übergeben die Adresse einer eigenen Funktion, die das Programm ausführen soll, wenn die Tastekombination STRG+C gedrückt wurde z.B. ...

signal(SIGINT,funktionsaufruf);

Jetzt wird es Zeit um zu sehen wie Sie die Funktion signal() in der Praxis einsetzen können ...

#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 Sie einen Signalhandler für das Signal SIGINT ein, der die Funktion sigfunc aufrufen soll.

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

#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 weiteres Beispiel mit SIGABRT ...

#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 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() (eigentlich sollte es nicht wollen sonder sollen heißen) auf Fehler überprüfen könnte dies so aussehen ...

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

Für Linux/Unix gilt außerdem noch, dass 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 Prozess, 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 numerischen Werte von System zu System variieren können. Also besser ...

signal(SIGINT, function);

... anstatt wie auch möglich ...

signal(2,function);

Weiter mit 4.2. raise und kill