Warum macht die ST-Computer eigentlich im Sommer einen Monat Pause? Nur, weil die Mitarbeiter auch einmal in Ruhe Urlaub machen wollen? Nein, nicht nur deswegen. Ein anderer Grund ist, daß der Sommer im Computerbereich normalerweise „nachrichtenarme" Zeit ist.
Nicht so dieses Jahr. Gleich zwei Hersteller drängen mit neuen ATARI-Emulatoren für PCs auf den Markt, um den bereits vorhandenen Alternativen „Janus", „Gemulator" und „STonX" Konkurrenz zu machen. Dabei hatte zunächst Application Systems Heidelberg mit „MagiC PC“ die Nase vorne, während „T0S2WIN" von aixit zum Redaktionsschluß noch auf sich warten ließ.
Kern von MagiC für PCs ist die Version 5.0, die mehr oder minder zeitgleich auch für Ataris und als MagiCMac ausgeliefert wurde. Neben der offensichtlichen Erweiterung des Dateisystemsauf Windows-95-kompatible lange Dateinamen gab es einige interne Erweiterungen, die in erster Linie der weiter ausgebauten MiNT-Kompa-tibilität zugute kommen.
So unterstützt MagiC 5 nun zumindest in eingeschränktem Maße Signale. Dabei fehlt zwar noch einiges an der MiNT-Funktiona-lität(sieheSIGWINCH), aber man kann immerhin schon mal die elementaren Signal-Handler installieren und die zugehörigen Signale verschicken. So läßt sich jetzt das Löschen eines Prozesses aus „u:\proc“ genau wie unter MiNT abfangen, indem man einen Händler für SIGTERM installiert.
Ebenfalls neu ist, daß MagiC nun verschiedene Möglichkeiten anbietet, um nebenläufige Prozesse innerhalb eines „Programms" zu implementieren. Im folgenden möchte ich eine Übersicht darüber geben, welche Mittel nun unter MiNT und MagiC verfügbar, und inwiefern sie untereinander kompatibel sind.
Der Systemaufruf Pfork() hat seinen Ursprung in klassischen Unix-Implementationen und hat so seinen Weg auch in MiNT (und nur dahin) gefunden. So komisch es klingen mag: einen Aufruf wie Pexec() (also Programm aus einer Programmdatei starten und auf das Ergebnis warten) gibt es in einem „normalen" Unix-System überhaupt nicht.
Statt dessen ist die nötige Funktionalität aufgeteilt: mit fork() erzeugt man einen zweiten Unix-Prozeß, der praktisch eine Kopie des Originalprozesses ist und sich nur darin unterscheidet, daß fork() im neu erzeugten Prozeß mit einem anderen Return-Wert zu rückkehrt als beim Aufrufer. Anschließend kann man nun mit exec() diesen Prozeß durch eine Programmdatei „überlagern“.
Daß ein solches Vorgehen überhaupt möglich ist, liegt an der Tatsache, daß Unix-Systeme normalerweise auf Hardware mit PMMU (Paged Memory Management Unit) laufen und jeder Prozeß seinen eigenen Adreßraum hat. Das „Verdoppeln" eines Prozesses kann daher zum größten Teil von der PMMU erledigt werden.
Unter MiNT sieht es anders aus, da es eine PMMU ja nur bei Falcon und TT gibt. Daher wird bei Pfork() der Aufrufer schlafengelegt, bis der neu erzeugte Prozeß entweder beendet wurde oder das MiNT-Äquivalent von execQ (Pexec-Modus 200) aufgerufen hat. Damit läßt sich zumindest die in Unix-Sourcen häufig benutzte Kombination forkQ/execQ brauchbar nachbilden.
Was aber, wenn dies nicht reicht? Nun, dann muß man sich eine gepatchte Version von FreeMiNT 1.12h5 besorgen (oder aber auf das nächste „offizielle" Release warten). Mit dieser Version laufen beide Prozesse weiter, allerdings mit einem Preis: da beide den gleichen Adreßraum belegen und keine PMMU benutzt wird, müssen bei jedem Kontext-Switch die entsprechenden Speicherbereiche ausgetauscht werden (Swapping). Das kostet natürlich Zeit.
Fazit: in der Regel ist Pfork() nur dann nützlich, wenn man Unix-Sourcen mit fork()/exec() portieren will.
Und wozu ist dann Pvfork() gut? Auch diese Funktion bildet eine Unix-Funktion nach. Spec 1170 (dies ist mehr oder minder der „offizielle“ Unix-Standard) definiert vfork() ähnlich wie MiNT: der neu erzeugte Prozeß darf nichts anderes tun als wieder zu terminieren oder per Pexec() eine neue Programmdatei zu starten. Auch für viele Unix-Systeme gilt: wenn man dies schon vorher weiß, kann sich der Kernel sehr viele überflüssige Verwaltungsaufgaben einsparen.
Wer tatsächlich Pvfork() oder die aktuelle Implementation von Pfork() benutzen will, muß übrigens sehr vorsichtig sein: der neue Prozeß läuft insbesondere mit demselben User-Stack. Daher braucht man schon ein besonderes Binding, um den Aufruf überhaupt benutzen zu können (der in der PureC-Library ist ungeeignet). Eine funktionstüchtige Implementation findet sich in der MiNT-Library (wo sonst?).
1: /*
2: thread.c
3: (c) 1996 MAXON Computer
4: Autor: Julian 7. Reschke, 29. Mai 1996
5: */
6:
7: #include <tos.h>
8:
9: typedef int threadfun (long arg);
10:
11: static vold cdecl
12i init_functIon (BASPAG *P)
13: {
14: threadfun *fun = (threadfun *) P->p_dlen;
15:
16: Pterm (fun (P->p_blen)) ;
17: }
18:
19: long
20: DOThread (threadfun *fun, long arg, long stksize, char *name)
21: {
22: BASPAG *b;
23:
24: b = (BASPAG *) Pexec (5, 0L, "", 0L);
25: Mshrink (0, b, stksize + sizeof (BASPAG));
26: b->p_tbase = init_function;
27: b->p_tlen = stksize + sizeof (BASPAG);
28: b->p_blen = arg;
29: b->p_dlen = (long)fun;
30: b->p_hitpa = (char *)b + stksize + sizeof (BASPAG);
31: return Pexec (104, name, b, 0);
32: }
MiNT bietet eine weitere Möglichkeit, einen nebenläufigen Prozeß zu erzeugen, und MagiC 5.0 ist nun dazu kompatibel. Mittels des schon immer vorhandenen Pexec-Modus „Create Basepage" wird ein neuer GEMDOS-Prozeß vorbereitet. Mit Modus 104 wird er nun gestartet und läuft von nun an parallel zum Aufrufer. Da man bei dieser Methode ohnehin einen neuen Prozeß-Stack angeben muß, sind die bei Pv-fork() erwähnten Probleme schon einmal ausgeschlossen.
Der neue Prozeß ist ein vollwertiger GEMDOS-Prozeß, der zusätzlichen eigenen Speicher allozieren und Dateien öffnen kann, eigene Signals besitzt usw. Wenn allerdings GEM-Funktionen benutzt werden sollen, muß auch dieser Prozeß erst einmal appl_init() aufrufen.
Aber Vorsicht: der neu erzeugte Prozeß teilt sich noch immer sämtlichen Speicher mit dem Aufrufer (sofern dieser zum BSS-oder Datensegment gehört oder vor der Erzeugung des Prozesses alloziert worden war). Daher verbietet sich die Benutzung aller Library-Funktionen, die entweder nicht vollständig reentrant sind oder gar statischen Speicher benutzen. Kandidaten aus der C-Library sind strtokQ, aber auch mallocf). Betroffen sind aber auch alle GEMDOS-Bindings aus der PureC-Library, und generell GEM-Bindings (weil sie in der Regel über statische Parameterblöcke implementiert sind).
Wie kann man nun derart gestartete Prozesse nutzen? Ein Standardbeispiel ist die Mupfel, die Shellscripts auch im Background ausführen kann. Dazu mußten sämtliche Funktionen vollständig reentrant implementiert und auf sämtliche globale Variablen verzichtet werden. Eine Betaversion der Mupfel, die dies nun auch unter MagiC unterstützt, liegt in der Maus MS2 (mupfbla2.zip, 0251/77262) und im Internet (http://wwwmath.uni-muenster.de/~reschke) bereit.
Eine weitere mögliche Anwendung kann man sich leicht im Netscape-Web-Browser ansehen (leider nicht auf dem ATARI): während sich mehrere Hintergrundprozesse um die Internet-Kommunikation kümmern, bleibt das Userinterface immer ansprechbar.
Abbildung 1 zeigt eine C-Funktion, die das Starten mittels Pexec 104 so einfach wie möglich macht. Man braucht nur den Namen der Hauptfunktion, die gewünschte Stack-Größe, einen optionalen Parameter sowie den neuen Prozeßnamen zu übergeben, und das war’s auch schon.
MagiC 5.0 bietet außerdem noch eine weitere Möglichkeit über neue AES-Funktionen. Im Gegensatz zu den oben beschriebenen Methoden benutzen dabei auch die neu erzeugten „Programmfäden“ denselben GEMDOS-Prozeß (Sharing von Filehandles, Signals etc.). Die Dokumentation hierzu ist über die ASH-Mailbox verfügbar. Auch an neue GEM-Libraries für PureC (ohne Reentrance-Prob lerne) wurde dabei gedacht.