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.
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.
OP := (DATASPACE VAR dest, DATASPACE CONST source)
PROC copy (DATASPACE CONST source, DATASPACE VAR dest)
PROC copy (DATASPACE CONST source, dest, INT CONST from page, to page, pages)
PROC copy (DATASPACE CONST ds, INT CONST from page, to page, pages)
PROC close (DATASPACE CONST ds)
DATASPACE PROC dataspace (TEXT CONST name)
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;
INT PROC dataspaces(TASK CONST task)
PROC delete (DATASPACE VAR ds)
PROC delete (DATASPACE CONST ds, INT CONST from page, pages)
BOOL PROC exists (DATASPACE CONST ds)
PROC move (DATASPACE VAR source, dest)
DATASPACE PROC next (DATASPACE CONST ds)
INT PROC next (DATASPACE CONST ds, INT CONST pageno)
INT PROC index (DATASPACE CONST ds)
INT PROC pages (DATASPACE CONST ds)
PROC set min heapsize (DATASPACE CONST ds, INT CONST new limit)
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 ;
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") ; ...
INT PROC storage (DATASPACE CONST ds)
PROC type (DATASPACE CONST ds, INT CONST typ)
INT PROC type (DATASPACE CONST ds)
type ('ds') < 0 falls 'ds' nie an ein BOUND Objekt
angekoppelt war,
type ('ds') = 0 falls 'ds' schon an ein BOUND Objekt
angekoppelt war.
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:
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;
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.
REF INT PROC address (BOUNDPTR CONST boundptr)
PROC close (BOUNDPTR VAR bound ptr)
DATASPACE PROC dataspace (BOUNDPTR CONST boundptr)
PROC open (DATASPACE CONST space, INT CONST map size BOUNDPTR VAR bound ptr)
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.
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.
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.
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.
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.
OP := (TASK VAR left, TASK CONST right)
BOOL OP = (TASK CONST left, right)
BOOL OP <> (TASK CONST left, right)
BOOL OP < (TASK CONST left, right)
BOOL OP <= (TASK CONST left, right)
TASK OP / (TEXT CONST task name)
TASK PROC brother (TASK CONST task)
TASK PROC father (TASK CONST task)
TASK PROC son (TASK CONST task)
BOOL PROC exists (TASK CONST task)
BOOL PROC exists task (TEXT CONST taskname)
BOOL PROC is niltask (TASK CONST task)
TEXT PROC name (TASK CONST task)
TASK PROC systemtask (TEXT CONST systaskname)
TASK PROC task (TEXT CONST task name)
TASK PROC xbrother (TASK CONST task)
BOOL PROC xexists task (TEXT CONST task)
TASK PROC xfather (TASK CONST task)
TEXT PROC xname (TASK CONST task)
TASK PROC xson (TASK CONST task)
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. *)
PROC begin (TEXT CONST new taskname)
PROC begin (TEXT CONST new taskname, father)
wait for SYSIO => 'fathertask' hat sich nicht selbst vom Terminal abgekoppelt
PROC begin (TEXT CONST new taskname, PROC startproc, TASK VAR t)
PROC begin (PROC startproc, TASK VAR t)
PROC end (TASK CONST victim, QUIET CONST quiet)
Der Auftrag wird nur akzeptiert, wenn die auftraggebende Task eine h�here Privilegstufe als 'victim' hat oder 'victim' ein Nachkomme des Auftraggebers ist.
PROC rename myself (TEXT CONST new name)
TASK PROC callee (TASK CONST task)
DINT PROC cpu time (TASK CONST task)
INT PROC dataspaces (TASK CONST task)
INT PROC index (TASK CONST task)
PROC limit (TASK CONST task, INT CONST limit)
INT PROC limit (TASK CONST task)
PROC prio (TASK CONST task, INT CONST prio)
INT PROC prio (TASK CONST task)
INT PROC privilege (TASK CONST task)
INT PROC status (TASK CONST task)
INT PROC storage (TASK CONST task)
DINT PROC wakeup time (TASK CONST task)