DAEMONS - MiNT-Hintergrundprozesse

Im folgenden sollen spezielle Features von MiNT verwendet werden, um ein Programm während des System-Startvorgangs auszuführen. Dazu gibt es die Datei MINT.CNF, in der (in der Regel TOS-) Programme eingetragen werden Der geneigte Leser mag nun ein wenden, daß der Ordner AUTO im Wurzel Verzeichnis des Boot-Laufwerks seit Jahren ein probates Mittel ist. Gerade in Verbindung mit MiNT tauchen aber Unschönheiten auf. Ändert sich durch Löschen oder Hinzukopieren die physikalische Reihenfolge der Dateien, so ist plötzlich nicht mehr gewährleistet, daß Programme auch wirklich nach MiNT gestartet werden. Und solche, die von den erweiterten Fähigkeiten MiNTs Gebrauch machen, mußten mühsam von Hand umbenannt werden, wenn mal ‘ohne’ gebootet werden soll. Hinzu kommt, daß man in der Datei MINT.CNF gleich Parameter spezifizieren kann, die an die auszuführende Anwendung weitergereicht werden. Also nur Vorteile?

Im Normalfall gibt es keine Möglichkeit, aus dem AUTO-Ordner heraus GEM-Programme zu starten, da zu dem Zeitpunkt des Programm Starts die AES in der Regel nicht aktiv sind. Die komfortablen Mechanismen MiNTs zur Prozeßsteuerung lassen uns jedoch einen ebenso einfachen wie sauberen (im Sinne von system-konformen) Ausweg. Aber die hier vorzustellende Vorgehensweise macht nicht nur im Zusammenhang mit AES-Aufrufen Sinn. Auch sogenannte Daemon-Programme (meist kleinere Utilities wie Drucker-Spooler, die nicht unbedingt AES-Aufrufe tätigen müssen) lassen sich so komfortabel in das System einbinden. Um die graue Theorie etwas auflockern zu können, wird dem geneigten Leser ein kleines Listing an die Hand gegeben. Der Zweck dieses Progrämmchens ist schnell erklärt. Es dient dazu, in der rechten oberen Ecke des Bildschirms den Status der Taste CapsLock darzustellen, stilisiert durch eine kleine LED - ein ausgefüllter Kreis, wenn die Taste gedrückt ist und ein leerer, falls nicht. Dieses Gerippe mag dann als Rüstzeug für weitere Experimente dienen. Der erste wichtige Funktionsaufruf ist Pvfork(). Er erzeugt einen neuen Prozeß, der allerdings genau den gleichen Speicher belegt wie der Aufrufer. Da dies wohl oder Übel zu Problemen führt, hält MiNT den Elternprozeß an. Die erste Aufgabe des Kind-Prozesses ist es nun, diesen durch die Pkill-Funktion zu terminieren. Dies ist nötig, weil die Routine in MiNT, die die Datei MINT.CNF analysiert und gegebenenfalls die angegebenen Programme startet, auf deren Beendigung wartet. Da es aber gerade die Aufgabe unseres Progrämmchens sein soll, ständig vorhanden zu sein würde MiNT ewig warten... Um die Aufgabe lösen zu können, muß das Kind sich die Prozeß-ID des Elternprozesses durch die Funktion Pgetppid() geben lassen. Da durch Pvfork() der Elternprozeß in allen bisherigen MiNT-Funktionen angehalten wird, ist die Endlosschleife, die er nach einer Rückkehr aus Pvfork ausführen würde, eine reine Vorsichtsmaßnahme. Der Aufruf Syield() dient nur dazu, Rechenzeit wiederabzugeben. Nach Ausführung des switch()-Konstruktes läuft also ein Prozeß ab, während MiNT normal mit dem Start weiterer Programme fortfährt, die ihrerseits natürlich das gleiche Spielchen treiben können... Was ist nun zu tun, wenn der Daemon-Prozeß AES-Aufrufe machen will? Hierfür installieren wir einen Signal Händler, den wir für die Funktionen Talarm() und Pause() brauchen. Signale sind im eigentlichen Sinne Unterbrechungen. Sie sollen uns aber nicht weiter interessieren. Wichtig zu wissen ist nur, daß die Funktion Talarm() einen MiNT-internen Timer startet. Wenn dieser abgelaufen ist, schickt der Kernel an uns das Signal SIGALRM. Und hierfür brauchen wir den Händler. Ist nämlich für ein Signal kein Händler installiert und wurden sonst keine Vorsichtsmaßnahmen ergriffen, wird MiNT die für jedes Signal festgelegte Standard-Aktion ausführen - und dies ist in der Regel das Terminieren des Prozesses (nicht gerade, was wir erreichen wollen). Auf dem Stack liegt bei Einsprung in den Signal-Handler ein long-Wert, der das Signal bezeichnet. Deshalb reicht eine Händler-Funktion, in der alle Signale abgefragt werden können. Mit Beendigung der Händler-Funktion beendet MiNT auch die Signalbehandlung. Jetzt kehrt auch der Pausei h Aufruf zurück. Anhand des global-Feldes kann man ersehen, ob die AES mittlerweile initialisiert wurden. Steht nämlich nach einem appl_init() in global [0] eine 0, sind die AES nicht verfügbar. Falls dies der Fall ist, beginnt das Talarm() / Pause()-Spiel von neuem... Wozu nun der ganze Aufwand? Bis Pause() zurückgekehrt ist, wird der Prozeß schlafen gelegt und braucht keine Rechenzeit. Dies bedeutet, daß unser Prozeß in regelmäßigen Intervallen prüfen kann, ob die AES mittlerweile verfügbar sind aber sonst die Performance des Systems nicht senkt.

