Untersttzung der Signalfunktionen in MagiC ab V4.50
----------------------------------------------------

Andreas Kromke
7.4.96


1. Was sind Signale ?
=====================

Meist werden Signale als Nachrichten (messages) betrachtet, die nur
aus einem Bit bestehen knnen, welches sagt, da eine Nachricht eingetroffen
ist. Insgesamt gibt es auch nur eine begrenzte Anzahl verschiedener
Nachrichten (genau: 31), und mehrmals dieselbe Nachricht hintereinander
zu schicken, hat keinen weiteren Effekt.

Ein wesentlicher Unterschied zum AES-Nachrichtenkonzept (->evnt_message())
besteht in der Behandlung auf der Seite des Empfngers. Whrend Nachrichten
in einer FIFO-Warteschlange landen und synchron, d.h. nur auf explizite
Abfrage des Empfngers abgerufen werden (d.h. sie werden "gepollt"),
entspricht das Verarbeiten eines Signals einem Interrupt. Mit dem Unterschied,
da dieser nicht asynchron, d.h. zu einem beliebigen Zeitpunkt vom System,
sondern von einem anderen Programm ("Proze") ausgelst wird.

Die wesentlichen GEMDOS-Aufrufe zur Signalbehandlung sind daher:

	Psignal() bzw. Psigaction()

zum Festlegen einer Signal-Behandlung und

	Pkill()

zum Senden eines Signals an ein anderes Programm (Proze).


2. Wozu Signale ?
=================

In der Praxis ergeben sich zwei Anwendungsflle. Einerseits kann mit dieser
Methode eine schnelle Prozekommunikation realisiert werden. Der Empfnger
kann ruhig in einer lngeren Berechnung stecken, trotzdem wird das Signal
i.a. sehr schnell bearbeitet (es sei denn, der Empfnger befindet sich gerade
in einer kritischen Dateioperation, dann wird die Ausfhrung verzgert).

Der zweite Anwendungsfall ist das Anfangen von Signalen, die vom System
ausgelst werden, meistens von SIGTERM (wird zum Beenden eines Programmes
verwendet). Dazu kann man aber auch einfach den etv_term-Vektor umbiegen,
Zweck und Wirkungsweise sind gleich.

So bleibt im wesentlichen als Zweck die einfache Umsetzung vorhandener MiNT-
bzw. UNIX- Programme, die Signale verwenden. Es gibt ja auch Leute, die meinen,
ein Ferrari ohne Autotelefon sei kein Ferrari.


3. Welche Signale gibt es ?
===========================

Im folgenden wird eine Aufstellung aller in MiNT definierten Signale gegeben,
ihr Verhalten unter MiNT und MagiC aufgefhrt (siehe unbedingt
auch MINT.DOC !!):


(0)	SIGNULL

"null".
Default: keiner.
Dieses Signal ist keines. Pkill() mit SIGNULL kann jedoch verwendet werden,
um die Gltigkeit einer Process-ID (z.B. eines Kindprozesses) zu testen.
Pkill() liefert nmlich entweder E_OK, wenn die ID gltig ist, sonst EFILNF.
Ansonsten wird das Senden von SIGNULL ignoriert, das Signal kann daher auch
nicht maskiert oder abgefangen werden.

(1)	SIGHUP

"hang up".
Default: Proze beenden.
Soll i.a. verschickt werden, wenn ein Terminal, mit dem ein Proze
kommuniziert, nicht mehr gltig ist. Nach Erhalt dieses Signals sollte ein
Proze keine Ein-/Ausgabe mehr auf das Terminal durchfhren.
In MagiC wird der VT52 dieses Signal in Zukunft verschicken, wenn ein
Terminalfenster geschlossen wurde.

(2)	SIGINT

"interrupt".
Default: Proze beenden.
Soll i.a. verschickt werden, wenn der Benutzer ^C eingibt.
Wird von MagiC in Zukunft statt der bisherigen ^C-Behandlung verwendet werden.

