Wir beginnen nun mit der sukzessiven Implementation des Platten-Treibers. Die Implementation des generischen Paketes für die doppelt verkettete Liste wollen wir dem Leser zum Teil zur Übung überlassen. Es sei allerdings darauf hingewiesen, daß die Realisierung des Paketes nicht notwendigerweise mit Pointerstrukturen erfolgen muß. Genauso denkbar ist, ein Array zu verwenden und dafür zu sorgen, daß, wenn die Liste voll ist, Tasks, die Leseanforderungen abgeben, vor dem Einfügen in die Liste blockiert werden. Wichtig ist jedoch, daß die Liste intern als geschütztes Objekt realisiert wird, z.B.:
Wir beginnen also mit dem Paket Platten_Treiber, wobei wir von der Möglichkeit der getrennten Compilierung häufig Gebrauch machen werden:
with Doppelt_verkettete_Liste, Platten_Controller;
package body Platten_Treiber is
task type Blockiere is -derBlockier-Task
entry
Nimm_gelesene_Daten(
data: Platten_Defs.data);
entry
Gib_gelesene_Daten(
data: out Platten_Defs.data);
end Blockiere;
type Blockiere_Pointer is access Blockiere;
type Listen_element is -daswirdinderdoppeltverkettetenListeabgespeichert
record
Spur: natural;
Sektor: natural;
Blockierender_Task: Blockiere_Pointer;
end record;
task Platten_Treiber_Task is -derScheibenwischer-undSchutz-Task
entry Lies;
end PLatten_Treiber_Task;
function Kleiner( -die''
x: Listen_element;
y: Listen_element)
return boolean is separate;
package Die_doppelt_verkettete_Liste is
new Doppelt_verkettete_Liste(
Element =
"
"
=
task body Blockiere is separate;
task body Platten_Treiber_Task is separate;
function Read(
Spur: natural;
Sektor: natural)
return Platten_Defs.data
is separate;
end Platten_Treiber;
Die eigentliche Implementation der Operation Read haben wir aufgeschoben. Am Beginn des Paket-Body's findet man die Spezifikation des Blockier-Tasks und des Listenelementes, danach folgen die Deklaration des ,,Scheibenwischer-Tasks``, im Paket Platten_Treiber_Task genannt, der geforderten Ordnungsrelation für die doppelt verkettete Liste sowie die Instantiierung dieser Liste. Die Implementation der beiden Task-Bodies sind ebenfalls wie die Implementation der Funktion Kleiner aufgeschoben worden und können getrennt übersetzt werden.
Als nächstes wollen wir die Implementation der Funktion Read ins Auge fassen.
separate(Platten_Treiber)
function Read(
Spur: natural;
Sektor: natural)
return Platten_Defs.data
is
out_data: Platten_Defs.data;
mein_blockierender_Task: Blockiere_Pointer := new Blockiere;
-hierwirdderBlockier-Taskkreiert
Element: Listen_element := (
Spur =
Sektor =
Blockierender_Task =
-daseinzufuegendeListenelement
begin
Die_doppelt_verkettete_Liste.Fuege_ein(
das_Element =
-EinfuegendesListenelementes
Platten_Treiber_Task.Lies;
-Leseanforderunganmelden
mein_blockierender_Task.Gib_gelesene_Daten(
data =
-hierblockiertderrufendeTaskbisdiegelesenen
-Datenangeliefertwerden.
return out_data;
end Read;
Im Deklarationsteil der Funktion wird der Blockier-Task kreiert und das einzufügende Listenelement angelegt und richtig initialisiert. Im Anweisungsteil wird zuerst das Listenelement in die Liste eingefügt, anschließend blockiert der rufende Task beim Aufruf des Entry's des Blockier-Tasks, bis die geforderten Daten gelesen und zurückgeliefert worden sind.
Bevor wir uns der Implementation der Tasks Blockiere und Platten_Treiber_Task zuwenden, wollen wir noch die Implementation der Funktion Kleiner behandeln.
separate(Platten_Treiber)
function Kleiner(
x: Listen_element;
y: Listen_element)
return boolean
is
-
-DerimListenelementvorhandenePointeraufdenBlockier-Task
-istfuerdenVergleichzweierListenelementeirrelevant.
-NurSpur-undSektornummersindinteressant.
-EinListenelementistalsokleineralseinanderes,wenn
-entwederdieSpurnummerkleineristoderbeigleicherSpur
-dieSektornummerkleinerist.
-
begin
return (x.Spur
((x.Spur = y.Spur) and (x.Sektor
end Kleiner;
Nun aber zu den Tasks! Zuerst implementieren wir den Blockier-Task :
separate(Platten_Treiber)
task body Blockiere is
gelesene_Daten: Platten_Defs.data; -zumZwischenspeicherndergelesenenDaten
begin
accept -hierwartetderTaskaufdasEintreffendergelesenenDaten
Nimm_gelesene_Daten(
data: Platten_Defs.data) do
gelesene_Daten := data; -hierwerdensiezwischengespeichert
end Nimm_gelesene_Daten;
accept -undhierwartetderTaskaufdasAbholenderDaten
Gib_gelesene_Daten(
data: out Platten_Defs.data) do
data := gelesene_Daten;
end Gib_gelesene_Daten;
end Blockiere;
Es bleibt also noch, den Platten_Treiber_Task zu implementieren:
with Platten_Controller;
separate(Platten_Treiber)
task body Platten_Treiber_Task is
type Richtung is (aufwaerts, abwaerts);
-Richtungen,indenensichderLese-Schreibkopfbewegenkann
akt_Richtung: Richtung := aufwaerts;
-Richtung,indersichderLese-Schreibkopfgeradebewegt
aktuelles_Element: Listen_element := (
Spur =
Sektor =
Blockierender_Task =
-Listenelement,daszuletztgelesenwurde
gelesene_Daten: Platten_Defs.data;
-zuletztgeleseneDaten
function Suche_aktuelles_Element(
momentan_aktuelles_Element: Listen_element)
return Listen_Element
is separate;
-suchtdasListenelement,dessenSpur/Sektor
-alsnaechstegelesenwerdensollen
begin
loop
accept -fuegtersteAnforderungein
Lies do
null;
end Lies;
loop
select
accept
-fuegtdiefolgendenAnforderungenein,
-bisalleAuftraegeerledigtwurden.
Lies do
null;
end Lies;
else
-fallsgeradekeinElementeingefuegtwird,
-erledigeeinenLeseauftrag
if Die_doppelt_verkettete_Liste.Liste_leer then
-fallskeineAnforderungmehrvorliegt,
-verlassedieinnereSchleifeundwartewieder
-aufdenerstenLeseauftrag
exit;
else
-dieListeistnichtleer,also:
aktuelles_Element := -suchenaechstesListenelement
Suche_aktuelles_Element(
momentan_aktuelles_Element =
-liesDatenmittelsAufrufdesControllers
gelesene_Daten :=
Platten_Controller.Positioniere_Lese_Schreibkopf_und_lies(
Spur =
Sektor =
-loescheListenelement
Die_doppelt_verkettete_Liste.Loesche_aktuelles_Element;
-uebergibDatenanBlockiere-Task
aktuelles_Element.Blockierender_Task.Nimm_gelesene_Daten(
data =
end if;
end select;
end loop;
end loop;
end Platten_Treiber_Task;
Eine Beschreibung der Implementation des Tasks findet sich in den Kommentaren des Codes. Wir wollen aber explizit darauf hinweisen, warum hier eine geschachtelte Loop-Anweisung notwendig ist: Angenommen, wir hätten die äußere Schleife weggelassen und die Exit-Anweisung im Then-Zweig der If-Anweisung durch null ersetzt. Dann läge im Prinzip dieselbe Funktionalität vor, der wesentliche Unterschied ist jedoch, daß sich der Task, wenn keine Leseanforderung ansteht und sich kein Leseauftrag in der Liste befindet, in einer Endlosschleife befindet, in der er ständig die Select-Anweisung ausführt. Dieses Verhalten ist auch unter dem Begriff Polling bekannt, und sollte tunlichst vermieden werden. Mit der geschickten Anordnung unserer beiden geschachtelten Schleifen ist uns das gelungen.
Als letztes bleibt also noch die Funktion Suche_aktuelles_Element zu implementieren:
separate(Platten_Treiber)
function Suche_aktuelles_Element(
momentan_aktuelles_Element: Listen_element)
return Listen_Element
is
package Liste renames Die_doppelt_verkettete_Liste;
function "
"
(x,y: Listen_element) return boolean renames Kleiner;
akt_El: Listen_element;
begin
if akt_Richtung = aufwaerts then
begin
return
Liste.Geh_zu_naechst_groesserem_Element(
aktuelles_Element =
exception
when Liste.Ende_der_Liste =
-esgibtkeingroesseres,daheraendernwirdieRichtung
akt_Richtung := abwaerts;
return
Liste.Geh_zu_naechst_kleinerem_Element(
aktuelles_Element =
end;
else -akt_Richtung
begin
return
Liste.Geh_zu_naechst_kleinerem_Element(
aktuelles_Element =
exception
when Liste.Anfang_der_Liste =
-esgibtkeinkleineres,daheraendernwirdieRichtung
akt_Richtung := aufwaerts;
return
Liste.Geh_zu_naechst_groesserem_Element(
aktuelles_Element =
end;
end if;
end Suche_aktuelles_Element;
Damit ist die Implementation des Platten-Treibers abgeschlossen. Wir haben in diesem Kapitel viel über die diffizilen Probleme bei der Task-Kommunikation und Task-Synchronisation gelernt. Das Beispiel als solches ist natürlich eine grobe Vereinfachung eines wirklichen Platten-Treibers, aber die wesentlichen Probleme wurden aufgezeigt. Welche Probleme bei interrupt-gesteuerten Geräte-Treibern auftauchen, werden wir im Kapitel 19 sehen.
Johann Blieberger