next up previous contents index
Next: Ein-/Ausgabe auf dem Bildschirm Up: L3 Referenzhandbuch Previous: Textdateien

Datenräume

Einführung

Datenräume bilden die oberste und allgemeinste Klasse für Objekte, die gespeichert werden. Der Typ DATASPACE bezeichnet Objekte vom Typ Datenraum, ein elementarer Datentyp des L3-Systemkerns. Datenräume können Daten beliebigen Typs aufnehmen und gewähren direkten Zugriff auf ihren Inhalt. Ein Datenraum ist als Container für Daten aufzufassen, es sind zunächst keine Annahmen über den Inhalt (Texte, ausführbarer Code oder vielleicht eine Datenbank?) nötig. Ein Datenraum ist einfach ein Stück linearer Speicher, das bis zu einem Gigabyte groß werden kann.

Datenräume sind lokale Objekte in den Tasks eines L3 Systems, oder einfacher gesagt, jeder Datenraum hat genau eine Besitzertask. Eine Task kann bis zu 16380 Datenräume besitzen. Da der dafür theoretisch erforderliche Adreßraum 16 Terabyte beträgt, der Prozessor jedoch nur 4 Gigabyte pro Task adressieren kann, muß noch eine Abbildung der aktuell angesprochenen Datenräume in den virtuellen Adreßraum der Task erfolgen. Diese Abbildung heißt Mapping. Das Mapping erfolgt in Segmenten zu 16 MB. Bis zu 234 dieser 16 MB Segmente können gleichzeitig »gemappt« sein, die restlichen 22 Segmente werden intern beansprucht. Da Datenobjekte meistens kleiner als 16 MB sind, bedeutet das, daß gleichzeitig 234 »Dateien geöffnet« sein können. Falls Datenräume größer als 16 MB sind, verringert sich die Anzahl der möglichen Mappings entsprechend, da ein solch großer Datenraum dann zwei oder mehr nebeneinanderliegende Einträge in der Mappingliste beansprucht. Falls sehr große Datenräume gemappt werden sollen, sollten sie sofort nach Erzeugung der Task gemappt werden, da andernfalls die Gefahr besteht, daß sich in der Mappingliste keine hinreichend große »Lücke« mehr findet. Mapping- Operationen sind implizit in den üblichen Datenraumoperationen und darauf aufbauenden enthalten. Beispielsweise ist nach einem edit auf eine neue Datei der Datenraum anschließend gemappt. Explizites Mapping per open und close ist nur dann erforderlich, wenn sehr viele (große) Datenräume gleichzeitig direkt adressiert werden müssen.

Die Datenräume einer Task sind in der Reihenfolge ihrer Erschaffung, bzw. ihres Empfangs, durchnumeriert. Dieser Datenraumindex kann zum Durchlaufen aller Datenräume einer Task benutzt werden. Datenraumindices, die nach Löschen eines Datenraums frei sind, können wiederbenutzt werden. Der Index kann also nicht zur Identifikation benutzt werden.

Bei der Beschreibung von Operationen auf Datenräumen sind drei Zugriffsebenen zu unterscheiden. Zunächst gibt es Operationen, die für den Datentyp DATASPACE Initialisierung, Zuweisungsprozeduren und dergleichen anbieten. Auf dieser Ebene ist ein Datenraum ein atomares Objekt. Er wird in seiner Ganzheit erzeugt und gehandhabt. Diese Ebene wird z.B. von der Intertask Kommunikation benutzt. Wie in Kapitel 8 beschrieben, ist es möglich einen oder mehrere Datenräume in einem atomaren Botschaftstransfer zwischen Sender- und Empfängertask zu übertragen.

Die nächste Zugriffsebene betrifft die Organisation eines Datenraums. Er ist ein Objekt, das aus einer Anzahl von »Pages« im virtuellen Speicher besteht. Diese technische Struktur eines Datenraums kann Angaben über benötigten Speicherplatz liefern und wird insbesondere für blockorientierte Schreib- und Leseoperationen, z.B. auf Diskette, benutzt.

Die dritte Zugriffsebene betrifft den korrekten Zugriff auf den Datenrauminhalt. Diese innere Struktur wird einem Datenraum von der konkreten Anwendung »aufgeprägt«.

Durch das Schlüsselwort BOUND bei der Deklaration einer Variablen wird in einem ELAN-Programm erreicht, daß diese Variable nicht im Standarddatenraum, sondern im jeweils angekoppelten Datenraum abgelegt wird.

Da die innere Struktur eines Datenraums anwendungsabhängig ist, können an dieser Stelle keine Aussagen über Möglichkeiten des Zugriffs auf diese Struktur gemacht werden. Ein Datenraum kann eine Typnummer erhalten. Durch Test dieser Typnummer kann dann festgestellt werden, ob die passenden Zugriffswerkzeuge zur Verfügung stehen.

Eine gewisse Sonderbehandlung erfahren TEXTE in Datenräumen. Texte werden als dynamische Strukturen in einem Textheap gespeichert. Die Mindestgröße dieses Textheaps kann mit der Prozedur set min heap size vorgegeben werden. Siehe dazu auch Kap 2.3: Aufbau des Textheap in anderen Datenräumen.

Koppelung von Datenräumen