(3)	SIGQUIT

"quit".
Default: Proze beenden.
Soll i.a. verschickt werden, wenn der Benutzer ^\ eingibt. Soll "hrter" als
SIGINT sein.
Wird von MagiC/VT52 z.Zt. nicht verschickt.

(4)	SIGILL

"illegal instruction".
Default: Proze beenden.
Entspricht der 68k-Exception "illegal instruction". Dieses Signal sollte nicht
abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es erscheinen immer acht Bomben.

(5)	SIGTRAP

"trap".
Default: Proze beenden.
Entspricht dem 68k-Exceptionvektor "trace". Dieses Signal sollte nur von
Debuggern abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es erscheinen immer 9 Bomben.

(6)	SIGABRT

"abort".
Default: Proze beenden.
Wird i.a. von der Bibliotheksfunktion abort() verwendet und sollte
normalerweise nicht abgefangen werden.
Wird vom System selbst nicht verschickt.

(7)	SIGPRIV

"Privilege violation."
Default: Proze beenden.
Entspricht dem 68k-Exceptionvektor "privilege violation". Dieses Signal sollte
nicht abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es erscheinen immer 8 Bomben.

(8)	SIGFPE

"floating point exception".
Default: ignorieren.
Entspricht dem 68k-Exceptionvektor "Division durch 0". Dieses Signal kann
ignoriert oder abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es kann bisher normalerweise nie auftreten.
Sollte ich bald ndern.

(9)	SIGKILL

"kill".
Default: Proze beenden. Kann nicht maskiert/behandelt werden.
Wenn dieses Signal verschickt wird, wird der Proze beendet, ohne eine Chance
zum Aufrumen zu haben. Daher sollte SIGKILL erst verwendet werden, wenn
SIGTERM erfolglos war.
Wird von MagiC selbst z.Zt. nicht verschickt.

(10)	SIGBUS

"Bus error."
Default: Proze beenden.
Entspricht dem 68k-Exceptionvektor "Busfehler". Dieses Signal sollte nicht
ignoriert oder abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es erscheinen immer 2 Bomben.
In MiNT werden SIGBUS,SIGSEGV und SIGPRIV beim ersten Eintreffen des Signals
auf die Default-Routine des Systems zurckgesetzt, so da ein doppelter
Bus-/Adre-/Privilegfehler den Proze immer beendet.

(11)	SIGSEGV

"segmentation violation".
Default: Proze beenden.
Entspricht dem 68k-Exceptionvektor "Adrefehler". Dieses Signal sollte nicht
ignoriert oder abgefangen werden.
In MiNT kann man dieses Signal abfangen, der Exceptionvektor wird also
praktisch fr diesen Proze umgebogen. In MagiC geht das z.Zt. noch nicht,
d.h. es erscheinen immer 3 Bomben.

(12)	SIGSYS

"Bad system call."
Default: Proze beenden.
Wird von MagiC z.Zt. nicht verschickt.

(13)	SIGPIPE

"pipe error."
Default: Proze beenden.
Wird beim Versuch verschickt, auf eine nicht mehr existiernde Pipe zu
schreiben. Sollte beim Drag&Drop maskiert werden.
Wird z.Zt. von MagiC nicht verschickt. Das mu ich bald ndern.

(14)	SIGALRM

"alarm".
Default: Proze beenden.
Wird von MiNT fr Talarm() verwendet und dient z.B. zum Behandeln von
time outs.
Talarm() gibt es in MagiC z.Zt. noch nicht, so da MagiC dieses Signal bisher
auch nicht verschickt.

(15)	SIGTERM

"terminate".
Default: Proze beenden.
Normales Signal zum Beenden eines Prozesses. Wird von MiNT beim Lschen der
Programmdatei in u:\proc verschickt.
Wird von MagiC z.Zt. noch nicht verschickt, das mu ich aber bald ndern.