Zu guter Letzt...

Zu beachten ist noch, daß eine Abfrage auf das Vorhandensein des MiNT-Cookies erfolgen sollte, um evtl, die Lauffähigkeit mit anderen TOS-Versionen sicherzustellen. Wird MiNT in Verbindung mit Ataris Multitasking AES verwendet, unterbleibt die Prozeßumschaltung für eine gewisse Zeit, weil MiNT Taskwechsel im Supervisor-Modus nicht zuläßt, die AES aber im Rahmen ihrer Initialisierung in eben diesen Modus wechseln. Man sieht, wie leicht sich Programme schreiben lassen, die im Rahmen der System-Initialisierung gestartet werden sollen. Die Arbeit für den Anwender endet schon nach dem Einträgen einer einzigen Zeile in der Datei MINT.CNF. Und selbst dies ließe sich durch entsprechende Tools verhindern. So schön nämlich die aus der Vergangenheit bekannten residenten Programme sind - sie lassen sich in den allerseltensten Fällen sauber aus dem Speicher entfernen. Daemon-Prozesse hingegen eröffnen neue Wege. Da sie normale Prozesse sind, lassen sie sich sauber und elegant entfernen. Tippen sie in einer Shell einfach 'kill -9 <pid' und weg isser. Und wer sich mit den Multitasking AES von Atari nicht anfreunden kann, hat mit ‘purem’ MiNT trotzdem viele Möglichkeiten... Es bleibt zu hoffen, daß alternative Multitasking-Umgebungen im TOS-Breich so bald wie möglich diese ebenso interessanten wie nützlichen Mechanismen der Prozeß-Steuerung zur Verfügung stellen.


/*
    (c)1994 by MAXON-Computer
    Autor: Thomas Künneth
    CAPS_LED
    Compiler: Pure C 1.1
    Datum: 10.10.1994
*/

/* Headerdateien */

#include <stdlib.h>
#include <tos.h>
#include <aes.h>
#include <vdi.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>

/* ab PureC 1.1 kann diese Zeile entfallen. 
Ältere PureC-Versionen benötigen diese Datei.
Sie liegt der Sourcecode-Distribution von MiNT bei */

#include <mintbind.h>

typedef enum { FALSE,TRUE } BOOLEAN ;

/* Makros */

#define CAPSLED1 "\n\r\033p CAPS_LBD \033q" 
#define CAPSLED2 "\n\r\xBD Th.Künneth, Röthenbach"

#define ERMEM "\n\rFehler: nicht genug Speicher für den Child-Prozeß"
#define NOMINT "\n\rFehler: MiNT nicht installiert - CAPSLED nicht lauffähig"

#define ENSMEM -39 /* insufficient memory * /

/* Funktionsprototypen */
void output(void);
void cdecl signal_handler(long which_signal); 
void cdecl simple_handler(long which_signal); 
BOOLEAN get_cookie(long cookie,long *value); 

/* globale Variablen */


int my_handle, work_out[57]; /* wird für v_opnvwk vq_extnd benötigt */


BOOLEAN abbruch; 
long MiNT_version;