Eine ständig innerhalb des L3 Systems verwendete Technik ist die Koppelung von Datenräumen durch faules Kopieren. Die Überlegung dabei ist, daß die virtuelle Speicherverwaltung ermöglicht, Kopieroperationen zunächst in Bezug auf das eigentliche Datenobjekt (den Datenraum) zu unterdrücken.

Dazu zunächst ein Blick auf die in dieser Erörterung aus Gründen der Verständlichkeit vereinfacht dargestellte, technische Organisation eines Datenraums. Sie läßt sich vorstellen als ein Zeiger auf eine Seitenblocktabelle, die Einträge dieser Tabelle verweisen auf die Blöcke auf dem Hintergrundspeicher (Festplatte). Durch eine von der MMU (Memory Management Unit) des 80386 unterstützte Adreßumsetzung erhält man so ein wahlfrei adressierbares Datenobjekt.

                         Seite p1    ----->    Block a (4096 Byte) 
DATASPACEA Ptr   --->    Seite p2    ----->    Block b    " 
                         Seite p3    ----->    Block c    " 
                         ...

Um eine Kopie dieses Datenraums zu erhalten, reicht es nun zunächst aus, einen zweiten Verweis auf die Seitenblocktabelle zu erzeugen und zu vermerken, daß der Datenraum gekoppelt ist.

                         Seite p1    ----->    Block a (4096 Byte) 
DATASPACEA Ptr  ---->    Seite p2    ----->    Block b    " 
DATASPACEB Ptr  ---->    Seite p3    ----->    Block c    " 
                         ...

Da die (internen) Datenraumnamen innerhalb des Gesamtsystems eindeutig sind, wird diese Kopiertechnik sowohl tasklokal, als auch systemweit benutzt.

Erst wenn ein Schreibzugriff stattfindet, müssen die betroffenen Datenraumseiten tatsächlich kopiert werden.

                         Seite p1    ----> |-->  Block a (4096 Byte) 
DATASPACEA Ptr   --->    Seite p2    ----> ||->  Block b    " 
                         Seite p3    ----> ||    Block c    " 
                                           || 
                         Seite p1    ------|| 
DSCOPY Ptr      --->     Seite p2    -------| 
                         Seite p3    --------->  Block d    " 
                                    (neu)

Dieses Verfahren erlaubt das Kopieren großer Datenmengen, ohne daß nennenswerte Datenmengen bewegt werden. Insbesondere im L3 Systemmantel ist diese Technik gut erkennbar. Das Tasksystem besteht von Beginn an aus mehreren Tasks, die jede logisch über 3MB Speicherplatz einnehmen. Da die Standarddatenräume der Tasks, die diesen Platz beanspruchen, sich jedoch nicht oder nur geringfügig unterscheiden, beträgt der Gesamtplatzbedarf eines frischen Systems nur etwa das Doppelte der Standarddatenraumgröße, plus ca. 1,5 MB für den L3- Systemkern und Treiberprozesse.

Datenraumoperationen

tabular3942

:=

OP := (DATASPACE VAR dest, DATASPACE CONST source)

Wirkung: Der Datenraum 'dest' wird als Kopie von 'source' angelegt.
Bemerkung: Es handelt sich um eine logische Kopie, eine (teilweise) physische Kopie wird erst nach einem Schreibzugriff auf einen der beiden Datenräume nötig.

nilspace

DATASPACE PROC nilspace

Wirkung: Liefert den leeren Datenraum nilspace, der ausschließlich als Quelle zum Kopieren verwendet werden darf.

copy

PROC copy (DATASPACE CONST source, DATASPACE VAR dest)

Wirkung: Der Datenraum 'dest' wird als Kopie von 'source' angelegt.

PROC copy (DATASPACE CONST source, dest, INT CONST from page, to page, pages)

Wirkung: Aus dem Datenraum 'source' werden 'pages' Seiten ab Seite 'from page' in den Datenraum 'dest' beginnend ab Seite 'to page' kopiert.
Bemerkung: Es handelt sich hier um logische Kopien, eine (teilweise) physische Kopie wird erst nach einem Schreibzugriff auf einen der beiden Datenräume nötig.

PROC copy (DATASPACE CONST ds, INT CONST from page, to page, pages)

Wirkung: Innerhalb des Datenraums 'ds' werden 'pages' Seiten ab Seite 'from page' beginnend ab Seite 'to page' kopiert.

Bemerkung: Es handelt sich (natürlich) um »faule Kopien«. Die Seiten werden nicht entkoppelt.

close

PROC close (DATASPACE CONST ds)

Wirkung: Die Abbildung des Datenraums 'ds' in den Adreßraum der Task wird explizit beendet.

dataspace

DATASPACE PROC dataspace (TEXT CONST name)

Wirkung: Liefert den Datenraum, der mit dem Namen 'name' assoziiert ist.
Beispiel:
create ("matrix4D", ds) ; 
 
TYPE DA   = STRUCT (ROW 25 INT a), 
     DB   = STRUCT (ROW 25 DA b), 
     DC   = STRUCT (ROW 25 DB c), 
     DD   = ROW 25 DC; 
 
BOUND DD VAR sp := dataspace ("matrix4D") ; 
 
INT VAR i,j,k,l; 
 