(16) gibt es nicht

(17)	SIGSTOP		17

"stop."
Default: Proze anhalten. Kann nicht blockiert/abgefangen werden.
In MagiC werden smtliche Threads eines Prozesses angehalten. Whrend des
"Stopped"-Zustandes gehen in MagiC keinerlei Mausklicks/Tasten usw.
verloren, beim Fortfhren des Prozesses ber SIGCONT werden die entsprechenden
Nachrichten ausgewertet.

(18)	SIGTSTP

"terminal stop".
Default: Proze anhalten.
Wie SIGSTOP, aber dieses Signal wird i.a. vom Benutzer durch Drcken von
^Z ausgelst und kann maskiert/abgefangen werden.
Wird von MagiC/VT52 z.Zt. nicht verschickt.

(19)	SIGCONT

"continue".
Default: Proze fortfhren. Kann nicht maskiert/ignoriert werden.
Ein angehaltener Proze (durch SIGSTOP oder auch durch Pause()) wird
fortgefhrt. Auch wenn das Signal nicht ignoriert werden kann, kann
eine Behandlungsroutine installiert werden.
In MagiC werden smtliche Threads aufgeweckt, die mit Pause() warten oder
per SIGSTOP o.. angehalten sind.

(20)	SIGCHLD

"child terminated."
Default: ignorieren
Wird sowohl beim Terminieren als auch beim Anhalten ("suspend") eines
Prozesses an den Elterproze verschickt. In MiNT kann man festlegen, da
nur beim Terminieren das Signal verschickt wird. Mit Pwait3() kann man in
MiNT feststellen, welcher Kindproze betroffen war.
Da MagiC z.Zt. nur das wartende Pexec() kennt (der Elterproze wartet solange,
bis der Kindproze terminiert ist), gibt es dieses Signal bisher nicht in
MagiC. Die durch den AES-Aufruf shel_write() erzeugten Prozesse sind keine
tatschlichen Kindprozesse, sondern vllig unabhngig. In MagiC mu man
daher auf die AES-Nachricht CH_EXIT warten, wenn man Programme parallel
gestartet hat.

(21)	SIGTTIN

"terminal input error".
Default: Proze anhalten
Ein Proze versucht von einem Terminal zu lesen, das ihm nicht gehrt, weil
er z.B. mit "pgm &" im Hintergrund gestartet wurde. => MINT.DOC.
Wird von MagiC/VT52 z.Zt. nicht verschickt.

(22)	SIGTTOU

"terminal output error".
Default: Proze anhalten
Wie (21), aber bei Ausgabe.

(23) gibt es nicht

(24)	SIGXCPU

"Exhaustion of CPU limit"
Default: Proze beenden.
Das per Psetlimit() bzw. durch die erweiterten shel_write()-Modi festgelegte
Kontingent von Rechenzeit ist abgelaufen.
Die Rechenzeitbegrenzung ist in MagiC bisher nicht implementiert, daher wird
dieses Signal vom System bisher nicht verschickt.

(28)	SIGWINC

"window changed"
Default: ignorieren
Die Terminalgre (d.h. die Anzahl Zeilen oder Spalten) hat sich (etwa durch
Verndern eines VT52-Fensters) verndert. Ein Programm, das z.B. mit Cursor-
steuerung arbeitet (ein Editor) mu sich entsprechend umkonfigurieren.
Zum Ermitteln der aktuellen Fenstergre gibt es in MiNT Fcntl()-Codes, die
bisher in MagiC nicht implementiert sind.
Es ist geplant da VT52 in Zukunft dieses Signal verschickt und da MagiC den
entsprechenden Fcntl()-Aufruf untersttzt. Bis dahin wird das Signal in
MagiC/VT52 nicht verschickt.

(29)	SIGUSR1
(30)	SIGUSR2

