Your Browser is not longer supported

Please use Google Chrome, Mozilla Firefox or Microsoft Edge to view the page correctly
Loading...

{{viewport.spaceProperty.prod}}

signal - Signalbearbeitung steuern

&pagelevel(4)&pagelevel

Definition

#include <signal.h>

void (*signal(int sig, void (*fkt) (int))) (int);

Die Funktion signal steht zur Behandlung von Signalen zur Verfügung.

Man muss zwei Arten von Signalen unterscheiden, die ein Programm erhalten und behandeln kann. Sie unterscheiden sich in der Art der Auslösung. In der Folge davon wird ihre Behandlung intern unterschiedlich realisiert:

  1. STXIT-Ereignisse

    STXIT-Ereignisse werden ausgelöst

    • durch Programmfehler, z.B. Adressfehler, Ausführung von ungültigen Instruktionen, Division durch Null etc.,

    • durch die Funktion alarm,

    • von außen, z.B. durch Drücken der K2-Taste, bestimmte Kommandos (ABEND, INFORM-PROGRAM etc.)

    Die Behandlung dieser Ereignisse wird intern über den BS2000-spezifischen STXIT- Contingency-Mechanismus realisiert. Dieser Mechanismus sowie die STXIT-Ereignisklassen sind ausführlich im Handbuch „Makroaufrufe an den Ablaufteil“ dargestellt.

  2. raise-Signale

    Hierunter sind alle Ereignisse zu verstehen, die mit der Funktion raise ausgelöst werden können. Mit raise lassen sich STXIT-Ereignisse simulieren und von STXIT-Ereignissen unabhängige Signale (benutzereigene sowie vordefinierte) senden.

    Die Behandlung dieser Art Signale wird C-spezifisch, d.h. nicht über den o.g. Mechanismus realisiert.

Hat ein Programm keine Behandlung von Signalen vorgesehen, wird bei Eintritt eines Signals das Programm abgebrochen.

Das Programm kann ein Signal aber auch abfangen. Dazu muss man die Funktion signal aufrufen und als Argument eine Funktion fkt übergeben. Es gibt dann folgende Möglichkeiten, auf ein Signal zu reagieren:

  • fkt ist die voreingestellte Funktion SIG_DFL: Das Programm wird abgebrochen.

  • fkt ist die vordefinierte Funktion SIG_IGN: Das Signal wird ignoriert.

  • fkt ist eine selbst geschriebene Routine: Das Signal wird gemäß dieser Routine behandelt.

Im Folgenden werden diese drei Möglichkeiten der Signalbehandlung etwas ausführlicher erläutert, um dabei vor allem auf die unterschiedliche Behandlung von STXIT-Ereignissen und raise-Signalen einzugehen.

Programmabbruch

Programmabbruch erfolgt dann, wenn das Programm keine Signalbehandlung vorsieht oder wenn signal mit der Funktion SIG_DFL aufgerufen wird.

STXIT-Ereignis:

Es erfolgt die Standard-Abbruchreaktion durch das Betriebssystem. Das Programm wird abgebrochen, und es werden Informationen ausgegeben zur Abbruchadresse und zum Fehlergewicht sowie eine DUMP-Meldung:

... PROCESSING INTERRUPTED AT adresse ..., EC=gewicht

... DUMP DESIRED? REPLY(Y=YES,N=NO)?

raise-Signal:

Es erfolgt eine C-spezifische Programmbeendigung durch exit(-1) und es werden folgende Meldungen ausgegeben:

CCM0101 signal occured: signal

CCM0999 Exit -1

Signal ignorieren

Ein Signal wird ignoriert, wenn signal mit der vordefinierten Funktion SIG_IGN aufgerufen wird. Der Programmablauf wird fortgesetzt, so als wenn kein Signal eingetreten wäre. Hier gibt es keinen Unterschied zwischen der Behandlung von STXIT-Ereignissen und raise-Signalen.

Signal gemäß eigener Funktion fkt behandeln