FOR i FROM 1 UPTO 05 
REP 
   FOR j FROM 1 UPTO 05 
   REP 
      FOR k FROM 1 UPTO 05 
      REP 
         FOR l FROM 1 UPTO 05 
         REP 
         sp[i].c[j].b[k].a[l] := i * j * k * l; 
         PER; 
      PER; 
  PER; 
PER;
dataspaces

INT PROC dataspaces

Wirkung: Liefert die Anzahl aller (benannten und unbenannten) Datenräume der Task.

INT PROC dataspaces(TASK CONST task)

Wirkung: Liefert die Anzahl aller (benannten und unbenannten) Datenräume der Task 'task'.
Bemerkung: Die größtmögliche Anzahl der Datenräume einer Task beträgt (zur Zeit) 16383.

delete

PROC delete (DATASPACE VAR ds)

Wirkung: Der Datenraum 'ds' wird gelöscht.

PROC delete (DATASPACE CONST ds, INT CONST from page, pages)

Wirkung: Im Datemraum 'ds' werden beginnend ab Seite 'from page' 'pages' Seiten gelöscht.
Bemerkung: Durch diese Prozedur steht nicht unmittelbar mehr freier Speicherplatz auf der Festplatte zur Verfügung. Die physische Räumung von Speicherplatz erfolgt erst durch die »Müllabfuhr«.

exists

BOOL PROC exists (DATASPACE CONST ds)

Wirkung: Liefert 'TRUE', falls der Datenraum 'ds' im Adressraum der Task existiert, sonst 'FALSE'.

move

PROC move (DATASPACE VAR source, dest)

Wirkung: Kopiert den Datenraum 'source' nach 'dest' und löscht 'source'.

next

DATASPACE PROC next (DATASPACE CONST ds)

Wirkung: Liefert den auf 'ds' folgenden Datenraum der Task. Die vorhandenen Datenräume können mit dieser Prozedur zyklisch abgerufen werden. Es gilt: next (nilspace) liefert den Standarddatenraum, next ('last ds') liefert nilspace.

INT PROC next (DATASPACE CONST ds, INT CONST pageno)

Wirkung: Liefert die nächste belegte Seite des Datenraums 'ds'. Falls 'pageno' = -1 ist, wird die erste belegte Seite geliefert, gibt es keine belegte Seite mehr, so wird -1 geliefert.
Bemerkung: In den Seitennummern können Lücken sein.

index

INT PROC index (DATASPACE CONST ds)

Wirkung: Liefert den Index des Datenraums 'ds'. Es gilt: index (next (nilspace)) = 1 Der Standarddatenraum einer Task hat den Index 1.

pages

INT PROC pages (DATASPACE CONST ds)

Wirkung: Liefert die Anzahl der durch 'ds' belegten Seiten (je 4096 Byte).

page size

INT PROC page size

Wirkung: Liefert die Größe einer Seite eines Datenraums in Byte.

set min heap size

PROC set min heapsize (DATASPACE CONST ds, INT CONST new limit)

Wirkung: Die Größe des Textheapbereiches in dem Datenraum wird verändert. Standardmäßig werden vom Laufzeitsystem mindestens 10 MB eines Datenraums als Textheap reserviert, unabhängig davon, ob die zu speichernde Struktur TEXTe enthält, oder nicht. Der tatsächlich zur Verfügung stehende Platz errechnet sich aus der Differenz der Größe des statischen Teils eines Datenobjekts zum nächstgrößen Vielfachen von 16 MB. Falls der statische Teil so viel Platz einnimmt, daß weniger als 10 MB für den Textheap zur Verfügung stehen, wird ein weiteres Datenraumsegment allokiert. Die Angabe 'new limit' erfolgt in Bytes, es findet keine Überprüfung dieses Werts auf eine sinnvolle Größe statt. Generell braucht man natürlich nur dann Überlegungen zu diesem Thema anzustellen, wenn man mit großen Datenobjekten agiert!
Beispiel:
  1. Der Datenraum soll ROW 3 000 000 INT aufnehmen. Da kein Text benötigt wird, kann erreicht werden, daß der Datenraum auf ein Segment von 16 MB beschränkt bleibt, indem der das Datenobjekt an eine Datenraumvariable gekoppelt wird, für die kein Textheap vereinbart wurde.
    create ("hugo", ds); 
    set min heap size (dataspace ("hugo"), 0); 
    TYPE MI = ROW 3 000 000 INT; 
    BOUND MI VAR mio := dataspace("hugo") ; 
    INT VAR i; 
    FOR i FROM 1 UPTO 3 000 000 
      REP 
         mio [i] := i 
      PER ;
  2. Der Datenraum soll ROW 40 000 TEXT aufnehmen. Es ist abzusehen, daß die Texte durchaus bis 1000 Byte lang werden. Die Heapgröße muß in diesem Fall auf (sicherheitshalber) 45 000 000 hochgesetzt werden:
    create ("texte satt", ds); 
    set min heap size (dataspace ("texte satt"), 45000000); 
    TYPE TH = ROW 40 000 000 TEXT; 
    BOUND TH VAR thd := dataspace("texte satt") ; ...
  3. Der Datenraum enthält eine ROW 1000 INT. Offensichtlich würde genügend Platz für den Textheap innerhalb 16 MB zur Verfügung stehen. Man braucht sich keine Gedanken um eine Einschränkung zu machen.

storage

INT PROC storage (DATASPACE CONST ds)