void main(void) {
    int work_in[11], 
        aes_handle,
        apid,               /* AES-Kennung */
        i,
        dummy;
    /* Begrüßungstext ausgeben */ 
    Cconws(CAPSLED1);
    Cconws(CAPSLED2);
    /* ohne MiNT gebootet? - Fehlermeldung und Schluß */
    if (get_cookie('MiNT',&MiNT_version) == FALSE) {
        Cconws(NOMINT);
        exit(1);
    }
    /* eine Kopie des Prozesses anlegen */ 
    switch (Pvfork()) {
        case 0:
            /* so, wir sind also der Kind-Prozeß -> Elternprozeß killen */
            Pkill(Pgetppid(),SIGKILL);
            break; 
        case ENSMEM:
            /* nicht genung Speicher für den Child-Prozeß */
            Cconws(ERMEM);
            exit(1);
        default:
            /* i'm the parent - wating to be killed by my child :-( */
            while (TRUE) Syield();
    }
    /* jetzt müssen wir solange warten, bis die AES da sind */ 
    Psignal(SIGALRM,simple_handler);
    for (;;) {
        apid = appl_init();
        if (_GemParBlk.global[0] != 0)
            break;
        /* wir warten 4 Sekunden und hoffen, daß die AES beim nächsten Versuch da sind */
        Talarm(4);
        Pause();
    }
    /* hierher kommen wir erst, wenn die AES da sind oder ein Fehler aufgetreten ist */
    if (apid < 0)
        exit(1); /* da hat was nicht geklappt */ 
    aes_handle = graf handle(&dummy,&dummy,&dummy,&dummy);
    /* bei den AES verabschieden */ 
    appl_exit();
    for(i = 0; i < 10; work_in[i++] =1); 
    work_in[10] = 2;
    my_handle = aes_handle; 
    v_opnvwk(work_in, &my_handle,work_out); 
    if (!my_handle)
        exit(1);
    /* Schreibmodus auf REPLACE */ 
    vswr_mode(my_handle,MD_REPLACE); 
    /* Fülltyp deckend */ 
    vsf_interior(my_handle,FIS_SOLID); 
    /* keine Umrandung */ 
    vsf_perimeter(my_handle, 0);
    /* Handler für verschiedene Signale installieren */
    Psignal(SIGALRM,signal_handler);
    Psignal(SIGTERM,signal_handler);
    Psignal(SIGABRT,signal_handler);
    abbruch = FALSE;
    /* die Programm-Hauptschleife */
    while (abbruch != TRUE) {
        output();       /* die stilisierte LED malen */
        Talarm(1);      /* Timer auf 1 Sekunde stellen */
        Pause(); /* hier wird der Prozeß schlafen gelegt */
    }
    /* die Workstation schließen */ 
    v_c1svwk (my_handle);
    /* und ordnungsgemäß beenden */ 
    exit(0);
}

/* Signal-Handler */
void cdecl signal_handler(long which_signal) 
{
    switch ((int)which_signal) {
        case SIGALRM:
            /* unser mit Talarm() gesetzter 
            Timer ist abgelaufen; wir können 
            hier ggf. ein paar Aktionen
            tätigen. (X)BIOS- und GEMDOS-
            Calls sind möglich, AES-
            und VDI-Aufrufe sind zu unterlassen 
            */
            break; 
        case SIGABRT: 
        case SIGTERM:
            /* und tschüs... */ 
            abbruch = TRUE; 
            break;
    }
}

void output(void) 
{
    int x_pos, 
        pxyarray[8];
    vq_extnd (my_handle,0,work_out);
    x_pos = work_out[0]/
    /* Unrandung der CapsLock-Anzeige */
    vsl_color(my_handle,1); 
    /* Füllfarbe */
    vsf_color(my_handle,0);
    pxyarray[0] = (x_pos - 16); 
    pxyarray[1] = 0;
    pxyarray[2] = x_pos;
    pxyarray[3] = 10;
    v_bar(my_handle,pxyarray);
    /* Abfrage, welche Sondertaste 
    gedrückt wurde. Wenn in >main< das
    appl_exit() erst kurz vor Programm
    -Ende gemacht wird kann und
    sollte statt dem BIOS lieber das
    AES befragt werden */
    if (Kbshift(-1) & 16) {
        /* Füllfarbe rot */
        vsf_color (my_handle, 2);
        v_circle(my_handle,(x_pos - 4),4,4); 
        /* Füllfarbe */
        vsf_color(my_handle,0);
    }
    v_arc(my_handle,(x_pos - 4),4,4,0,3600); 
}
    
void cdecl simple_handler(long which_signal) 
{
    /* die Warning »Parameter
    which_signal' is never used in function 
    simple_handler« kann selbstverständlich 
    ignoriert werden */
} 
BOOLEAN get.cookie(long cookie,long *value) 
{
    long oldstack = Super(0L);
    long *cookiejar = *((long **)0x5a0L); 
    Super((void *)oldstack);
    if (cookiejar !=  NULL)
        do {
            if (cookiejar[0] == cookie) {
                if (value)
                    *value = cookiejar[1];
                return (TRUE);
            }
            else
                cookiejar = &(cookiejar[2]);
        }
        while (cookiejar[-2]);
    return (FALSE); 
}

Thomas Künneth
Aus: ST-Computer 01 / 1995, Seite 68

Links

Copyright-Bestimmungen: siehe Über diese Seite