Ein Signal wird gemäß einer selbst geschriebenen Funktion fkt behandelt, wenn signal mit dem Namen dieser Funktion aufgerufen wird. Bei Eintritt eines Signals wird das aufrufende Programm unterbrochen und die Funktion fkt ausgeführt. Nach Beendigung der Signalbehandlung wird das Programm an der Stelle fortgesetzt, an der es unterbrochen wurde (Ausnahme: in fkt wurden die Funktionen exit bzw. longjmp aufgerufen).

STXIT-Ereignis:

fkt wird intern als eigener STXIT-Contingency-Prozess realisiert, das übrige Programm als sog. „Basisprozess“. Die Steuerung wird durch das Betriebssystem vorgenommen.

raise-Signal:

fkt wird intern als „normale“ C-Funktion behandelt und nicht über den Contingency-Mechanismus realisiert. Die Steuerung obliegt dem C-Laufzeitsystem.

Weitere Einzelheiten bzgl. der unterschiedlichen Realisierung und der damit verbundenen unterschiedlichen Möglichkeiten von signal-Aufrufen entnehmen Sie bitte den „Hinweisen“.

Parameter

int sig

Signal, das behandelt werden soll.

Für sig können symbolische Konstanten eingesetzt werden, die in der folgenden Tabelle unter „SIGNR“ aufgelistet sind. Diese Konstanten sind in der Include-Datei <signal.h> definiert.
Die Tabelle zeigt außerdem in der letzten Spalte die möglichen Arten der Signalauslösung (STXIT-Ereignis / raise / alarm). Bei STXIT-Ereignissen ist die jeweilige STXIT- Ereignisklasse angegeben.

SIGNR

Bedeutung

Signalauslösung durch

SIGHUP

Abbruch der Dialogstationsleitung

ABEND / raise

SIGINT

Unterbrechung von der Dialogstation (K2)

ESCPBRK / raise

SIGILL

Ausführung einer ungültigen Instruktion

PROCHK / raise

SIGABRT

raise-Signal für Programmbeendigung mit

_exit(-1); abort

raise / abort

SIGFPE

fehlerhafte Gleitkommaoperation

PROCHK / raise

SIGKILL

raise-Signal für Programmbeendigung mit

exit(-1)

raise

SIGSEGV

Speicherzugriff mit unerlaubtem Segmentzugriff

ERROR / raise

SIGALARM

Zeitintervall abgelaufen (Realzeit)

RTIMER / raise / alarm

SIGTERM

Programmbeendigung

TERM / raise

SIGUSR1

vom Benutzer definiert

raise

SIGUSR2

vom Benutzer definierts

raise

SIGDVZ

Division durch 0

PROCHK / raise

SIGXCPU

CPU-Zeit aufgebraucht

RUNOUT / raise

SIGBPT

Haltepunkt (nicht unterstützt)

SVC

SIGTIM

Zeitintervall abgelaufen (CPU-Zeit, SETIC)

TIMER / raise

SIGINTR

SEND-MESSAGE-Kommando

INTR / raise

SIGSVC

SVC-Aufruf (nicht unterstützt)

SVC

Die symbolische Konstante für die Signalnummer kann durch einen weiteren symbolischen Namen ergänzt werden, z.B. signal(SIGDVZ + SIG_PSK, fkt). Mit diesem Zusatz (im Beispiel "+ SIG_PSK") wird gesteuert, ob die Funktion fkt nur auf Grund eines STXIT-Ereignisses oder zusätzlich auf Grund eines raise-Signals aktiviert werden soll und ob fkt dem entsprechenden Signal temporär oder permanent zugeordnet werden soll. Die technischen Hintergründe hierfür entnehmen Sie bitte den „Hinweisen“. Die symbolischen Namen sind in <signal.h> definiert.

Ohne Zusatz ist SIG_TSK voreingestellt.

Symbolischer Name

Zuordnung

Aktivierung durch

SIG_TSK

temporär

STXIT / raise (Standard)

SIG_TS

temporär

STXIT