Wirkung: Liefert den von 'ds' belegten Speicherplatz. Die Angabe erfolgt in Bytes. Das Ergebnis ist jedoch immer ohne Rest durch 4096 teilbar, da es aus der Anzahl der Seiten des Datenraums errechnet wird.

type

PROC type (DATASPACE CONST ds, INT CONST typ)

Wirkung: Der Datenraum 'ds' erhält die Typnummer 'typ'. Die Typnummer muß eine positive Zahl sein.

INT PROC type (DATASPACE CONST ds)

Wirkung: Liefert die Typnummer des Datenraums 'ds'.
Bemerkung: Falls dem Datenraum 'ds' eine Typnummer zugewiesen wurde, wird diese geliefert. Wurde 'ds' keine Typnummer zugewiesen gilt folgendes:

type ('ds') < 0 falls 'ds' nie an ein BOUND Objekt angekoppelt war,
type ('ds') = 0 falls 'ds' schon an ein BOUND Objekt angekoppelt war.

Datenräume in ELAN-Programmen

Durch das Schlüsselwort BOUND bei der Deklaration einer Variablen teilt man dem ELAN-Compiler mit, daß die Werte dieser Variablen in einem Datenraum gespeichert werden. Das Ablegen von Datenobjekten in Datenräumen kann aus verschiedenen Gründen notwendig bzw. sinnvoll sein:

Beispiel für die Verwendung eines unbenannten Datenraums:

BOUND ROW 10000 REAL VAR liste; 
(* 'liste' soll in einem Datenraum abgelegt werden          *) 
DATASPACE VAR ds :: nilspace; 
(* erzeugen des Datenraums 'ds' als Kopie des nilspace      *) 
liste := ds; 
(* 'liste' wird an den Datenraum 'ds' angekoppelt           *) 
bearbeite liste; 
(* Das Datenobjekt 'liste' wird bearbeitet                  *) 
copy (ds, public, "Liste"); 
(* Der Datenraum 'ds' wird unter dem Namen "Liste" in der 
   Task "PUBLIC" gesichert                                  *) 
delete (ds); 
(* nicht vergessen, sonst wird der belegte Platz nicht 
   wieder freigegeben, bevor die Task gelöscht wird         *) 
. bearbeite liste: 
  liste [314] := pi;

Beispiel für die Verwendung eines benannten, von der Dateiverwaltung der Task verwalteten, Datenraums:

BOUND ROW 10000 STRUCT (REAL wert, INT position) VAR liste; 
(* 'liste' soll in einem Datenraum abgelegt werden          *) 
create ("liste2") ; 
liste := dataspace ("liste 2"); 
(* 'liste' wird an einen neuen Datenraum angekoppelt. Dieser 
   Datenraum wird unter dem Namen "liste 2" von der Dateiver- 
   waltung der Task verwaltet                               *) 
bearbeite liste; 
(* Das Datenobjekt 'liste' wird irgendwie bearbeitet        *) 
programmende;

Mapping

In den bislang erörterten Prozeduren wurde zumindestens implizit vorausgesetzt, daß ein Datenraum ein Datencontainer ist. Er wird als Speicherobjekt für eine Datenstruktur aufgefaßt. Der Zugriff auf den Inhalt des Datenraums setzt Kenntnis der inneren Struktur voraus. Es kann jedoch auch notwendig sein, ohne Beachtung der inneren Struktur auf den Inhalt eines Datenraums zuzugreifen. Beispielsweise wird diese Möglichkeit benutzt, um in der Task hardware configurator in eine (Binär-)datei, die zum Standarddatenraum eines Treibers wird, die vom Benutzer eingestellte Interruptnummer, Portadresse und dergleichen einzutragen. Der in dieser Datei enthaltene ausführbare Code kann schwerlich als Datenstruktur betrachtet werden, nichtdestotrotz müssen Schreib/Leseoperationen in solchen Datenräumen möglich sein.

Für derartige Zwecke gibt es den Datentyp BOUNDPTR. Variablen dieses Typs liefern für einen gemappten Datenraum eine Basisadresse, die, durch ein Offset ergänzt, Zugriff auf den Inhalt des Datenraums ermöglicht.

BOUNDPTR

TYPE BOUNDPTR

Wirkung: Typ für Variablen die Mapping eines Datenraums erlauben, der nicht über BOUND Variable angekoppelt werden soll oder kann.

address

REF INT PROC address (BOUNDPTR CONST boundptr)

Wirkung: Liefert die Mappingadresse.

close

PROC close (BOUNDPTR VAR bound ptr)

Wirkung: Das Mapping des mit 'bound ptr' assoziierten Datenraum wird beendet.

dataspace

DATASPACE PROC dataspace (BOUNDPTR CONST boundptr)

Wirkung: Liefert den durch 'boundptr' adressierten Datenraum.

open

PROC open (DATASPACE CONST space, INT CONST map size BOUNDPTR VAR bound ptr)

Wirkung: Mappt den Datenraum 'space' in der (in Byte) angegebenen Größe. Die Variable 'bound ptr' muß solange gültig sein, wie das Mapping Bestand haben soll.

Beispiel:
LET mb16 = 0x 0100 0000 , 
    config area = 0x80  ; 
BOUNDPTR VAR std space ; 
.... 
open (dataspace("stdds"), mb16, std space); 
... 
drivers ip := VAL (REF INT [address (std space) 
                            + config area]) ;