"User defined"
Default: Proze beenden.
Diese Signale drfen durch Benutzerprogramme verwendet werden. Da ein
Proze per Default beim Empfang dieser Signale beendet wird, sollte jedoch
nur dann ein Signal verschickt werden, wenn der Empfnger bekannt ist.


4. Welche Prozespezifischen Daten betreffen Signale ?
======================================================

Fr jeden Proze existiert unter MagiC genau eine Basepage, auch
PD (ProcessDescriptor) genannt. Unter MagiC 4.5 ist diese Basepage erweitert
um eine Struktur, die das Verhalten des Prozesses auf Signale definiert.

Jeder Proze hat:

Einen Bitvektor der wartenden Signale (pending signals)
-------------------------------------

Dieser Bitvektor beschreibt diejenigen Signale, die noch auf ihre
Bearbeitung warten, weil sie z.B. gerade durch die Signalmaske gesperrt sind
oder weil der Proze in einem Zustand ist, in dem er keine Signale bearbeiten
kann.

Eine Signalmaske
----------------

Dieser 32-Bit-Wert definiert als Bitvektor diejenigen Signale, die momentan
gesperrt sind. D.h. wenn Bit 30 gesetzt sind, ist das Signal SIGUSR2 gesperrt.
Die Signalmaske wird nicht nur explizit, d.h. durch Systemaufrufe, sondern
auch implizit durch andere Vorgnge beeinflut. Whrend der Abarbeitung eines
Signals ist dieses z.B. gesperrt, andere knnen jedoch auftreten. Nach der
Signalabarbeitung wird es wieder freigegeben und damit z.B. eine weitere
Signalbehandlung ausgelst.
Einige Signale (SIGKILL,SIGSTOP,SIGCONT) knnen nicht maskiert werden.

Eine Tabelle der Form "struct sigaction"
----------------------------------------

Fr jedes mgliche Signal (1..30) eine Struktur der Form

		typedef struct
		{
		        void    cdecl (*sa_handler)( long sig );
		        long    sa_mask;
		        int     sa_flags;
		} SIGACTION;

die bei PureC in TOS.H festgelegt ist. Fr SIGKILL kann jedoch der
Tabelleneintrag nicht gendert werden.
<sa_handler> ist entweder 0 (Default-Signal-Behandlung durch das System) oder
1 (Signal ignorieren), sonst die Adresse einer Signalbehandlungsroutine. Diese
wird im User-Modus ausgefhrt, bekommt auf dem Stack (hier fehlt bei TOS.H
das "cdecl" !!!) als LONG die Signalnummer.
<sa_mask> enthlt die zustzlich whrend der Signalbehandlung zu maskierenden
Signale (d.h. dann gilt mask = oldmask+(1<<sig)+sa_mask), sa_flags beeinflut
das Verhalten des Signals:
	SA_NOCLDSTOP (1)
		legt in MiNT fest, da SIGCHLD nur beim Terminieren, nicht beim
		Anhalten eines Kindprozesses ausgelst wird.
Andere Flags gibt es (noch) nicht.

Bei Pexec() werden Signalmaske und "pending"-Wert des Kindprozesses auf Null
gesetzt. <sa_handler> wird vererbt, wobei ein Wert ungleich 0 oder 1 immer auf
0 gesetzt wird. <sa_mask> und <sa_flags> werden auf Null gesetzt.


5. Welche Systemaufrufe beeinflussen Signale ?
==============================================

Nheres siehe MGX_DOS.TXT. Beachte auch THREADS.TXT zur besonderen Situation
von Signalen bei Programmen mit mehreren Threads.
Hier nur ein berblick der GEMDOS-Funktionen:

Signalbehandlung:
-----------------

LONG Psignal(WORD sig, void *action);
LONG Psigaction( WORD sig, struct sigaction *act,	struct sigaction *oact );

Rckkehr von Signalbehandlung:
------------------------------

LONG Psigreturn( void );