SIG_PSK

permanent

STXIT / raise

SIG_PS

permanent

STXIT

void (*fkt)(int)

Name der Funktion, die bei Eintritt eines Signals aufgerufen werden soll. Diese Funktion erhält als einziges Argument die Signalnummer vom Typ int. Die Funktion muss vor dem entsprechenden signal-Aufruf definiert werden!

In <signal.h> gibt es zwei vordefinierte Funktionen:

SIG_DFL

Diese Funktion ist voreingestellt und bewirkt einen Programmabbruch. Die Art des Abbruchs hängt davon ab, ob es sich um ein STXIT-Ereignis oder um ein raise-Signal handelt (siehe oben).

SIG_IGN

Das Signal wird ignoriert.

Returnwert

Vor dem signal-Aufruf gültige Funktion zur Signalbehandlung



bei Erfolg. signal liefert die letzte Einstellung der Signalbehandlung, und zwar SIG_DFL, SIG_IGN oder eine vom Benutzer geschriebene Funktion fkt.


SIG_ERR (= 1)

bei Fehler, z.B. wenn sig keine gültige Signalnummer ist oder fkt auf eine unzulässige Adresse zeigt.
Zusätzlich wird errno auf den entsprechenden Fehlercode gesetzt:
EINVAL (unzulässiges Argument)
EFAULT (unzulässige Adresse).

Hinweise

Das Signal SIGKILL kann nicht abgefangen werden, d.h. ihm kann weder eine selbst geschriebene Funktion noch SIG_IGN zugeordnet werden.

Wenn für ein Signal, dem bereits eine Signalbehandlung zugeordnet ist, eine zweite Funktion zur Signalbehandlung angemeldet wird, wird zunächst die erste Funktion abgemeldet, bevor die neue Funktion angemeldet wird. Aus diesem Grunde ist für eine kurze Zeitspanne keine Signalbehandlung für das betroffene Signal angemeldet.

Aus einer Funktion, die dem Signal SIGTERM zugeordnet ist, kann nicht mit einem long- jmp-Aufruf zurückgekehrt werden. Zum Zeitpunkt der Signalauslösung sind die Einträge im C-Laufzeitstack für alle Funktionen einschließlich der main-Funktion bereits abgebaut.

Bei Programmstart ist für alle Signale SIG_DFL voreingestellt (vgl. nächsten Absatz).

Temporäre/permanente Zuordnung: In vielen Implementierungen (z.B. UNIX) und auch im ANSI-Standard ist die temporäre Zuordnung eines Signals zu einer Funktion vorgesehen. Das bedeutet: Die vom Anwender vorgenommene Zuordnung einer Funktion zu einer Signalnummer gilt nur temporär für ein einziges Auftreten dieses Signals. Die Zuordnung wird nach Auftritt des Signals aufgehoben und auf SIG_DFL (Programmabbruch) zurückgesetzt. Lediglich die Zuordnung SIG_IGN (Signal ignorieren) gilt permanent für mehrmaliges Auftreten des entsprechenden Signals.

  • Im BS2000 ist die Signalbehandlung für Signale vom Typ „STXIT-Ereignisse“ über den STXIT-Contingency-Mechanismus realisiert. Dieser Mechanismus beruht auf einer permanenten Zuordnung eines STXIT-Ereignisses zu einer STXIT-Contingency-Routine, d.h. eine temporäre Zuordnung kann nur durch explizites Abmelden erreicht werden.

  • Um einerseits die temporäre Zuordnung vieler Implementierungen zu erfüllen, andererseits den BS2000-üblichen permanenten Charakter performant zu unterstützen, werden beide Varianten angeboten. D.h., man kann auswählen, ob eine Signalroutine temporär oder permanent zugeordnet wird.

  • Zusätzlich wird aus Performancegründen die Möglichkeit angeboten, zu entscheiden, ob eine Signalroutine nur durch STXIT-Ereignisse ausgelöst werden kann (performanter) oder zusätzlich auch durch raise-Signale.

  • Die o.g. Auswahlmöglichkeiten sind realisiert durch symbolische Ergänzungen der eigentlichen Signalnummer: SIG_TSK, SIG_TS, SIG_PSK, SIG_PS (siehe Parameterbeschreibung sig).

  • Wollen Sie ein Signal jedes Mal mit fkt abfangen, sind z.B. folgende signal-Aufrufe möglich:

    signal(SIGDVZ + SIG_PSK, fkt);   /* fkt wird durch STXIT-Ereignis und */
                                     /* raise-Signal SIGDVZ aktiviert.  */
    signal(SIGDVZ + SIG_PS, fkt);    /* fkt wird nur durch STXIT-Ereignis */
                                          /* SIGDVZ aktiviert. */
    

    Folgende Aufrufe sind äquivalent, d.h. beide sehen eine temporäre Zuordnung vor, und die Signalroutine wird durch ein STXIT-Ereignis und ein raise-Signal aktiviert:

    signal(SIGDVZ, fkt);
    signal(SIGDVZ + SIG_TSK, fkt);