Soll sagen: die Startadresse für die Aktivierung des Treibers, also der Anfangswert seines Befehlszählers (drivers ip) wird aus dem Konfigurationsbereich des Programms gelesen. Dieses Programmsegment ist aus dem "install" Programm zu einem Treiber abgeleitet. Es zeigt ganz nebenbei, daß auch die Benutzung von BOUNDPTRn Kenntnisse über die innere Struktur des Datenraums erfordert, da ohne nähere Kenntnisse über die config area auch ein BOUNDPTR nur zum Herumstochern taugte.

Der Datentyp TASK

Einführung

Prozesse und damit insbesondere auch Tasks sind die einzig globalen Objekte in einem L3 System. Es muß für jeden Prozeß eine eindeutige Benennung im System geben. Insbesondere müssen Prozesse auch über ihre Lebensdauer hinaus eindeutig identifizierbar sein. Wäre diese Sicherheit nicht gegeben, bestünde z.B. die Gefahr, daß nach Löschen einer Task durch ihren Besitzer ein anderer Benutzer im System eine gleichnamige Task einrichtet und zum Empfänger von Daten wird, die nicht für ihn bestimmt sind.

Die Sicherheit der Benennung wird bei L3 durch vom Systemkern vergebene interne Taskbezeichner erreicht. Ein solcher Bezeichner besteht aus einem Taskindex und einer Generationsnummer. Der Index ist der zeitinvariante Teil des internen Taskbezeichners. Durch Kombination des Index mit einer Generationsnummer ist sichergestellt, daß Tasks auch über die Zeit eindeutig identifizierbar sind. Bei erneuter Vergabe des Index wird die Generationsnummer hochgezählt, so daß auch bei »alten« Systemen die Eindeutigkeit der Taskbezeichner sicher ist.

Diese Taskbezeichner können mit Hilfe von Taskvariablen benutzt werden. Bei benannten Tasks kann der Bezeichner durch den Operator / bzw. die Prozedur task erhalten werden. Unbenannte Tasks, die im Taskbaum mit ihrem »Pseudonamen '-'« ausgewiesen sind, können über Verwandschaftsbeziehungen erreicht werden.

Für einige standardmäßig in einem L3 System vorhandene Tasks und einige der an sich unbenannten Realprozesse gibt es vereinfachende Schreibweisen bzw. Pseudonamen, die die Identifizierung vereinfachen.

Das gesamte Tasksystem wird von der Task SUPERVISOR verwaltet. Der SUPERVISOR kann als einzige Task im System spezielle Verwaltungsoperationen auf Objekten vom Typ Task ausführen. Erzeugen, Anhalten und Löschen einer Task geschehen also stets durch Aufträge an die Task SUPERVISOR. Informationen über den Aufbau des Systems, also Namen und Vater/Sohn Beziehungen der Tasks verwaltet der SUPERVISOR im Systemkatalog. Falls eine Task Informationen aus dem Systemkatalog benötigt, kann sie durch access catalogue eine Kopie dieses Katalogs vom SUPERVISOR anfordern.

Neben den Operationen, die allgemein den Typ TASK und die Verwandschaftsbeziehungen zwischen Objekten dieses Typs betreffen, gibt es weitere, die die Interna einzelner Tasks behandeln. Darunter fallen storage (belegter Speicherplatz), Tasknummer (index) oder Statusangaben. Alle diese Angaben stammen aus dem Prozeßkontrollblock (PCB) der jeweiligen Task im Systemkern. Die Prozeduren am Ende dieses Abschnitts stellen die ELAN Schnittstelle zum Systemkern dar.

Privilegien von Tasks

Die Tasks sind in drei Privilegklassen eingeteilt. Das höchste Privileg hat die Task SUPERVISOR. Nur diese Task darf Objekte des Typs TASK anlegen und löschen. Die mittlere Stufe, das Systemprivileg, haben die Tasks des Systemzweigs. Diese Tasks können insbesondere für jede Task, außer ihren eigenen Vorfahren, einen Ende Auftrag absetzen, der von SUPERVISOR akzeptiert wird. Man kann also z.B. von der Task OPERATOR aus jede Task außer SUPERVISOR und SYSUR löschen (VORSICHT!).

Das niedrigste Privileg (das Benutzerprivileg) haben die Task PUBLIC und ihre Nachkommen. In diesem Zweig des Systems dürfen bzw. können Tasks nur sich selbst oder ihre Nachkommen löschen.

Privilegien werden vererbt, Tasks unter SYSUR haben also stets das Systemprivileg, Tasks unter PUBLIC das Benutzerprivileg.

Restriktionen für Tasks

Bei einem Betriebssystem mit virtueller Speicherverwaltung und Datenraumsharing (»faules Kopieren«) kann für eine Task nicht angegeben werden, daß sie z.B. x Prozent des Plattenplatzes einnehmen darf, da das Erreichen eines solcher Wertes nicht effizient bestimmt werden kann. Um andererseits zu verhindern, daß Tasks das System unkontrolliert bis zur Aktionsunfähigkeit vollschreiben, kann für alle Tasks eine Schranke vorgegeben werden, bei deren Erreichen sie von der Arbeit zeitweise suspendiert werden. So wird erreicht, daß diese Tasks keine neuen Datenräume anlegen etc. Zumindestens einigen Tasks mit Systemprivileg muß zugestanden werden, bis 100% agieren zu dürfen, um gegebenfalls von hier aus andere Tasks zu löschen. Für Tasks mit Benutzerprivileg sollte dagegen eine Schranke angegeben werden, die mindestens 10 MB Freiraum läßt. Die Einstellung dieser Schranke kann im privilegierten Systemzweig mit der Prozedur limit vorgenommen werden.