Signalmaske:
------------

LONG Psigblock( LONG mask );
LONG Psigsetmask( LONG mask );

Abfrage:
--------

LONG Psigpending( void );

Warten auf Signale:
-------------------

LONG Pause( void );
LONG Psigpause( LONG mask );

Signal verschicken:
-------------------

LONG Pkill( WORD pid, WORD sig );


6. Was darf ein Signalhandler ?
===============================

In MiNT darf ein Signalhandler keine AES-Aufrufe und keine VDI-Aufrufe
machen. In MagiC ist das natrlich kein Problem, weil AES und VDI keine
von zeichenorientierten UNIX-Freaks geduldeten Fremdkrper im System sind.

Zunchst einmal gibt es aber noch ein Problem mit den bisherigen Versionen des
VT52: Ein Signalhandler kann noch keine Tastaturabfrage im VT52 machen, die
Tasten werden nicht empfangen. Das mu sich jedoch bald ndern.

Ein Signalhandler luft im User-Modus und verwendet den User-Stack des
Haupt-Threads, der solange schlft. In MiNT wird der Supervisor-Stack
des Prozesses (es gibt in MiNT keine Threads) verwendet. Daher schiet
laut MINT.DOC die Verschachtelung von Signalen ab 4 Stck einen Proze wegen
Stapelberlaufs ab. In MagiC ist jeder Signalhandler ein eigener Thread mit
eigenem Supervisorstack, es kann also lediglich der extrem unwahrscheinliche
Fall des Userstack-berlaufs eintreten. Wenn jedoch zur Behandlung eines
Signals zuwenig Speicher fr einen neuen Thread zur Verfgung steht, wird
eine Alertbox "System hat keinen freien Speicher mehr" ausgegeben, und 
es sollte schleunigst ein Programm beendet werden. Fr jede Signalbehandlung
bentigt MagiC ca. 7k Speicher.

Es ist zu beachten, da das gleiche gilt wie bei Threads (->THREADS.TXT).
D.h. die entsprechenden Systembibliotheken mssen reentrant sein. Ein
Signalhandler ist ein Thread. Es kommt jedoch noch erschwerend hinzu, da der
Haupt-Thread whrend der Abarbeitung des Signals angehalten wird. Wenn also
der Haupt-Thread eine Semaphore (wind_update()!!) gesetzt hat, ensteht ein
Deadlock, wenn der Signalhandler diese Semaphore ebenfalls setzen will.
Weiterhin kann ein Proze in MagiC an beinahe beliebiger Stelle unterbrochen
werden (MagiC ist reentrant, sogar DOS ist unterbrechbar !!!), so da der
Haupt-Thread u.U. wichtige Bereiche des Systems sperrt (Dateien, Verzeichnisse,
Semaphoren). Daher kann es z.B. vorkommen, da bestimmte Dateien nicht
gelscht oder geffnet werden knnen.
Ggf. mu ich den Fall, in dem ein Proze durch ein Signal unterbrochen werden
kann, noch einschrnken, d.h. die Unterbrechung nur in bestimmten Situationen
zulassen.

Beim normalen Beenden eines Signalhandlers werden alle von dem Handler
gesperrten Semaphoren automatisch freigegeben. Weiterhin werden ggf. Fenster,
Bildschirmhintergrund und Menleiste des Signalhandlers freigegeben. Man
beachte, da ein Signalhandler eine eigene AES-message-queue hat, d.h. 
evnt_message() und appl_write() sind mit Vorsicht zu verwenden.