Probleme können sich bei den drei verschiedenen Signalnummern ergeben, die durch dieselbe STXIT-Ereignisklasse (PROCHK) abgebildet werden. Folgende signal-Aufrufe werden unterschiedlich behandelt, je nach Art der Auslösung des Signals:

signal(SIGILL, fkt1);
signal(SIGFPE, fkt2);
signal(SIGDVZ, fkt3);

STXIT-Ereignis:

Es wird in jedem Fall fkt3 aufgerufen, wenn die Signale SIGILL und SIGFPE über den STXIT-Contingency-Mechanismus abgefangen werden. Aber selbst, wenn nur für ein Signal ein signal-Aufruf vorgesehen ist, wird die zugeordnete Routine bei Eintreten aller drei Signale aktiviert.

raise-Signal:

Werden die Signale jedoch mit der raise-Funktion ausgelöst, wird die jeweils zugeordnete Funktion aktiviert. Signale, für die kein signal-Aufruf vorgesehen ist, werden standardmäßig behandelt (SIG_DFL, Programmabbruch).

Eine Funktion, die einem Signal zugeordnet ist, benötigt für ihren Ablauf eine intakte C-Umgebung. Daher werden bei regulärer Programmbeendigung unmittelbar vor dem Abbau der C-Umgebung alle Signalroutinen abgemeldet. Danach eintretende Ereignisse werden nicht mehr abgefangen, auch nicht SIGTERM.

Beispiel

Folgendes Programm fängt mit der Funktion fkt die STXIT-Ereignisse SIGDVZ (Division durch 0) und SIGINT (Unterbrechung mit der K2-Taste) ab und gibt eine entsprechende Fehlermeldung aus. Mittels der Funktionen setjmp und longjmp fährt das Programm nach Behandlung beider Unterbrechungsereignisse (sie finden an verschiedenen Stellen des Programmes statt) mit derselben Programmstelle fort (neue Eingabeaufforderung).

#include <stdio.h>
#include <signal.h>
#include <setjmp.h>
jmp_buf env;
void fkt(int sig)
{
  if(sig == SIGDVZ + SIG_PS)
    printf("Divisionsfehler, Eingabe wiederholen\n");
  if(sig == SIGINT + SIG_PS)
    printf("K2-Taste gedrueckt, Eingabe wiederholen\n");
  longjmp(env, 1);
}
int main(void)
{
  float a;
  float b;
  double z;
  signal(SIGDVZ + SIG_PS, fkt);
  signal(SIGINT + SIG_PS, fkt);
  setjmp(env);
  printf("Bitte a und b eingeben\n");  /* Unterbrechung mit K2 möglich */
  scanf("%f %f", &a, &b);
  z = a / b;                           /* Division durch 0 möglich, */
                                       /* falls b = 0 */
  printf("z = %f\n", z);
  printf ("Programmende\n");
  return 0;
}

Siehe auch

alarm, longjmp, raise, setjmp