Prioritäten von Tasks

L3 ist ein Timesharing Betriebssystem. Die Zuteilung von Rechenzeit an Tasks erfolgt durch den Scheduler im Systemkern, der allen Prozessen reihum eine Zeitscheibe von 10 Millisekunden anbietet. Dieses Verfahren wird trotz der großen Anzahl von Prozessen in einem L3 System durch einige Randbedingungen sehr effizient:

Grundsätzlich erhalten nur Systemprozesse (Treiber, Kern) jede ihrer mögliche Zeitscheiben. Einige wichtige Tasks (Supervisor, CONSOLE) werden nur einmal (prio = 1), normale Tasks werden generell zwei- (prio = 2) bis fünfmal (prio = 5) bei der Vergabe übergangen. Desweiteren warten die meisten Prozesse im System auf die Übertragung einer Botschaft. Falls die Botschaft noch nicht da ist, verbraucht der Prozeß nicht seine zustehenden 10 Millisekunden, sondern teilt dem Kern per Systemcall mit, daß der Scheduler den nächsten Prozeß bedienen kann. Durch dieses Verfahren bleiben im Endeffekt nur wenige Prozesse, die ganze Zeitscheiben verbrauchen. Die Tasks haben, wie oben erwähnt eine Priorität zwischen 2 und 5. Diese Priorität wird dynamisch vergeben: Tasks, die viel Rechenzeit verbrauchen, also selten oder garnicht warten, sondern »rechnen«, sinken in ihrer Priorität, Tasks, die viel von ihren Zeitscheiben »weitergeben«, weil sie z.B. im wesentlichen auf Eingaben warten, werden belohnt und behalten ihre Priorität 2.

Operatoren und Prozeduren

tabular4246

TASK

TYPE TASK

Wirkung: Datentyp »interner Taskbezeichner«.

:=

OP := (TASK VAR left, TASK CONST right)

Wirkung: Zuweisungsoperator für Taskbezeichner.

= <>

BOOL OP = (TASK CONST left, right)

BOOL OP <> (TASK CONST left, right)

Wirkung: Liefert TRUE, falls 'left' gleich (bzw. ungleich) 'right' ist.

< <=

BOOL OP < (TASK CONST left, right)

BOOL OP <= (TASK CONST left, right)

Wirkung: Prüfung auf die Relation. Liefert 'TRUE', falls 'left' Nachkomme (Sohn, Enkel,..) von 'right' ist.

/

TASK OP / (TEXT CONST task name)