Bei Psigreturn() werden die Semaphoren aller Signalhandler als auch die des
Haupt-Thread freigegeben. Nicht jedoch werden Fenster/Bildschirmhintergrund/
Menleiste der Signalhandler freigegeben. Das wre zwar kein groes Problem
gewesen, drfte aber in der Praxis nicht notwendig sein. Psigreturn()
restauriert den Supervisor-Stack des Haupt-Thread, d.h. ein setjmp/longjmp-
Mechanismus braucht nur den usp zu setzen. Das ist in MINT.DOC nicht
ausreichend dokumentiert und funktioniert auch nur dann, wenn der Haupt-Thread
per GEMDOS-Aufruf wartet. Kommt AES ins Spiel, strzt MultiTOS ab. Psigreturn()
sollte daher nach Mglichkeit vermieden werden.
Psigreturn() ist in MagiC nicht "void" wie in MiNT sondern "LONG". Wenn
die Funktion von einem Nicht-Signalhandler aufgerufen wird, liefert
Psigreturn() EACCDN zurck, sonst E_OK. Hat Psigreturn() E_OK geliefert, sollte
(wie in MiNT auch nicht) auf keinen Fall die Behandlungs-Prozedur normal
per rts beendet werden, weil die Rcksprungadresse auf dem Userstack nach der
Ausfhrung von Psigreturn() ungltig ist.


7. Beispielprogramme
====================

Das erste Programm installiert einen Signalhandler fr die beiden
Signale SIGUSR1 und SIGUSR2. Damit kann man die Verschachtelung der
beiden Signale testen, wenn beide hintereinander verschickt werden.
Das Programm sollte im VT52 bzw. im MINIWIN laufen.
Wenn man whrend der "for()" Warteschleife Ctrl-Alt-Esc bettigt,
erkennt man, da MagiC fr jeden Signalhandler einen eigenen Thread
erstellt. Das hat den Vorteil, da unter MagiC keine Ereignisse
(Maus, Timer, ...) verlorengehen knnen, whrend ein Signal bearbeitet
wird.

------------------------------ schnipp --------------------------
#include <tos.h>
#include <stdio.h>

void cdecl handler(long signr)
{
	long i;

	printf("handler: Signal %ld empfangen.\n", signr);
	Cconws("warte...");
	for	(i = 0; i < 7000000L; i++)
		;
	Cconws("...OK\r\n");
}

int main( void )
{
	long ret;

	printf("Meine ProcID ist %d.\n", Pgetpid());
	ret = (long) Psignal(SIGUSR1, handler);
	printf("Psignal => %ld\n", ret);
	ret = (long) Psignal(SIGUSR2, handler);
	printf("Psignal => %ld\n", ret);
	Cconin();
	return(0);
}
------------------------------ schnipp --------------------------

Das zweite Programm zeigt die Behandlung des Psigreturn()-Aufrufs.
Unter MiNT funktioniert dieses Programm nur, wenn man keine AES-
Aufrufe (evnt_keybd() oder evnt_multi()) verwendet, sondern z.B.
Cconin() verwendet.
D.h. das folgende Programm funktioniert unter MagiC und strzt
unter MultiTOS (MiNT 1.08+AES 4.1) ab. Das Problem liegt wahrscheinlich
an der Restauration des Supervisor-Stacks (Systemstapelzeiger), dabei
versagt MultiTOS wegen des inhomogenen Konzepts AES<->MiNT.

------------------------------ schnipp --------------------------
#include <tos.h>
#include <aes.h>
#include <setjmp.h>
#include <tosdefs.h>
#include <stdio.h>

jmp_buf env;

void cdecl handler(long signr)
{
	printf("handler: Signal %ld empfangen.\n", signr);
	Cconws("Mache Psigreturn()\r\n");
	Psigreturn();
	longjmp(env, 1);
}

int main( void )
{
	long ssp;

	appl_init();
	printf("Meine ProcID ist %d.\n", Pgetpid());
	Psignal(SIGUSR1, handler);

	if	(setjmp(env))
		Cconws("komme von longjmp.\r\n");
	else	Cconws("komme von setjmp.\r\n");
	ssp = Super(0L);
	Super((void *) ssp);
	printf("ssp = 0%08lx\n", ssp);
	evnt_keybd();
	return(0);
}