Wirkung: Liefert den internen Taskbezeichner der Task des angegebenen Namens, falls sie existiert. Der eigene Katalog wird automatisch aktualisiert (identisch mit: task (TEXT CONST task name).
Fehler: * ... gibt es nicht

brother

TASK PROC brother (TASK CONST task)

Wirkung: Liefert den nächsten Bruder von 'task'. Falls kein Bruder existiert, wird 'niltask' geliefert. Aktualisiert den eigenen Katalog nicht automatisch!

father

TASK PROC father

Wirkung: Liefert den internen Taskbezeichner der eigenen Vatertask.

TASK PROC father (TASK CONST task)

Wirkung: Liefert den internen Taskbezeichner der Vatertask von 'task'. Existiert keine Vatertask, wird niltask geliefert. Aktualisiert den eigenen Katalog nicht automatisch!

myself

TASK PROC myself

Wirkung: Liefert den eigenen internen Taskbezeichner.

niltask

TASK CONST niltask

Wirkung: Bezeichner für »keine Task«. So liefern die Prozeduren 'son', 'brother' und 'father' als Resultat 'niltask', wenn keine Sohn-, Bruder- oder Vatertask existiert.

public

TASK PROC public

Wirkung: Liefert den Taskbezeichner der Task PUBLIC.

son

TASK PROC son (TASK CONST task)

Wirkung: Liefert den Taskbezeichner der ersten Sohntask von 'task'. Falls keiner im Katalog vermerkt ist, wird 'niltask' geliefert. Aktualisiert den eigenen Katalog nicht automatisch!

supervisor

TASK PROC supervisor

Wirkung: Liefert den Taskbezeichner der Task SUPERVISOR.

exists

BOOL PROC exists (TASK CONST task)

Wirkung: Liefert 'TRUE', falls der mit 'task' spezifizierte Prozeß existiert. Hiermit kann also auch die Existenz von Realprozessen überprüft werden.

exists task

BOOL PROC exists task (TEXT CONST taskname)

Wirkung: Liefert 'TRUE', falls eine Task mit Namen 'taskname' existiert, sonst 'FALSE'.

is niltask

BOOL PROC is niltask (TASK CONST task)

Wirkung: task = niltask

name

TEXT PROC name (TASK CONST task)

Wirkung: Liefert den Namen von 'task'. Die Task muß noch im System existieren. Falls die 'task' noch nicht im eigenen Katalog enthalten ist, wird dieser aktualisiert.

systemtask

TASK PROC systemtask (TEXT CONST systaskname)

Wirkung: Liefert den Taskbezeichner des durch 'systaskname' spezifizierten Realprozesses. Es lassen sich alle Treiberprozesse über diese Prozedur erreichen, wenn ihr Name bekannt ist.

task

TASK PROC task (TEXT CONST task name)

Wirkung: Liefert den Taskbezeichner der Task des angegebenen Namens, falls sie existiert. Der eigene Katalog wird automatisch aktualisiert.
Fehler: * ... gibt es nicht

Die folgenden Prozeduren entsprechen in ihrer Wirkung den ähnlich benamten

brother, son etc. Im Gegensatz zu jenen wird das Resultat jedoch nicht über den

Taskkatalog erreicht, sondern direkt beim SUPERVISOR angefragt. Die Prozeduren sind also direkte Kommunikationen vermittels des SUPERVISOR Protokolls (Kap.13.6).

xbrother

TASK PROC xbrother (TASK CONST task)

Wirkung: Fragt direkt bei SUPERVISOR nach brother(task). Falls es keine derartige Task gibt, wird niltask als Resultat geliefert.

xexists task

BOOL PROC xexists task (TEXT CONST task)

Wirkung: Direkte Anfrage an die Task SUPERVISOR. Liefert 'TRUE', falls eine Task mit Namen 'task' existiert, sonst 'FALSE'.

xfather

TASK PROC xfather (TASK CONST task)

Wirkung: Fragt direkt bei SUPERVISOR nach father(task). Falls es keine derartige Task gibt, wird niltask als Resultat geliefert.

xname

TEXT PROC xname (TASK CONST task)

Wirkung: Fragt direkt bei SUPERVISOR nach name (task). Falls keine benannte Task existiert, wird niltext als Resultat geliefert.

xson

TASK PROC xson (TASK CONST task)

Wirkung: Fragt direkt bei SUPERVISOR nach son (task). Falls es keine derartige Task gibt, wird niltask als Resultat geliefert.

access catalogue

PROC access catalogue

Wirkung: Die Task SUPERVISOR wird aufgefordert eine Kopie des Systemkatalogs zu senden, mit der die eigene Version überschrieben wird. Prozeduren, die systemglobale Informationen benötigen (father, brother, son) arbeiten dann auf dieser neuen Fassung des Katalogs.

Beispiel:
INT VAR i; TASK VAR t; 
taskinfo; 
(* Im Systemkatalog sind die Tasks noch nicht vorhanden *) 
page; 
FOR i FROM 5 DOWNTO 1 
   REP begin("Task " + text(i),PROC unlink,t); 
PER; 
taskinfo; 
(* Im Systemkatalog sind alle fünf Tasks vorhanden *) 
FOR i FROM 1 UPTO 5 
   REP end (son (myself), quiet); access catalogue 
PER; 
taskinfo; 
(* Hiernach sind alle fünf Tasks im Systemkatalog wieder gelöscht *) 
(* Wäre access catalogue nicht aufgerufen worden, *) 
(* hätte es beim Löschen des zweiten Sohnes *) 
(* die Fehlermeldung "'end' unzulaessig" gegeben, *) 
(* da der taskeigene Katalog nicht aktuell war. *)

begin

PROC begin (TEXT CONST new taskname)

Wirkung: Eine neue Task mit dem angegebenen Namen wird als Sohntask der aufrufenden Task mit der Startprozedur monitor gestartet. Falls diese Prozedur im Monitor, also interaktiv, aufgerufen wird, wird die neue Task sofort an das Terminal gekoppelt.

Fehler: Eine Task mit dem Namen 'new taskname' existiert bereits.

PROC begin (TEXT CONST new taskname, father)

Wirkung: Eine neue Task mit dem angegebenen Namen wird als Sohntask der (Manager)Task 'father' mit der Startprozedur monitor gestartet.

Fehler: falscher Auftrag für Task "father"(04000010,"sender") => 'fathername' ist keine Managertask passenden Typs. 40000010 ist der (gesendete) Auftragscode des Dateimanagerprotokolls.

wait for SYSIO => 'fathertask' hat sich nicht selbst vom Terminal abgekoppelt

PROC begin (TEXT CONST new taskname, PROC startproc, TASK VAR t)

Wirkung: Es wird eine Task mit dem Namen 'new taskname' als Sohn der aufrufenden Task mit der Prozedur 'startproc' gestartet. 't' identifiziert die neue Task, falls kein Fehler aufgetreten ist.

PROC begin (PROC startproc, TASK VAR t)

Wirkung: Es wird eine unbenannte Task (Pseudoname "-") als Sohn der aufrufenden Task mit der Prozedur 'startproc' gestartet. 't' identifiziert die neue Task, falls kein Fehler aufgetreten ist. Bei Verwendung des Pseudonamens kann keine Namenskollision auftreten.

end

PROC end (TASK CONST victim)

Wirkung: Nach Kontrollfrage (mit Prozedur yes) wird an die Task SUPERVISOR der Auftrag geschickt, die Task 'victim' zu löschen. Der Auftrag wird nur akzeptiert, wenn die auftraggebende Task eine höhere Privilegstufe als 'victim' hat oder 'victim' ein Nachkomme des Auftraggebers ist. Falls der Auftrag akzeptiert wird, beendet SUPERVISOR 'victim' und damit implizit auch alle Nachkommen von 'victim'.

PROC end (TASK CONST victim, QUIET CONST quiet)

Wirkung: Löschen ohne Kontrollfrage.

PROC end

Wirkung: Wie: end (myself)

halt

PROC halt (TASK CONST victim)

Wirkung: An die Task SUPERVISOR wird der Auftrag geschickt, der Task 'victim' einen Fehler zu senden. Die Wirkung in 'victim' entspricht dem Auftreten eines Laufzeitfehler im gerade laufenden Programm. Näheres siehe Kapitel 11.

Der Auftrag wird nur akzeptiert, wenn die auftraggebende Task eine höhere Privilegstufe als 'victim' hat oder 'victim' ein Nachkomme des Auftraggebers ist.

rename myself

PROC rename myself (TEXT CONST new name)

Wirkung: An die Task SUPERVISOR wird der Auftrag geschickt, den Namen der Task in 'new name' zu ändern. Damit werden alle Taskvariablen, die sich auf diese Task beziehen, ungültig. Es ist auch möglich, eine Task mit dem Pseudonamen "-" zu benennen, bzw. eine benannte Task »zur Strichtask« zu machen (name "-").

Verwaltung von Tasks

callee

TASK PROC callee (TASK CONST task)

Wirkung: Liefert den Partner von 'task', auf den 'task' im geschlossenen Wartezustand (receive) wartet. Falls 'task' nicht im geschlossenen Warten verharrt, wird niltask geliefert.

cpu time

DINT PROC cpu time (TASK CONST task)

Wirkung: Liefert die verbrauchte Rechenzeit von 'task'.

dataspaces

INT PROC dataspaces (TASK CONST task)

Wirkung: Liefert die Anzahl der Datenräume von 'task'.

INT PROC dataspaces

Wirkung: Liefert die Anzahl der eigenen Datenräume. (put (dataspaces(myself))).

index

INT PROC index (TASK CONST task)

Wirkung: Liefert den Taskindex von 'task'. Der Index ist der zeitinvariante Teil des internen Taskbezeichners.
Bemerkung: Der Taskindex ermöglicht die eindeutige Identifikation von »Strichtasks« (Tasks mit Pseudonamen -).
Beispiel: put (hex(index(son(printer))))

limit

PROC limit (TASK CONST task, INT CONST limit)

Wirkung: 'task' wird von der Speicherverwaltung blockiert, wenn 'limit' Prozent des Speicherplatzes des Systems belegt sind. Diese Einstellung kann nur von Tasks mit Systemprivileg vorgenommen werden!
Fehler: 'limit'-Angabe muß zwischen 1 und 100 liegen

INT PROC limit (TASK CONST task)

Wirkung: Liefert den eingestellten Wert für 'task'.

prio

PROC prio (TASK CONST task, INT CONST prio)

Wirkung: Die Priorität von 'task' wird auf 'prio' gesetzt. Diese Einstellung kann nur von Tasks mit Systemprivileg vorgenommen werden!

INT PROC prio (TASK CONST task)

Wirkung: Liefert die Priorität von 'task'.

privilege

INT PROC privilege (TASK CONST task)

Wirkung: Liefert das Privileg von 'task'. Die Privilegstufen sind:
2:
Supervisorprivileg; darf Tasks erzeugen. Nur Task SUPERVISOR.
1:
Systemprivileg; erlaubt »Systemverwalteroperationen«. Task SYSUR und Nachkommen.
0:
Benutzerprivileg; Task PUBLIC und Nachkommen.

Bemerkung: Für Abfragen der vorhandenen Berechtigungsstufe gibt es wertliefernde Prozeduren sinntragenden Namens:

INT PROC user privilege

INT PROC system privilege

INT PROC supervisor privilege

status

INT PROC status (TASK CONST task)

Wirkung: Liefert den Taskstatus von 'task'. Möglich sind:
wait:
Task wartet auf Botschaft.
busy:
Task ist aktiv.
blkd:
Task ist blockiert
dead:
Exitus. Die Task kann keine Aktion mehr ausführen. Sie kann nur noch gelöscht werden.

storage

INT PROC storage (TASK CONST task)

Wirkung: Liefert den belegten Speicherplatz von 'task'. Die Angabe besagt, wieviel Platz die Datenräume von 'task' beanspruchen. Die Angabe erfolgt in Byte und ist (wegen Paging) stets durch 4096 ohne Rest teilbar.
Bemerkung: Irgendwelche Rückschlüsse aus Angaben für mehrere Tasks sind nicht möglich.

wakeup time

DINT PROC wakeup time (TASK CONST task)

Wirkung: Wenn 'task' ein timeout gesetzt hat, um auf eine Botschaft zu warten oder durch pause, so liefert diese Prozedur den Zeitpunkt, zu dem diese Wartezeit abläuft.
Bemerkung: Das Ergebnis ist eine systemtime Angabe, die noch anstehende Wartezeit einer Task kann also z.B. durch: put ( time (wakeup time (task) - systemtime, 8,3) angezeigt werden.


next up previous contents index
Next: Ein-/Ausgabe auf dem Bildschirm Up: L3 Referenzhandbuch Previous: Textdateien

Michael Hohmuth
Sun Mar 31 04:49:53 MET DST 1996