home *** CD-ROM | disk | FTP | other *** search
Text File | 1990-04-23 | 111.5 KB | 3,120 lines |
- .. //////////////////////////////////////////////////////////////////
- .. /// Dokumentation zum Multi-Tasking Subsystem V2.10 / April 1990
- .. /// Basissystem CPMULTI
- .. /// Format: DOCLIST 1.00-Format
- .. //////////////////////////////////////////////////////////////////
- .. /// Bitte passen Sie die nachfolgenden Zeile ggf. für Ihren
- .. /// Drucker an. Die Voreinstellung paßt auf einen EPSON FX-85
- .. /// mit Papierformat 12"
- .. //////////////////////////////////////////////////////////////////
- .PL 72 Seitenlänge 72 Zeilen
- .LL 80 Zeilenlänge 80 Zeichen
- .ID 5 Einrücken um 5 Zeichen vom linken Rand
- .RM 5 5 Zeichen bis zum rechten Rand Platz lassen -> 70
- .. Zeichen Text je Zeile
- .HS = Zeichen für Kopf-/Fuß-Trenner
- .RS - Zeichen für Trennung von Referenz-Einträgen
- .CO (c) Copyright 1988..1990 Ch.Philipps Software-Technik
- .. //////////////////////////////////////////////////////////////////
- .SE Einleitung
- Lieber Anwender,
-
- Ziel des Multi-Tasking Subsystems für Turbo-Pascal 5.x war es, die
- Programmierung paralleler Prozesse und das Studium der damit verbun-
- denen Probleme der Inter-Prozess-Kommunikation (kurz: IPC = Inter
- Process Communication) auf Basis einer weit verbreiteten
- Programmierumgebung unter MS-DOS/PC-DOS zu ermöglichen.
- Wenngleich das vorliegende Produkt zunächst lediglich als Experimen-
- tierwerkzeug gedacht war, entwickelte es sich schon bald zu einem
- umfangreichen und leistungsfähigen Produkt.
-
- Mittlerweile hat das Multi-Tasking Subsystem einen weiten
- Anwenderkreis für sich gewonnen und wird in vielen Unternehmen
- erfolgreich zur Realisierung von Projekten z. B. in den Bereichen
- Datenkommunikation, Meß- und Regeltechnik und Gerätesteuerung
- sowie an zahlreichen Universitäten im In- und Auslang eingesetzt.
- Die überaus positive Resonanz von Seiten der Lizenznehmer war Anlaß
- zu einer neuerlichen Überarbeitung und Fortentwicklung des
- Produktes, das Ihnen nun in der Version 2.10 vorliegt.
-
- Bedingt durch die Entstehungsgeschichte, wurde das Subsystem von
- Anfang an einem intensiven Test unterworfen, weshalb ich nun der
- festen Überzeugung bin, Ihnen ein ausgereiftes und (soweit man dies
- je von einer Software behaupten darf) fehlerfreies Produkt anbieten
- zu können.
-
- Zur Einstimmung, kurz ein Überblick über den Funktionsumfang des
- Basissystems:
-
- - maximal 50 (problemlos erweiterbar) parallel ablaufende Tasks in 9
- Prioritätsebenen
- - Prioritäten zur Laufzeit änderbar
- - Code-Sharing
- - preemptiver Scheduler
- - sichere Verwendung von DOS-Funktionen innerhalb der Tasks möglich;
- kein Taskswitch innerhalb einer DOS-Funktion oder während der
- Ausführung eines kritischen Interrupts (Platten-I/O, ...)
- - dynamische Zeitscheibensteuerung
- - programmierbare Zeitscheibengröße (auch kleiner 55,5 ms)
- - Time-Slicing abschaltbar
- - Exclusiv-Modus für einzelne Tasks
- - Message-Passing
- - Semaphoren
- - "Named Pipes" (Turbo Pascal Text-Datei Gerätetreiber)
- - Resourcen
- - programmierbare Timer
- - einfache Ereignissteuerung (maximal 20 gleichzeitig aktive
- Ereignisse, jedoch problemlos erweiterbar)
- - lauffähig auf allen PC, XT, AT, PS/2 und 100% kompatiblen System
- mit DOS Version 2.xx, 3.xx und 4.0x, DR-DOS
- - Zeichenorientierter Druckerspooler für die Einheit LST
- - interruptgesteuerte V24 Unterstützung (PD) im Quellcode
- - Pipe-Unterstützung für die serielle Schnittstelle, d. h. Zugriff
- auf die Schnittstelle über TEXT-Dateien
- - Quellcode verfügbar
-
- Dies schließt noch nicht die zusätzlichen Möglichkeiten ein, die
- sich Ihnen durch die Einbindung einer weiteren, für registrierte
- Benutzer ebenfalls im Lieferumfang enthaltenen Unit erschließen:
-
- - erweiterte Tastaturcodes (Zeichen, Scancode, Shift-Status)
- - Manipulation des Tastaturpuffers
- - Unterbindung von Tastatureingaben
- - DOS als Sub-Task
- - Aktivierung von Tasks per Hot-Key
-
- Ferner erhalten registrierte Benutzer den Quellcode einiger
- Zusatzunits (siehe Hinweise in der Dokumentation), die in der
- ShareWare-Version nur als TPU enthalten sind.
-
- Ab der Version 2.10 kann ein Norton-Guide ähnliches Online-
- Hilfesystem zum Multi-Tasking Subsystem direkt vom Autor
- erworben werden. Dieses hält die wichtigesten Informationen
- sowie den gesamten Referenzteil des Handbuchs übersichtlich
- gegliedert auf Knopfdruck für Sie bereit.
- Der Online-Guide basiert auf dem Programm GUIDE, welches zum
- Lieferumfang der Turbo Professional Toolbox gehört und wird
- incl. der Quelldateien aller Hilfetexte geliefert. So ist es
- Lizenznehmern o. g. Tools möglich, die Hilfe nach eigenen
- Vorstellungen zu erweitern.
-
- Obwohl das Multi-Tasing Subsystem mittlerweile kaum noch
- Wünsche offenläßt, mag es dennoch sinnvolle Erweiterungen
- geben, auf die ich selbst bislang nicht gekommen bin.
- Sollten Sie also Funktionen benötigen, die das Multi-Tasking
- Subsystem in seiner heutigen Ausbaustufe noch nicht beinhaltet,
- so würde ich mich freuen, von Ihnen zu hören.
-
- Ich möchte an dieser Stelle nicht versäumen, mich ausdrücklich bei
- L. David Baldwin und TurboPower Software für ihren einzigartigen
- Debugger TDEBUG 4.01 zu bedanken, der mir bei der Entwicklung
- der Ursprungsversion unter Turbo Pascal 4.0 eine unschätzbare
- Hilfe war. Auch die Turbo Analyst Toolbox aus dem Hause
- TurboPower Software hat mit das Tuning des Multi-Tasking
- Subsystems sehr erleichert.
-
- Nun wünsche ich Ihnen viel Vergnügen mit Ihrem Multi-Tasking Sub-
- system und verbleibe
-
- mit freundlichen Grüßen,
-
- Christian Philipps
- .SE Multi-Tasking Subsystem für Turbo-Pascal 5.x Version 2.10
-
-
-
-
-
-
-
-
- Turbo Pascal 5.x Multi-Tasking Subsystem
-
-
- Benutzerhandbuch
-
-
-
-
-
-
-
-
- (c) Copyright 1988, 1989, 1990
-
- Christian Philipps Software-Technik
-
- alle Rechte vorbehalten
-
-
- Version 2.10 / April 1990
-
-
-
-
-
-
-
-
-
-
-
-
- Anschrift: Christian Philipps
- Software-Technik
- Düsseldorfer Str. 316
-
- D-4130 Moers 1
-
- Telefon..: 02841 / 35932
- Fido-Netz: 509/3.4
-
- .SE Warenzeichen
- Warenzeichen, die in diesem Handbuch erwähnt werden
-
-
- TurboPower Software ist Warenzeichen von TurboPower Software, Scotts
- Valley
-
- Turbo Professional ist eingetragenes Warenzeichen von Sunny Hill
- Software, verwendet unter Lizenz von TurboPower Software
-
- Turbo Pascal und TASM sind Warenzeichen von Borland International
-
- MS-DOS, OS/2 und MASM sind Warenzeichen der Microsoft Corporation
-
- A86 ist Warenzeichen von Eric Isaacson Software, Bloomington
-
- UNIX ist Warenzeichen von AT & T
-
- IBM PC, XT, AT, PS/2 und PC-DOS sind Warenzeichen der International
- Business Machines Corporation
-
- DR-DOS ist Warenzeichen der Firma Digital Research
- .SE Lizenzvereinbarung
- Lizenzvereinbarung (ShareWare-Version)
- --------------------------------------
-
- Die vorliegende Software und die dazugehörige Dokumentation sind
- urheberrechtlich geschützt. Jede Verletzung dieser Lizenzvereinbarung
- wird nach besten Kräften strafrechtlich verfolgt.
-
- Das Multi-Tasking Subsystem, einschließlich im Lieferumfang
- enthaltener Zusatzprogramme und der zugehörigen Dokumentation, ist
-
- Copyright (c) 1988..1990 von
-
- Christian Philipps Software-Technik
-
- Düsseldorfer Str. 316
- 4130 Moers 1
-
- Teile der Software sind Copyright (c) von Borland International
- und TurboPower Software.
-
- Dies ist ein Produkt, das u. a. mittels des ShareWare-Konzeptes
- vermarktet wird. Dies bedeutet, daß Sie das Recht haben, diese Software
- und diese Dokumentation nach Belieben an andere Interessenten wei-
- terzugeben, vorausgesetzt, dies geschieht in der ursprünglichen
- und unmodifizierten Form. Von dieser Auflage sind sowohl Software
- als auch Dokumentation betroffen.
- Der Vertrieb dieses Produktes ohne eine schriftliche Genehmigung des
- Autors sowie die Weitergabe in Verbindung mit einem kommerziellen
- Produkt sind strengstens untersagt.
-
- Sie haben das Recht, das vorliegende Softwarepaket für die Dauer von
- 30 Tagen kostenlos zu testen und anzuwenden. Sollten Sie sich nach
- Ablauf dieser Testperiode entschließen, das Programm weiterhin
- einzusetzen, so erwarte ich, daß Sie sich als ordnungsgemäßer
- Benutzer registrieren lassen.
-
- Die Lizenzgebühren schlüsseln sich wiefolgt auf:
-
- Objektcode 171,-- DM
- Quellcode 342,-- DM (nur nach/bei Erwerb des Objektcodes)
-
- Online-Guide 35,-- DM (nur nach/bei Erwerb einer Objekt-
- oder Quellcode-Lizenz)
-
- Diese Preise verstehen sich incl. Mehrwertsteuer.
-
- Für Verpackung und Versand werden je Bestellung 6,- DM zusätzlich
- berechnet.
-
- Als schnellster Zahlungsweg hat sich die Einsendung eines
- Verrechnungsschecks erwiesen.
-
- Anschrift: Christian Philipps
- Software-Technik
- Düsseldorfer Str. 316
- D-4130 Moers 1
-
- Gewerbliche Anwender und öffentlich-rechtliche Einrichtungen können
- das Multi-Tasking Subsystem auch auf Rechnung bestellen (Bestellung
- auf Papier mit Firmenkopf). Der Rechnungsbetrag ist in diesem Fall
- ohne Abzug innerhalb von 14 Tagen nach Erhalt der Ware zahlbar. Auf
- der Rechnung werden 14% MwSt ausgewiesen. Anderslautende AGB werden
- nur dann akzeptiert, wenn sie Christian Philipps Software-Technik
- begünstigen.
-
- Erfüllungsort für die beiderseitigen Leistungen und - soweit
- gesetzlich zulässig - Gerichtsstand ist Moers.
-
- Die Rechtsbeziehung zwischen dem Autor und dem Lizenznehmer unter-
- liegen ausschließlich dem Recht der Bundesrepublik Deutschland.
-
-
-
- Wenngleich die ShareWare-Version nicht in ihren Funktionen einge-
- schränkt ist, so enthält Sie doch als kleine Erinnerung für Sie,
- sich als Benutzer registrieren zu lassen, einen Textbildschirm, der
- automatisch nach Beendigung jedes mit dieser Unit erstellten Pro-
- grammes aufgeblendet wird. Unmittelbar nach Eingang Ihrer Lizenz-
- gebühr, erhalten Sie eine Version des Subsystems, die diesen Bild-
- schirm nicht mehr enthält, d. h. mit der Sie eigene Programme zur
- kommerziellen Nutzung erstellen können.
-
- Zusätzlich SCHENKE ich Ihnen eine lizensierte, auf dem Multi-Tasking
- Subsystem basierende Unit, mit deren Hilfe Sie volle Kontrolle über
- die Tastatur ausüben, Tasks per Hot-Key aktivieren, ein DOS-Programm
- (z. B. einen zweiten COMMAND.COM) als Task ausführen und PopUp-Tasks
- während laufender DOS-Task verwalten können. Auch erhalten Sie den
- Quellcode des Print-Spoolers (Unit MtPrint) und der V24-Pipe-Unter-
- stützung (Unit V24Pipe). Weiterhin steht jedem registrierten Benut-
- zer für die Dauer von 1 Jahr telefonische Unterstützung zur Verfü-
- gung.
- .SE Garantie- und Haftungsausschluß
- Garantie- und Haftungsausschluß
- -------------------------------
-
- Der Autor garantiert NICHT die Eignung des Multi-Tasking Subsystems
- incl. evtl. Zusatzprogramme für einen bestimmten Anwendungsfall,
- weder nach ausdrücklicher Absprache, noch implizit durch den Inhalt
- der Dokumentation oder der gelieferten Software.
-
- Weiterhin ist der Autor UNTER KEINEN UMSTÄNDEN für Schäden haftbar,
- die sich aus der Nutzung oder Unfähigkeit zur Nutzung des vorlie-
- genden Produktes ergeben. Dies schließt den Verlust von Geschäfts-
- gewinnen, die Unterbrechung der geschäftlichen Abläufe, den Verlust
- von Daten sowie alle übrigen materiellen und ideellen Verluste und
- deren Folgeschäden ein und gilt selbst dann, wenn o. g. Person zuvor
- ausdrücklich auf die Möglichkeit derartiger Schäden hingewiesen
- worden ist.
-
- DURCH DIE NUTZUNG DER VORLIEGENDEN SOFTWARE ERKLÄRT DER ANWENDER
- SEIN EINVERSTÄNDNIS MIT DIESEN LIZENZVEREINBARUNGEN SOWIE DEM
- GARANTIE- UND HAFTUNGSAUSSCHLUSS.
- .SE Systemvoraussetzungen
- I. Systemvoraussetzungen
- Um das Multi-Tasking Subsystem nutzen zu können, benötigen Sie
- mindestens
-
-
- - einen IBM PC, XT, AT, PS/2 oder 100% kompatiblen Computer
-
- - MS-DOS oder PC-DOS Version 2.x, 3.x oder 4.0x bzw. DR-DOS
-
- - Turbo-Pascal Version 5.x
-
- - 256 KB RAM
-
- - 2 Diskettenlaufwerke, besser jedoch eine Festplatte
-
-
-
- Um Änderungen an den Assembler-Quellen vornehmen zu können, ist
- zusätzlich der Microsoft Macroassembler (MASM) ab Version 4.0
- oder der Turbo Assembler V1.00 erforderlich.
-
- Der Turbo-Debugger (intern wie auch stand-alone) ist in den meisten
- Fällen problemlos für die Fehlersuche zusammen mit dem Subsystem
- einsetzbar.
-
-
- Einschränkungen
- ---------------
-
- Die Coprozessoren 8087, 80x87 werden NICHT unterstützt.
-
- .SE Einbindung des Subsystems
- II. Einbindung der Units des Multi-Tasking Subsystems
-
-
- In der gesamten Dokumentation gehe ich davon aus, daß Sie mit dem
- Umgang des Turbo-Pascal Compilers Version 5.x vertraut sind, das
- UNIT-Konzept dieses Compilers verstanden haben und den Umgang mit
- den im Compilerhandbuch beschriebenen Features von Turbo-Pascal
- beherrschen.
- Sollten Sie an einigen Stellen der Beschreibung grundlegende Ver-
- ständisschwierigkeiten haben, so schlage ich vor, daß Sie das
- Turbo-Pascal Handbuch noch einmal zur Hand nehmen.
-
- Nützlich zum Verständnis der Beschreibung, aber nicht unbedingt
- erforderlich, ist ein wenig Erfahrung im Umgang mit Interruptbehand-
- lung, Exit-Prozeduren und einem Macroassembler für den PC.
-
-
- Das Multi-Tasking Subsystem besteht aus insgesamt 8 Units:
-
- a) CPMULTI
-
- Dies ist das eigenliche Subsystem, das die Grundlage für die
- beiden anderen Units bildet.
-
- b) MTPOPUP (nur registrierte Version)
-
- Die auf dem Basissystem aufbauende Unit, die alle Funktionen
- enthält, um vollständige Kontrolle über die Tastatur ausüben zu
- können, Tasks per Hot-Key aus dem Schlaf zu reißen und DOS als
- Sub-Task ausführen zu können.
-
- Diese Unit ist in der separaten Dokumentation MTPOPUP.DOK
- beschrieben
-
- c) QUEUE
-
- Eine zusätzliche Unit, die ich bereits von einiger Zeit als
- Public Domaine Zusatz zum Subsystem freigegeben habe. Diese
- enthält universell verwendbare Funktionen zur Verwaltung von
- doppelt verketteten Listen.
- Der Einsatz dieser Unit setzt voraus, daß Sie sich im Umgang mit
- untypisierten Pointer und der Typumwandlung sowie den
- Prozedurparametern von Turbo Pascal 5.x sicher fühlen.
- Die Routinen dieser Unit stellen die Konsistenz der Queues zu jedem
- Zeitpunkt durch geeignete Semaphoren-Operationen sicher.
-
- d) CPMISC (in der registrierten Version mit Quellcode)
-
- Eine Reihe nützlicher Routinen, die teilweise von der Unit QUEUE
- verwendet werden.
-
-
- e) MTPIPE
-
- Text-Datei Gerätetreiber für "Named-Pipes". Diese Unit ermöglicht
- einen einfachen Datenaustausch zwischen mehreren Tasks über einen
- hauptspeicherinternen Ringpuffer. Das Lesen bzw. Schreiben der
- Pipe erfolgt mittels der Standard-Prozeduren
- Read/Readln und Write/Writeln.
-
- Diese Unit ist in der separaten Dokumentation MTPIPE.DOK be-
- schrieben.
-
-
- f) MTPRINT (in der registrierten Version mit Quellcode)
-
- Text-Datei Gerätetreiber für die Einheit "Lst". Diese Unit ersetzt
- die Standard-Unit "Printer". Alle Ausgaben auf den Drucker (z. B.
- mittels Writeln(Lst,'Dies ist ein Text');) werden über eine Pipe
- gepuffert und von einem Hintergrundprozeß zum Drucker geschickt.
- MTPrint stellt somit einen einfachen Drucker-Spooler auf
- Zeichenebene zur Verfügung.
- Die größe des Druckpuffers sowie die Priorität des Hintergrund-
- prozesses sind konfigurierbar.
-
- Diese Unit ist in der separaten Dokumentation MTPRINT.DOK be-
- schrieben.
-
-
- g) V24 (Public Domaine; im Quellcode)
-
- Diese Unit enthält einen Satz interruptgesteuerter V24-Routinen,
- die ich als Public-Domaine Software freigegeben habe. Diese Rou-
- tinen werden speziell für den Einsatz in Verbindung mit dem Sub-
- system durch die Unit V24Pipe unerstützt.
-
-
- h) V24PIPE (in der registrierten Version mit Quellcode)
-
- Auf Basis der Public-Domaine V24-Routinen (siehe Punkt g), stellt
- diese Unit eine Verbindung zur seriellen Schnittstelle über den
- Pipe-Treiber (siehe Punkt e) her.
- Sie ermöglicht Ihnen, Daten mittels Read/ReadLn von der
- Schnittstelle zu lesen und mittels Write/Writeln an diese zu
- senden.
-
- Diese Unit ist in der separaten Dokumentation V24PIPE.DOK be-
- schrieben.
-
-
- Kommen wir nun zur Anwendung: Die Einbindung des Multi-Tasking
- Subsystems in eigene Programme gestaltet sich sehr einfach. Voraus-
- setzung ist, daß die im Lieferumfang enthaltenen Units dür den
- Turbo-Compiler verfügbar gemacht werden. Bitte entnehmen Sie die im
- Einzelnen hierfür erforderlichen Schritte Ihrem Turbo-Pascal Handbuch.
-
- Mittels einer USES-Anweisung, binden Sie die jeweils benötigten
- Units dann in Ihr Anwendungsprogramm ein. Hierbei ist darauf zu
- achten, daß CPMULTI stets die erste der Units des Subsystems sein
- muß. Die Reihenfolge, in der die übrigen Units eingebunden werden
- müssen sowie die Abhängigkeiten zwischen Ihnen, entnehmen Sie bitte
- dem Kapitel 8 (Abhängigkeiten).
-
- Sollten Sie zusätzlich Units aus dem Turbo-Pascal Basispaket (z. B.
- Crt oder DOS) benötigen, so sollten diese VOR CPMULTI in der
- USES-Liste stehen.
-
- Beispiel: USES Crt, Dos, CPMulti, MtPopUp, Queue;
-
- ACHTUNG!
-
- Die Units CPMULTI und MTPOPUP verwenden ihrerseits die Unit CRT.
- Dies führt zu Konflikten, wenn Sie das Turbo Professional Paket von
- TurboPower Software verwenden, welches eine alternative Unit TpCRT
- zur Bildschirmbehandlung anbietet. In diesem Fall sind die vorge-
- nannten Units unter Definition des Compiler-Conditionals TpCRT neu
- zu übersetzen. In den vorangegangenen Subsystemversionen,
- befanden sich im Lieferumfang jeweils auch UNITS, die unter
- Einbindung von TpCRT übersetzt wurden. Da jedoch die Turbo
- Professional Tools mittlerweile nicht mehr als UNITS, sondern
- nur noch im Quellcode ausgeliefert werden und zudem mehrere
- verschiedene Versionen im Umlauf sind, waren diese UNITS nur
- selten mit den jeweiligen Turbo Professional UNITS der Anwender
- funktionsfähig (UNIT version mismatch). Die Version 2.10
- enthält daher diese speziellen Versionen nicht mehr.
-
- Sollten Sie die Objektcode-Version des Subsystems erworben
- haben und daher nicht die Möglichkeit haben, die Subsystem-UNITS
- neu zu übersetzen, biete ich Ihnen an, mir Ihre Version von TPCrt
- zuzusenden. Sie erhalten dann gegen Erstattung der Unkosten
- (ca. 20 DM incl. Versand) eine für Ihre Konfiguration passende
- Objektversion zugesandt.
-
- Bitte beachten Sie auch, daß TpCRT, wie auch die Standard-CRT Unit,
- VOR CPMULTI in der USES-Liste erscheinen muß. Dies ist insbesondere
- bei der Verwendung von MTPOPUP erforderlich, da dort einige in TpCRT
- enthaltene Funktionen redefiniert werden, um den Systemdurchsatz zu
- erhöhen.
- Die .PRO-Versionen wurden unter Einbindung der Version 5.x des Turbo
- Professional Paketes erstellt. Sollten Sie bei der Einbindung Ihrer
- Version von TPCRT Schwierigkeiten haben, so müssen Sie den Quellcode
- des Subsystems erwerben und die Units neu übersetzen.
-
- Weiterhin ist zu beachten, daß jede Task über einen eigenen, priva-
- ten Stack verfügt und nur das PASCAL-Hauptprogramm den Turbo-Stack
- verwendet. Daher wird das Turbo-Laufzeitsystem unmittelbar nach dem
- Erzeugen der ersten Task einen Stack-Fehler feststellen, sofern Sie
- keine besonderen Vorkehrungen treffen, um dies zu vermeiden. Hierzu
- ist es erforderlich, GRUNDSÄTZLICH den Compilerschalter S- zu
- verwenden. Dieser unterbindet die Generierung von Code zur Prüfung
- auf Stack-Überlauf. Dadurch geht Ihnen zwar in der Entwicklungsphase
- eine zusätzlicher Kontrollmechanismus verloren, jedoch wäre ihnen
- dieser ohnehin nur im Hinblick auf das Hauptprogramm, nicht jedoch
- auf die Subtasks von Nutzen.
- Das Multi-Tasking Subsystem enthält aber eine Prozedur, die Ihnen
- den aktuellen Zustand der Task-Table auf dem Bildschirm anzeigt.
- Diese Auflistung enthält für alle Tasks, mit Ausnahme des Hauptpro-
- grammes, die Anzahl noch freier Bytes auf dem Stack. Weiterhin
- meldet Ihnen das System den Stacküberlauf einer Task zur Laufzeit
- sowohl optisch als auch akustisch, bevor es die betreffende Task als
- "Crashed" markiert und deaktiviert.
- Ich denke, unter diesen Umständen läßt sich der Verlust der Stack-
- Prüfung durch Turbo-Pascal durchaus verschmerzen.
-
-
- Bereits durch die Eintragung der Unit CPMULTI in Ihre USES-Klausel,
- aktivieren Sie das Multi-Tasking für Ihr Programm. Der Initialisie-
- rungsteil der Unit startet unmittelbar nach dem Programmaufruf das
- Subsystem und aktiviert Ihr Pascal-Hauptprogramm als Task Nr. 2. Die
- Tasknummer 1 wird durch eine niedrig priorisierte Endlosschleife
- belegt, die immer dann ausgeführt wird, wenn gerade keine Nutztask
- zur Bearbeitung zur Verfügung steht.
-
- Für das Abschalten des Multi-Tasking brauchen gleichermaßen keine
- Vorkehrungen getroffen werden, da dies automatisch durch die Exit-
- Prozedur der Subsystem-Unit erledigt wird. Dies gilt sowohl für ein
- normales Programmende, als auch für einen Programmabbruch durch
- Ctrl-Break oder einen Laufzeitfehler.
-
- Zu einem totalen Systemabsturz wird es nur in außergewöhnlich
- schweren Fällen kommen, wenn durch einen Programmfehler z. B. die
- internen Verwaltungsstrukturen beschädigt, oder der private Stackbe-
- reich einer Task durch z. B. eine fehlerhafte Pointeroperation
- überschrieben wurde.
-
- Bevor Sie nun aber bis über beide Ellenbogen in die Programmierung
- einsteigen, empfehle ich Ihnen, das gesamte Handbuch gründlich zu
- studieren!!!
- Nicht nur wird dieses Ihnen ein tieferes Verständis von den Hinter-
- gründen und Möglichkeiten des Subsystems geben, sondern Ihnen auch
- kostbare Stunden der Fehlersuche ersparen!
- .SE CPMULTI / Einleitung
- III. Das Basissystem (CPMULTI)
-
-
- 1. Einleitung
-
- Im Hinblick auf die immer größere Verbreitung von UNIX und UNIX-ähn-
- lichen Betriebssystemen und insbesondere in Anbetracht der bevorste-
- henden OS/2-Invasion, kann es für denjenigen, dem das Interesse an
- Multitaskingsystemen nicht fehlt, wohl aber das zur Anschaffung
- eines solchen erforderliche Kapital, nur nützlich sein, wenn er den
- Umgang mit parallelen Prozessen und deren Synchronisation bereits
- unter DOS mit Hilfe einer so preisgünstigen Entwicklungsumgebung wie
- Turbo-Pascal erlernen kann.
- Das Turbo-Pascal Multi-Tasking Subsystem stellt Ihnen alle Funktio-
- nen zur Verfügung, die Sie dazu benötigen. Auch für die Entwicklung
- von Anwendungssystemen aus dem Bereich der Meß- und Regeltechnik sowie
- der Datenkommunikation, ist dieses Produkt hervorragend geeignet.
-
- Die folgenden Abschnitte der Dokumentation geben einen Abriß der
- internen Arbeitsweise des Multi-Tasking Subsystems. Bedingt durch
- den Umfang und die Vielschichtigkeit der Thematik, bleibe ich
- allerdings die ganze Zeit über recht nahe an der Oberfläche. Dies
- stellt zwar die Verständlichkeit meiner Ausführungen sicher, wird
- jedoch die Tüftler unter Ihnen kaum richtig befriedigen. Aus diesem
- Grunde habe ich am Ende dieser Dokumentation einige Quellen genannt,
- in denen weitaus tiefgehendere und umfassendere Informationen zu den
- angesprochenen Themen zu finden sind und die mir selbst bei der
- Entwicklung dieses Produktes eine unschätzbare Hilfe waren.
- .SE CPMULTI / Konzept
- 2. Konzept
-
- Entsprechend dem Vorbild von UNIX und OS/2, wurde das Subsystem als
- Time-Sharing System und NICHT für Real-Time Programmierung entwor-
- fen. Dennoch ist die Reaktionszeit des Subsystems, insbes. im
- Bereich der Ereignisverarbeitung, vielfach auch für Echtzeitanwen-
- dungen ausreichend kurz.
- In erster Linie geht es jedoch darum, die vorhandene CPU-Leistung
- möglichst gerecht auf alle im System vorhandenen Prozesse zu vertei-
- len. Sobald ein Prozeß, der sich gerade im Zustand der Abarbeitung
- befindet, seine Zeitscheibe aufgebraucht hat, wird ihm zwangsweise
- die CPU entzogen und ein anderer auf CPU-Zuteilung wartender Prozeß
- aktiviert (preemptive scheduling).
- Eine in den Hardware-Timer-Interrupt eingeklinkte Systemroutine
- kontrolliert mehrmals in der Sekunde die Zeitzuteilung und leitet
- ggf. einen Task-Wechsel (Task-Switch) ein.
-
- In den Vorgängerversionen war die minimale Größe einer Zeitscheibe
- auf ca. 55,5 Millisekunden festgelegt, wobei ein Standardwert von
- 110 Millisekunden voreingestellt war. Dies entspricht der Interrupt-
- frequenz der Systemuhr des PC.
- Auch die Version 2.02 behält diese Voreinstellung bei, da sie sich
- in der Mehrzahl der Anwendungsfälle als optimal herausgestellt hat.
- Gerade im Bereich der Datenkommunikation jedoch, ist selbst ein 18
- Mal in der Sekunde ausgeführter Taskwechsel (ca. 55,5ms je Zeit-
- scheibe) zu groß. Aus diesem Grunde wurde in der vorliegenden
- Version die Möglichkeit geschaffen, die Zeitscheibengröße "beliebig"
- zu verringern.
- "Beliebig", da ab dem Faktor 6 (ca. 9,25ms je Zeitscheibe) auf
- langsameren Rechnern Interrupt-Überläufe auftreten können, die zwar
- vom System abgefangen werden, jedoch unnötig CPU-Zeit verbrauchen,
- ohne dadurch eine weitere Geschwindigkeitssteigerung zu erzielen.
-
- Ab der Version 2.10 des Subsystems wurde die Möglichkeit
- eingeführt, das preemptive Verhalten des Schedulers zu
- unterbinden. Eine globale Konstante (Preempt) kontrolliert, ob
- der Ablauf einer Zeitscheibe zu einem zwangsweise CPU-Entzug
- führen darf oder nicht. Wird diese auf FALSE gesetzt, obliegt
- es allein dem Programmierer durch geeignete Synchronisation
- seiner Tasks das gewünschte Taskwechselverhalten zu erzeugen.
-
-
- Dynamisches Scheduling
-
- Nun kann es aber aufgrund der Ausführung unter DOS passieren, daß
- sich zu demjenigen Zeitpunkt, an dem ein Prozeß sein Quantum aufge-
- braucht hat, das Betriebssystem DOS in einem kritischen Zustand
- befindet. Dies ist z. B. immer dann der Fall, wenn gerade ein
- DOS-Funktionsaufruf bearbeitet wird.
- Würde nun der aktive Prozeß ungeachtet dieses Umstands suspendiert,
- so führte ein nachfolgender DOS-Funktionsaufruf durch einen anderen
- Prozeß unweigerlich zu einem Systemabsturz übelster Sorte. Aus
- diesem Grunde, darf ein Task-Switch immer nur dann erfolgen, wenn
- sichergestellt ist, daß sich das Betriebssystem in einem konsisten-
- ten Zustand befindet selbst dann, wenn dadurch der aktive Prozeß
- sein Quantum um einen nicht vorhersehbaren Wert überschreitet.
- Die Zeitüberschreitung ist deshalb nicht vorhersehbar, weil theo-
- retisch ein Programm, welches sehr viele DOS-Aufrufe absetzt, in der
- Zeit bis zur nächsten Überprüfung durch den Scheduler bereits die
- nächste DOS-Funktion aufgerufen haben könnte, was dann wiederum zu
- einer neuerlichen Überschreitung des Quantums um mindestens einen
- Timer-Tick führt.
-
- Das andere Extrem wäre ein Prozeß, der jede Sekunde genau ein Mal
- eine zeitunkritische Abfrage auf einen bestimmten Port durchführt
- (Meßdatenerfassung, Alarmanlagensteuerung, ...) und sich dann,
- sollte kein Eingriff erforderlich werden, wieder eine Sekunde lang
- schlafen legt. Ein derartiger Prozeß würde nur sehr selten seine
- Zeitscheibe aufbrauchen, jedoch im Falle eines notwendig gewordenen
- Eingriffs, ebenfalls nach Ablauf seines Zeitquantums suspendiert. Er
- würde also, insgesamt gesehen, gegenüber einem "Dauerläufer" in
- Hinblick auf die Zeitzuteilung benachteiligt.
-
- Um die Konsequenzen dieser Extremfälle, von denen letzterer weit
- häufiger auftreten dürfte, ein wenig abzuschwächen, wurde ein in
- gewissen Grenzen dynamisches Scheduling realisiert.
- Ein Prozeß, der sein Quantum überzieht, wird solange bei der CPU-
- Zuteilung übergangen, bis die zuvor zuviel erhaltene Zeit verbraucht
- ist. Andererseits erhält ein Prozeß, der seine Zeitscheibe permanent
- nicht aufbraucht, die überzählige Zeit gutgeschrieben und darf bei
- der nächsten Zuteilung entsprechend länger laufen.
- Um die zu "sühnende" bzw. gutgeschriebene Zeit nicht ins Unendliche
- anwachsen zu lassen (dies kann insbes. bei der Gutschrift gesche-
- hen), wird eine maximale Anzahl +/- Ticks definiert, auf die diese
- Dynamik beschränkt wird.
- Beide Werte, Zeitscheibengröße und Grenze für dynamisches Schedu-
- ling, sind voll programmierbar. Standardmäßig ist eine Zeitschei-
- bengröße von zwei Timer-Ticks mit eine Grenze für die dynamische
- Zeitzuteilung von +/- 10 Ticks eingestellt.
-
-
- Prioritätensteuerung
-
- Ein weiterer Mechanismus zur Tasksteuerung, ist die Vergabe von
- Prioritäten.
- Das Subsystem kennt 9 verschiedene Prioritätsebenen. Für drei dieser
- Ebenen sind symbolische Namen verfügbar:
-
- Pri_Nice = Priorität 1
- Pri_User = Priorität 5
- Pri_Kernel = Priorität 9
-
- Die übrigen Ebenen lassen sich entweder als absoluter Zahlenwert
- oder als "Nuance" einer der drei Standard-Ebenen darstellen (z. B.
- Pri_User+1). Die mittlere Priorität ist "Pri_User" und wird auto-
- matisch auch für das Pascal-Hauptprogramm vergeben.
-
- Der Scheduler behandelt die unterschiedlichen Prioritätsebenen
- wiefolgt: Solange sich in der Ebene "Pri_Kernel" ausführungsbereite
- Tasks befinden, wird die CPU NIEMALS einer Task zugeteilt, die sich
- in den Ebenen darunter befindet.
- Innerhalb einer Prioritätsebene werden die Tasks nach dem Round-
- Robin Prinzip zur Verarbeitung ausgewählt, d. h. sie kommen reihum
- zum Zuge.
-
- Für den Programmierer hat diese Art des Schedulings natürlich
- Konsequenzen. Beim Design der Anwendung, muß er die einzelnen Tasks
- entwerfen und einer der 9 Prioritätsebenen zuteilen. Hierbei wird u.
- U. eine Entscheidung getroffen, die für den späteren Systemdurchsatz
- von grundlegender Bedeutung ist. Dies Erstentscheidung ist jedoch
- nicht endgültig, da die Prioritäten zur Laufzeit beliebig geändert
- werden können.
-
- An dieser Stelle möchte ich nur einmal kurz anmerken, daß ich im
- Folgenden die Begriffe "Task" und "Prozeß" als Synonyme verwende,
- wenngleich dies vermutlich nicht bei allen Lesern auf Zustimmung
- stoßen wird. Da man sich jedoch auch in den einschlägigen Veröffent-
- lichungen zum Thema Multi-Tasking über die Wortwahl nicht ganz einig
- zu sein scheint, denke ich, mir diese Freiheit nehmen zu dürfen.
-
- Es empfielt sich, Tasks, die die meiste Zeit ihrer Existenz damit
- verbringen, auf das Eintreten eines Ereignisses zu warten (Zeichen
- von der Schnittstelle, Tastatureingaben, ...), mit sehr hoher
- Priorität zu versehen.
- Andererseits sollten Tasks, die beständig durchlaufen und ihre
- Zeitscheibe vermutlich stets voll auskosten werden, in die Standard-
- ebene User gestellt werden.
- Tasks mit niedrigster Priorität werden wohl nur sehr selten zur
- Anwendung kommen. In der Ebene Nice läuft in der Regel nur ein
- Null-Prozess ab, der die Aufgabe hat, die CPU zu beschäftigen,
- solange keine anderen Prozesse verfügbar sind.
-
-
- Exclusivrechte
-
- Befinden sich mehrere Task mit gleicher Priorität im System,
- von denen einzelne keinesfalls durch den Ablauf einer
- Zeitscheibe unterbrochen werden dürfen, so war es bisher nur
- möglich, für den gesamten kritischen Verarbeitungsabschnitt die
- CPU zu blockieren. Dies führte dazu, daß auch Timer und
- Ereignisse in dieser Zeit nicht erkannt wurden. Auch wurde die
- verbrauchte Zeit nicht im Task-Descriptor angerechnet.
-
- Ab der Version 2.10 ist es möglich, für jede Task einen sog.
- Exclusiv-Modus zu aktivieren. Solange die Task diesen nicht
- wieder verläßt, wird sie nicht durch Ablauf ihrer Zeitscheibe
- und nicht durch eintretende Ereignisse unterbrochen. Timer und
- Ereignisse werden aber bearbeitet und die verbrauchte CPU-Zeit
- wird im Task-Descriptor vermerkt.
-
- Der Exclusiv-Modus wirkt grundsätzlich nur auf Task-Ebene.
- Gibt eine Task im Exclusiv-Modus die CPU durch Aufruf einer
- blockierenden System-Funktion (z. B. SemWait()) auf, so wird
- dies in ihren Task-Descriptor eingetragen. Wird sie zu einem
- späteren Zeitpunkt reaktiviert, so setzt sie die Verarbeitung
- im Exclusiv-Modus fort.
-
- Tasks, die sich nicht im Exclusiv-Modus befinden, werden
- weiterhin durch Ablauf ihrer Zeitscheibe unterbrochen (es sei
- denn, das Time-Slicing wurde generell abgeschaltet).
-
-
- Ein wunder Punkt mit Konsequenzen
-
- Ein wunder Punkt des vorliegenden Multi-Tasking Subsystems ist das
- Fehlen von interruptgesteuertem Platten-I/O. Multi-Tasking Betriebs-
- systeme verfahren bei I/O-Operationen von und zu Plattenlaufwerken
- so, daß nach Absetzen der I/O-Anforderung an die entsprechende
- Hardware, der anfordernde Prozeß solange in Wartezustand versetzt
- wird, bis ein die Hardware per Interrupt signalisiert, daß die
- Anforderung vollständig bearbeitet ist (vereinfachte Darstellung!).
- In der Zwischenzeit kann die CPU an andere Prozesse vergeben werden.
- Aus diesem Grunde wird der Systemdurchsatz u. U. erheblich verbes-
- sert, wenn I/O-intensive Prozesse mit sehr hoher Priorität abgear-
- beitet werden; sie warten die meiste Zeit auf die Beendigung ihrer
- I/O-Anforderungen und behindern so die übrigen Tasks nicht.
-
- DOS, als Single-Tasking Betriebssystem, kennt diese Problematik
- nicht und besitzt somit keinerlei Möglichkeiten, die CPU während des
- Wartens auf das Beenden einer I/O-Anforderung an andere Prozesse zu
- vergeben. Das Multi-Tasking Subsystem verfügt nicht über ein eige-
- nes, interruptgesteuertes Disk-Handling, bedient sich also der BIOS,
- bzw. DOS-Routinen zur Durchführung von Plattenoperationen. Da
- grundsätzlich kein Task-Switch während der Ausfürung einer DOS
- Funktion oder des Platten-Interrupts 13H erfolgen darf, wird eine
- I/O-intensive Task, die mit hoher Priorität gefahren wird, voraus-
- sichtlich das ganze System lahmlegen!!!!
-
- Mittlerweile verfügen viele AT-Systeme über einen Interrupt-
- Mechanismus (Post/Wait-Interrupt 15H), der des einem
- Betriebssystem erlauben soll, bei Verwendung der
- BIOS-Plattenroutinen dennoch die CPU in Wartephasen an andere
- Prozesse zu vergeben. MS-DOS, als Single-Task System, ignoriert
- diesen Mechanismus selbstverständlich.
-
- Eine interne Version des Multi-Tasking Subsystems, die über
- einen eigenen Post/Wait-Handler verfügte, hat gezeigt, daß die
- Ausnutzung der Wartezeiten bei Plattenzugriffen zwar möglich
- ist, jedoch eine vollständig anderen Mechanismus zur
- Überwachung des internen DOS-Zustands erfordert und insgesamt
- weniger sicher ist.
-
- Es war letztendlich eine Gewissensentscheidung, zugunsten
- erhöhter Betriebssicherheit des Produktes, eine geringfügig
- reduzierte Systemauslastung bei starkem Aufkommen von
- Plattenzugriffen in Kauf zu nehmen. Ich denke, hiermit in Ihrem
- Sinne gehandelt zu haben.
-
-
- Interprozeßkommunikation
-
- Was die Mechanismen zur Interprozeßkommunikation anbelangt, so
- stehen Ihnen neben Semaphoren auch Message-Passing-Funktionen
- und Resourcen zur Verfügung.
- Das so unkomplizierte und daher auch so beliebte "Shared-Memory"
- (gemeinsam genutzte Speicherbereiche) erfordert keine gesonderte
- Systemunterstützung, da, bedingt durch die Struktur eines Turbo-
- Pascal Programmes, alle Tasks ein gemeinsames Datensegment und einen
- gemeinsamen Heap besitzen. Der gleichzeitige Zugriff auf gemeinsam
- genutzte Speicherbereiche muß allerdings durch geeignete
- Semaphoren-Operationen synchronisiert werden (siehe hierzu Demopro-
- gramm PRO_CON).
- Vielfach kann bereits die Unit MtPipe mit ihrem Pipe-Treiber für den
- Datenaustausch zwischen einzelnen Tasks genutzt werden, wodurch der
- überwiegede Teil des Synchronisationsaufwands bereits automatisch
- erledigt wird.
-
- Programmierbare Timer
-
- Eine weitere, häufig benötigte Funktion ist der Timer. Das Multi-
- Tasking Subsystem stellt eine nur durch den zur Verfügung stehenden
- dynamischen Speicher begrenzte Anzahl von Timern mit einer
- Auflösung, die der jeweils aktuell eingestellten minimalen
- Zeitscheibengröße entspricht, zur Verfügung. Bedingt durch die
- Konzeption der Timer-Überwachung, bleibt der hierfür erforderliche
- Aufwand ungeachtet der Anzahl gleichzeitig aktiver Timer konstant
- (und minimal).
-
- Es stehen Ihnen zwei verschiedene Arten von Timern zur Verfügung.
- Die Eine bewirkt, daß nach Ablauf eines Timers, das an der bei der
- Anforderung übergebenen Speicherstelle befindliche Byte incremen-
- tiert wird. Diese Form des Timers ist z. B. hervorragend für die
- Timeout-Überwachung in DFUE-Anwendungen geeignet.
-
- Weiterhin haben Sie die Möglichkeit, sog. Watchdog-Timer zu
- definieren, die nach Ablauf des Timers den automatischen Aufruf
- einer bei der Definition des Timers übergebenen Prozedur bewirken.
- Diese Form der Timer wurde z. B. innerhalb des Kernels verwendet, um
- den Systemaufruf "Sleep" zu realisieren.
-
-
- Ereignisverarbeitung
-
- Als zeitweise recht nützlich, hat sich eine weitere Systemfunktion
- erwiesen: Das Ereignis. Ein Prozeß, der ein Ereignis anfordert,
- wird solange blockiert, bis das angeforderte Ereignis eingetreten
- ist.
-
- Es stehen Ihnen zwei verschiedene Ereignisformen zur Verfügung.
- Die erste Form ermöglicht das Warten auf die Veränderung einer
- bestimmten Speicherstelle. Entstanden ist diese Funktion, um den
- Tail-Pointer eines Ringpuffers überwachen zu können, der durch eine
- Interrupt-Routine mit Daten gefüllt wurde.
-
- Die zweite Variante wartet darauf, daß der an einem beliebigen Port
- anliegende Wert sich verändert (z. B. durch die Betätigung eines
- Tasters).
-
- Da bei jedem Timer-Tick (d. h. mit der Frequenz der aktuell
- eingestellten minimalen Zeitscheibe) den Eintritt des Ereignisses
- überprüft wird, ist insbes. für diesen Bereich die Möglichkeit der
- Erhöhung der Interrupt-Frequenz interessant.
-
- Ab der Version 1.30 sorgt der Systemkern dafür, daß eine Task, die
- durch ein Ereignis aus einem Wartezustand geweckt wird, an den Kopf
- der Scheduler-Queue ihrer Pritoritätsebene gestellt wird. So kann
- sie sehr rasch auf das eingetretene Ereignis reagieren, auch wenn
- noch andere Tasks derselben Priorität ausführungsbereit sind.
- Ab der Version 2.00 steht eine optimierte Verwaltung der Ereignista-
- belle zur Verfügung, wodurch die maximale Anzahl gleichzeitig
- aktiver Ereignisanforderungen erheblich erhöht wurde. Die Zeiter-
- sparnis bei der Ereignisprüfung wird jedoch durch eine geringfügig
- erhöhten Zeitbedarf bei der Einfügung/Löschung einer Ereignisanfor-
- derung in die/aus der Ereignistabelle erkauft.
- .SE CPMULTI / Realisierung
- 3. Realisierung
-
- Mit Ausnahme eines ca. 2,5 KB großen Assembler-Kerns, der die
- besonders maschinennahen Programmteile enthält, ist das gesamte
- Subsystem als Turbo-Pascal 5.x Unit (TPU) realisiert worden.
-
-
- Das Herz des Systems, die Task-Table
-
- Das Kernstück des Subsystems bildet die Prozeßtabelle (Task-Table).
- Diese ist aus Gründen der Performance als statische Tabelle im
- Datensegment definiert und derzeit auf 50 Einträge begrenzt. Durch
- Änderung der Konstanten MaxTasks und Neuübersetzung der Unit, läßt
- sich dieser Wert problemlos erhöhen.
-
- Dies Task-Table enthält für jede mögliche Task einen Eintrag, den
- sogenannten Task-Deskriptor. Der Aufbau dieser Verwaltungsstruktur
- wird im Referenzteil näher betrachtet; für den Augenblick soll die
- Feststellung genügen, daß in dieser Struktur alle Angaben abgelegt
- sind, die den jeweiligen Ausführungszustand einer Task wiederspie-
- geln.
-
- Eine Task kann sich zu jedem Zeitpunkt in einem von 8 Zuständen
- befinden. Der jeweilige Zustand ist in einem Feld des Task-Deskrip-
- tors abgelegt und über einen Systemaufruf zugänglich. Folgende
- Zustände sind bekannt:
-
- Running..: Diese Task besitzt gerade die Kontrolle.
-
- Ready....: Diese Task ist ausführungsbereit, wartet jedoch
- augenblicklich auf CPU-Zuteilung.
-
- Sleeping.: Diese Task hat sich selbst für eine gewisse Zeit
- suspendiert
-
- Crashed..: Das Subsystem hat einen Stacküberlauf für diese Task
- erkannt und sie aus dem Verkehr gezogen; der Stack ist
- jedoch erhalten und kann z.B. mit einem Debugger unter-
- sucht werden.
-
- Waiting..: Diese Task wartet auf eine Semaphore oder ein
- Ereignis.
-
- Sending..: Diese Task wartet darauf, daß diejenige Task, an
- die sie eine Nachricht gesendt hat, die Nachricht in
- Empfang nimmt.
-
- Receiving: Diese Task wartet auf eine Nachricht von einer
- anderen Task.
-
- Suspended: Diese Task wurde von einer anderen Task zeitweise
- inaktiv gesetzt. Sie kann auch nur von einer andern Task
- wieder in den Ready-Zustand versetzt werden.
-
- Ein weiterer Zustand (Available) zeigt einen leeren Slot der Task-
- Table an.
-
- Der nun folgende Zustandsgraph zeigt alle Zustände und die möglichen
- Übergänge zwischen ihnen auf. Hierbei ist ein Übergang immer nur in
- Richtung einer Pfeilspitze denkbar.
-
-
- ┌───────────┐ 13 ┌───────────┐
- ┌──────>│ Running ├───────┬──────>│ Crashed │
- │ └─────┬─────┘ │ └───────────┘
- │ │ │
- 2│ 1│ │
- │ │ └────────────┐
- │ │ │
- ┌─────┴─────┐ │ 4 ┌───────────┐ │
- │ Ready │<──────┴─────┬─┤ Waiting │<─┐3 │
- └───────────┘ │ ├───────────┤ │ │
- 6├─┤ Sleeping │<─┤5 │
- │ ├───────────┤ │7 │
- 8├─┤ Sending │<─┼───┘
- │ ├───────────┤ │
- 10├─┤ Receiving │<─┤9
- │ ├───────────┤ │
- 12└─┤ Suspended │<─┘11
- └───────────┘
-
- Zustandsgraph, Multi-Tasking Subsystem Version 2.10
-
-
- Verwaltung der Tasks
-
- Im System befindliche Tasks werden logisch über Queues verwaltet,
- die als verkettete Listen realisiert sind. Das Subsystem
- unterscheidet hierbei
-
- a) die Task-Queues je Prioritätsebene, die ausschließlich Tasks in
- den Zuständen "Running" und "Ready" enthalten,
- b) die Sender-Queue je Task-Deskriptor, in die diejenigen Tasks
- eingereiht werden, die eine Message an den entsprechenden Prozeß
- senden und auf deren Empfang warten möchten (Zustand "Sending").
-
- Tasks in den Zuständen "Sleeping", "Suspended", "Receiving" und
- "Crashed" sind in keine Queue eingereiht.
- Tasks im Zustand "Waiting" hängen entweder in der Event-Table oder
- in der Warteschlange zu einer Semaphore bzw. Resource.
-
- Wann immer eine neue Task erzeugt wird, wird ihr ein freier Slot der
- Task-Table zugewiesen, dessen Nummer gleichzeitig zur Task-Nummer
- wird. Einige der Systemfunktionen erhalten diese Task-Nummer als
- Parameter.
- Weiterhin erhält jede Task einen privaten Stack, der automatisch auf
- dem Heap angelegt und initialisiert wird.
- Nur wenn ein freier Task-Table-Slot verfügbar ist und die für den
- Stack angeforderte Speichermenge vom Heap zugeordnet werden kann,
- verläuft ein CreateTask-Systemaufruf erfolgreich. Er liefert dann
- die Task-Nummer als Funktionswert.
-
-
- Code-Sharing
-
- Man beachte, daß Turbo-Pascal in hohem Maße Code generiert, der
- reentrant ist und sich somit durchaus für Code-Sharing eignet.
- Code-Sharing bedeutet einerseits, daß ein und dieselben Unterrouti-
- nen von mehreren Tasks gleichzeitig genutzt werden, wobei zu einem
- Zeitpunkt ein und derselbe Code-Bereich theoretisch mehrmals paral-
- lel durchlaufen werden kann.
- Andererseits kann Code-Sharing jedoch auch bedeuten, daß ein und
- dieselbe PASCAL-Prozedur mehrmals als Task aktiviert werden kann,
- wodurch sich zu einem Zeitpunkt ein und derselbe Programmcode, mit
- jeweils einem eigenen Registersatz und einem privaten Stack, mehr-
- mals nebeneinander in Bearbeitung sein kann.
-
- Es ist bei der Verwendung von Code-Sharing allerdings durch geeig-
- nete Synchronistationsmechanismen, z. B. Semaphoren, dafür Sorge zu
- tragen, daß kritische Bereiche immer nur von einer Task zu einem
- Zeitpunkt durchlaufen werden können. Bei den Prozeduren/Funktionen
- der Standard-Units ist dies nicht gewährleistet.
- Nehmen wir die WriteLn-Prozedur als Beispiel, so kann es durchaus
- passieren, daß während der Ausgabe eines Strings ein Task-Wechsel
- auftritt. Die Task, welche als nächste die Kontrolle erhält, ruft
- nun ihrerseits ebenfalls WriteLn auf, um einen String auszugeben.
- Nun, das Ergebnis ist zwar kein Programmabsturz, jedoch mit Sicher-
- heit auch nicht das, was ursprünglich beabsichtigt war.
-
- Mittlerweile habe ich beide beschriebenen Arten von Code-Sharing
- ausprobiert und bin bislang auf keine technischen Probleme gestoßen.
-
-
- Programmierbare Timer
-
- Kommen wir nun zum Thema Timer: Ein Timer, der bei jedem Tick
- heruntergezählt und auf Ablauf untersucht werden muß, stellt für den
- Systemkern natürlich eine zusätzliche Belastung dar. Die CPU-Zeit,
- die für derartige Verwaltungsaufgaben verbraucht wird, steht auf der
- anderen Seite konsequenterweise den Anwendungsprozessen nicht mehr
- zur Verfügung.
- Bedenkt man nun, daß z. B. bei Kommunikationsanwendung mitunter
- recht viele Timer gleichzeitig aktiv sein können, so stellt sich
- schon die Frage nach einer "CPU-schonenden" Implementierung.
- Das in diesem System verwirklichte Konzept ist NICHT auf meinem
- eigenen Mist gewachsen; es ist der Timer-Behandlung in MINIX nachem-
- pfunden, einem UNIX-ähnlichen Betriebssystem für IBM-PC/XT/AT und
- 100% kompatible Clones von Andrew S. Tanenbaum.
- MINIX sei übrigens jedem, der tiefer in die Betriebssystemprogram-
- mierung einsteigen möchte, wärmstens als herrvorragendes Studienma-
- terial empfohlen. Ohne dieses System und die zugehörige Grundlagen-
- literatur, wäre mir die Programmierung des vorliegenden Subsystems
- sicherlich nicht innerhalb so kurzer Zeit möglich gewesen.
-
- Die Timer-Queue ist so organisiert, daß immer nur ihr erstes Element
- bei einem Timer-Tick überprüft werden muß. Ist der erste Timer
- abgelaufen, so wird er bearbeitet und aus der Queue entfernt. Der
- nachfolgende Timer rückt an seine Stelle usw., usw. Dies erfordert
- zwar etwas mehr Aufwand bei der einmaligen Einfügung eines Timers in
- die Queue, reduziert jedoch den bei jedem Uhreninterrupt erforder-
- lich werdenden Aufwand auf ein Minimum.
-
-
- Beispiel:
- ┌─────┬─┐ ┌─────┬─┐ ┌─────┬─┐
- Timer-Queue ───>│ 3 │─┼───>│ 5 │─┼───>│ 1 │─┼─┤
- └─────┴─┘ └─────┴─┘ └─────┴─┘
- Wartezeit: 3 Ticks 8 Ticks 9 Ticks
-
-
- Ereignisse
-
- Bevor ich näher auf die zur Verfügung stehenden Mechanismen zur
- Inter-Prozeß-Kommunikation eingehe, noch ein Wort zu den Ereignis-
- sen.
- Sicherlich ist eine ganze Reihe von Ereignissen denkbar, die es
- lohnt, vom Subsystem her zu unterstützen. In der vorliegenden
- Version des Subsystems wird allerdings lediglich die Überwachung
- einer Speicherstelle bzw. eines Ports auf Veränderung unterstützt.
-
- Wozu benötige ich ein Ereignis? Ein Ereignis kann den Systemdurch-
- satz bisweilen erheblich steigern, da der auf das Eintreten dieses
- Ereignis wartende Prozeß nicht in einer Warteschleife CPU-Zeit
- verbraucht, sondern solange suspendiert wird, bis der Systemkern das
- Ereignis registriert.
- Der konkrete Anwendungsfall, der mich zur Implementierung des
- Ereignisses anregte, war ein Kommunikationsprogramm, in dem eine
- Task einen Ringpuffer auslesen sollte, der durch eine Interrupt-
- Routine gefüllt wird.
-
- Die Interrupt-Routine wird von der Hardware jedesmal aktiviert, wenn
- ein Zeichen im Schnittstellenbaustein zum Auslesen bereitsteht, ein
- Zustand also, der sehr oft sehr schnell hintereinander auftreten
- kann. Demzufolge ist die Interrupt-Routine derart kurz zu halten,
- daß für das Pflegen einer Semaphore keine Zeit bleibt.
- Um eine schnelle Bearbeitung der in den Ringpuffer eingespeisten
- Zeichen zu erreichen (und einen Pufferüberlauf zu vermeiden), muß
- die Task, die für die Behandlung dieses Puffer zuständig ist, mit
- höchster Priorität gefahren werden. Dies jedoch setzt voraus, daß es
- Punkte gibt, an denen sie die Kontrolle abgibt und Tasks auf einer
- niedrigeren Prioritätsebene zu Zuge kommen läßt. Das wiederum wird
- niemals der Falls sein, wenn sie stets in einer Endlosschleife den
- Zustand der Puffer-Indizes abfragt.
- Sicher könnte sich die "Puffer-Task" immer dann für einen gewissen
- Zeitraum suspendieren (Sleep()), wenn der Zustand der Indizes
- anzeigt, daß keine Daten zur Bearbeitung anstehen, jedoch führt dies
- dazu, daß eine schnelle Reaktion auf während einer "Schlafphase"
- hereinkommende Zeichen unmöglich wird. Ja es könnte sogar der
- Ringpuffer übergelaufen sein, bevor die "Puffer-Task" wieder aus
- ihrem Schlaf erwacht.
-
- Ich denke, es dürfte klar geworden sein, daß die gerade geschilderte
- Situation ohne weitergehende Systemunterstützung nicht befriedigend
- lösbar ist. Mit Hilfe des Ereignisses ist es nun möglich, die
- "Puffer-Task" auf eine Veränderung desjenigen Rinpuffer-Index warten
- zu lassen, der von der Interrupt-Routine verändert wird. Solange,
- bis diese Veränderung eintritt, bleibt die betreffende Task inaktiv.
- Bei jedem Timer-Tick überprüft nun das System die Ereignistabelle
- und reaktiviert ggf. die Task deren Ereignis eingetreten ist.
- Auch hierbei muß mit einer gewissen Verzögerung gerechnet werden,
- jedoch liegt diese zumeist (wenn gerade keine DOS-Aktivitäten einen
- Task-Switch verhindern) nur im Bereich eines Timer-Ticks, also in
- einem vertretbaren Rahmen.
-
-
- Inter-Prozeß-Kommunikation
-
- Kommen wir nun zum interessantesten Funktionsbereich, den Funktionen
- zur Inter-Prozeß-Kommunikation, im Folgenden nur noch mit IPC
- abgekürzt.
-
- Wie bereits erwähnt, stellt das Multi-Tasking-Subsystem hier sowohl
- das Message-Passing, als auch Semaphoren zur Verfügung. Es würde zu
- weit führen, an dieser Stelle die Hintergründe dieser Mechanismen
- erschöpfend behandeln zu wollen, weshalb ich hierzu auf geeignete
- Literatur hinweisen möchte (siehe hierzu Literaturhinweise).
-
-
- A. Message-Passing
-
- Die Funktionen des Message-Passing sind sehr einfach und klar in
- ihrer Arbeitsweise. Wünscht eine Task an eine andere eine Nachricht
- zu senden, so wird sie in die Sende-Queue der Empfänger-Task einge-
- reiht, sofern der Empfänger gerade nicht empfangsbereit ist.
- "Empfangsbereit" ist eine Task immer dann, wenn sie mittels eines
- Systemaufrufes den Empfang einer Nachricht von diesem SPEZIELLEN
- SENDER oder aber von IRGENDEINER Task angefordert hat. Wollte der
- Empfänger von einer ganz bestimmten Task empfangen, so bleiben alle
- anderen Sender unberücksichtigt!! Soll von irgendeiner anderen Task
- empfangen werden, so werden die Sender in derjenigen Reihenfolge
- bearbeitet, in der sie ihre Sendeanforderung gestellt haben.
-
- Zusätzlich wurde die Möglichkeit realisiert, augenblicklich mit
- einem entsprechenden Returncode zurückzukehren, wenn die Gegenstelle
- nicht empfangsbereit ist oder (im Falle des Empfangsaufrufes) keine
- Sendeanforderung zur Bearbeitung ansteht.
-
-
- B. Semaphoren
-
- Semaphoren sind ein Thema, über das mittlerweile derart viel ge-
- schrieben wurde, daß ich an dieser Stelle nur ihre interne Darstel-
- lung näher erläutern werde.
-
- Intern besteht eine Semaphore aus einem zwei Byte langen Signal-
- Count, der beim Anlegen der Semaphore auf den Wert 1 gesetzt wird
- und einer Warteschlange, in die anfordernde Prozesse bei Bedarf
- eingereiht werden.
- Ein Wert größer Null repräsentiert grundsätzlich einen Frei-Status,
- der Wert Null selbst stellt den Belegt-Status dar.
- Wann immer eine Semaphore, deren Signal-Count derzeit den Wert Null
- besitzt mit einem Wait-System-Call angesprochen wird, wird der
- anfordernde Prozeß solange suspendiert, bis der Signal-Count durch
- einen anderen Prozeß logisch erhöht wird. Letzteres führt dann zu
- einer Reaktivierung des ersten wartenden Prozesses, bzw. zu einer
- tatsächlichen Erhöhung des Signal-Count, falls keine wartenden
- Prozesse anstehen.
-
- Ungeachtet der internen Darstellung, werden Semaphoren innerhalb der
- Anwendung nur mit Hilfe von untypisierten Pointern (Typ Pointer)
- angesprochen. Ein derartiger Pointer wird beim Erzeugen einer
- Semaphore zurückgeliefert und dient fortan als Handle in allen
- Funktionsaufrufen, die sich auf diese Semaphore beziehen. Die
- maximale Anzahl gleichzeitig aktiver Semaphoren ist nur durch den
- zur Verfügung stehenden Heap-Space begrenzt.
-
- C. "Named Pipes"
-
- Pipes (Röhren) sind Kommunikationskanäle zwischen einzelnen Tasks.
- Hierbei verläuft die Kommunikation immer in einer Richtung, d. h.
- ein Partner schreibt in die Pipe und der andere liest daraus. Für
- eine bidirektionale Kommunikation zwischen zwei Tasks sind also immer
- zwei Pipes erforderlich.
-
- Pipes sind ein sehr einfaches Medium für den Datenaustausch zwischen
- unterschiedlichen Tasks. Der Pipe-Treiber übernimmt hierbei die
- Aufabe, den Zugriff auf den Datenbereich der Pipe zu synchronisieren,
- so daß die Kommunikationspartner selbst von dieser Aufgabe befreit
- sind.
-
- Ähnlich den UNIX Named Pipes, können auch im Multi-Tasking Subsystem
- durchaus mehrere Prozesse gleichzeitig in eine Pipe schreiben bzw.
- aus einer Pipe lesen. Die Pipe wird wie eine TEXT-Datei angesprochen,
- d. h. alle Zugriffe auf die Pipe werden mittels der Pascal
- Standardprozeduren für Textdateizugriffe durchgeführt.
-
- Bitte entnehmen Sie die Einzelheiten der separaten Dokumentation zur
- Unit MTPipe.
-
- D. Resourcen
-
- Resourcen verhalten sich ähnlich wie Semaphoren, jedoch ist in
- der internen Datenstruktur die Prozeßnummer des jeweiligen
- Eigentümers der Resource vermerkt. Fordert der Eigentümer
- selbst die Resource erneut an (z. B. durch rekursiven Aufruf),
- so wird er dadurch nicht blockiert, sondern der Aufruf wird
- ignoriert.
-
- Weiterhin hat der Programmierer bei der Verwendung von
- Resourcen die Möglichkeit, beim Request-Aufruf anzugeben, ob
- die betreffende Task auf das Freiwerden der Resource warten,
- oder mit einem Fehlercode vom Aufruf zurückkehren und ihre
- Verarbeitung fortsetzen soll.
-
- Der Eigentümer einer Resource hat neben der vollständigen
- Freigabe der Resource die Möglichkeit, diese zwischenzeitlich an
- exakt eine wartende Task abzugeben und unmittelbar nach der
- Freigabe durch letztere selbst wieder die Eigentumsrechte zu
- erwerben. Diese Funktionalität entstand allerdings aus einem
- speziellen Anwendungsfall heraus und wird nur selten sinnvoll
- einsetzbar sein.
-
-
- Hinweis zur Prozeßsynchronisation
-
- Zum Abschluß dieses Abschnitts noch ein Hinweis: Wie bereits er-
- wähnt, sind für die Verwendung der Funktionen aus den Standard-Units
- (z. B. WriteLn) besondere Vorkehrungen zu treffen was die Absiche-
- rung kritischer Bereiche betrifft. Dies gilt mitunter nicht nur für
- einzelne Funktionen, sondern auch für logisch zusammengehörige
- Operationen.
- Möchte z. B. eine Task an der Bildschirmposition 1,10 ein Zeichen
- ausgeben, eine andere Task jedoch ein Zeichen an der Position 20,12,
- so muß sichergestellt sein, daß der Ablauf "Cursorpositionierung
- (GotoXY) und Ausgabe des Zeichens (Write)" nicht durch eine Aktion
- unterbrochen werden kann, die in irgendeiner Art und Weise in diesen
- Vorgang eingreift. Es darf also niemals geschehen, daß genau zwi-
- schen der Cursorpositionierung auf 1,10 und VOR der Ausgabe des
- Zeichens an dieser Stelle, die zweite Task den Cursor mal eben
- schnell für eigene Zwecke auf die Bildschirmposition 20,12 bewegt.
-
- Diese Konfliktsituation läßt sich auf zwei verschiedene Arten lösen:
- Einerseits kann man, wie in der Prozedur WriteCharXY im Demoprogramm
- PRO_CON.PAS gezeigt, die CPU für den kritischen Zeitraum blockieren.
- Diese Vorgehensweise ist jedoch nur bei sehr kleinen Abschnitten
- ratsam, da durch die Blockierung der CPU auch Funktionen wie z. B.
- Timer- und Ereignisüberwachung sowie die Zeitscheibensteuerung
- zeitweise außer Kraft gesetzt werden.
- In der Mehrzahl der Fälle ist einer Lösung mit Hilfe von Semaphoren,
- wie in den Funktionen RBuffGet und RBuffPut gezeigt, der Vorzug zu
- geben. Hier wird diejenige Task, die in den kritischen Bereich
- eintreten möchte, während dieser gerade von einer anderen Task
- durchlaufen wird, solange suspendiert, bis letztere den kritschen
- Bereich verläßt.
- .SE CPMULTI / Exportierte Typ- und Datendefinitionen
- 4. Exportierte Typ- und Datendefinitionen
-
- Globale Datentypen:
-
- Priority = Byte;
-
- Dieser Typ wird zur Festlegung der Task-Priorität bei der Erzeu-
- gung der Task angegeben.
-
-
- WaitFlagType = (Wait, NoWait);
-
- Dieser Typ wird als Parameter in den Funktionen des Message-Pas-
- sing verwendet und legt dort fest, ob im Falle fehlender Sende-/
- Empfangsbereitschaft gewartet wird, oder eine sofortige Rückkehr
- mit Fehlercode gewünscht ist.
-
-
- TaskType = PROCEDURE(TaskParm:Pointer);
-
- Jede Task muß als FAR-Prozedur dieses Typs codiert werden.
-
-
- TaskReturn = (Task_Crashed, Task_NotFound, Task_NoMsg,
- Task_NotReady, Task_OK, Task_Invalid);
-
- Returncodes für die Message-Passing Funktionen.
-
- Task_Crashed...: Die angesprochene Task steht aufgrund eines
- Stacküberlaufs nicht mehr zur Verfügung
- Task_NotFound..: Es existiert keine aktive Task mit
- dieser Nummer
- Task_NoMsg.....: Es steht keine Nachricht an (Receive
- mit NoWait)
- Task_NotReady..: Die angesprochene Task ist nicht
- empfangsbereit (Send mit NoWait)
- Task_OK........: Aktion erfolgreich
- Task_Invalid...: ungültige Task-Nummer; wahrscheinlich
- außerhalb des zulässigen Bereiches 1..MaxTasks
-
-
- TaskStatus = (Running, Ready, Sleeping, Available, Crashed,
- Waiting, Sending, Receiving, Suspended);
-
- Returncode, der von der Funktion GetTaskState geliefert wird und
- den Zustand der angesprochenen Task wiederspiegelt.
-
-
- SemReturn = (Sem_NoSpace, Sem_NotFree, Sem_OK, Sem_Invalid);
-
- Returncodes der Semaphoren-Funktionen.
-
- Sem_NoSpace..: Kein dynamischer Speicherplatz zum Anlegen der
- Semaphore mehr vorhanden
- Sem_NotFree..: Versuch eine Semaphore zu löschen, in deren
- Warteschlange sich noch Tasks befinden
- Sem_OK.......: Aktion erfolgreich
- Sem_Invalid..: ungültiges Semaphoren-Handle (voraussichtlich
- NIL)
-
-
- RscReturn = (Rsc_NoSpace, Rsc_NotFree, Rsc_OK, Rsc_NotOwner,
- Rsc_Invalid);
-
- Rsc_NoSpace..: kein Heap vorhanden
- Rsc_NotFree..: Belegt und/oder wartende Tasks bei DeleteResource
- Rsc_Ok.......: Aktion erfolgreich
- Rsc_NotOwner.: Versuchte Freigabe durch eine andere Task als den
- Eigentümer
- Rsc_Invalid..: Übergabe eines NIL-Pointers
-
-
- TaskNoType = Integer;
-
- Dies ist der Typ, in dem die Task-Nummer dargestellt wird. Die
- Redefinition des Typs Integer wurde vorgenommen, um Ihr Programm
- von einer evtl. später erforderlich werdenden Änderung in diesem
- Bereich unabhängig zu machen.
-
-
- BPtr = ^Boolean;
-
- Definition, die von GetTimBusy benötigt wird.
-
-
- PointerType = RECORD CASE Integer OF
- 1: (P : Pointer);
- 2: (POfs : Word;
- PSeg : Word);
- END;
-
- Redefinition eines Pointers, um an den Segment- und den Offset-
- teil zu gelangen.
-
-
- WatchDogType = PROCEDURE(WatchDogParm:Pointer);
-
- Eine Prozedur dieses Typs kann als Parameter an die Funktionen
- QueueWatchDog und CancelWatchDog übergeben werden.
-
-
- Globale Konstanten
-
- Task_NoSpace
-
- Returncode der Funktion CreateTask, der anstelle der Task-Nummer
- zurückgeliefert wird, wenn kein dynamischer Speicher zum Anlegen
- des Stacks vorhanden ist.
-
-
- Task_NoSlot
-
- Returncode der Funktion CreateTask, der anstelle der Task-Nummer
- zurückgeliefert wird, wenn die maximale Anzahl gleichzeitig
- aktiver Tasks überschritten wird.
-
-
- StateText
-
- Array welches den Task-Status im Klartext enthält und mit einen
- Index vom Typ TaskStatus angesprochen wird. Diese Tabelle wird
- z. B. von der Prozedur DumpTaskTable benötigt.
-
-
- AnyTask
-
- Task-Nummer, die im Receive-Aufruf angegeben wird, wenn eine
- Nachricht von einer beliebigen anderen Task empfangen werden
- soll.
-
- Preempt : Boolean = True;
-
- Standardmäßig führt das Multi-Tasking Subsystem einen
- Task-Wechsel durch, wann immer die Zeitscheibe einer Task
- abgelaufen ist und das System sich in einem konsistenten
- Zustand befindet. Für spezielle Anwendungen kann dieses
- Verhalten hinderlich, ja sogar gefährlich sein.
- Ist es erforderlich, daß der Programmierer volle Kontrolle
- darüber ausübt, wann ein Task-Wechsel durchgeführt werden
- darf, so kann Preempt auf FALSE gesetzt, und damit der
- Task-Wechsel durch Zeitscheibenablauf unterbunden werden.
-
-
- Globale Variablen
-
- InInt28 (Boolean)
-
- Dies ist ein Flag, das von einem externen Interrupt-Handler für
- den DOS-Multitasking Interrupt 28H gesetzt werden kann, um dem
- Subsystem anzuzeigen, daß ein Task-Wechsel nun gefahrlos möglich
- ist, obwohl das DOS-Critical-Flag gerade gesetzt ist. Diese
- Variable wird von der aufbauenden Unit MtPopUp benötigt.
- .SE CPMULTI / Interne Datenstrukturen
- 5. Wichtige interne Datenstrukturen und Konstanten
-
- Dieses Kapitel ist nur in der Dokumentation der lizenzierten Version
- von CpMulti enthalten.
- .SE CPMULTI / Referenzteil
- 6. Funktionsbeschreibung (alphabetisch)
-
-
- .RE BindCPU
- Deklaration
-
- PROCEDURE BindCPU;
-
- Funktion
-
- Die Prozedur BindCPU dient zum zeitweisen Blockieren aller
- Kernel-Funktionen. Insbesondere ist BindCPU für den Kernel selbst
- vonnöten, wenn Operationen durchgeführt werden müssen, die eine
- logisch unteilbare Einheit darstellen, jedoch aus mehreren
- Anweisungen bestehen. Dies ist z. B. immer bei der Veränderung
- einer Semaphore der Fall.
-
- Beispiel
-
- ...
- BindCPU; {CPU blockieren}
- GotoXY(1,10); {untrennbare Aktion}
- Write('*');
- ReleaseCPU; {CPU freigeben}
- ...
-
- Anmerkungen
-
- BindCPU legt SÄMTLICHE Kernel-Funktionen lahm, d. h. auch die
- Timer- und Zeitscheibensteuerung sowie die Ereignisüberwachung.
- Aus diesem Grunde sollte BindCPU nur für sehr kleine Programmab-
- schnitte verwendet werden, anderenfalls ist eine Lösung mit
- Semaphoren vorzuziehen.
-
- Siehe auch
-
- ReleaseCPU
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE CancelTimer
- Deklaration
-
- FUNCTION CancelTimer(MemPtr:Pointer):BOOLEAN;
-
- Funktion
-
- Ist ein Timer aktiv, der nach Ablauf die angegebene Speicherstel-
- le incrementiert, so wird dieser deaktiviert und der Funktions-
- wert TRUE geliefert. Der Funktionswert FALSE zeigt an, daß kein
- derartiger Timer gefunden wurde.
-
- Beispiel
-
- VAR Timeout : Boolean;
- Ok : Boolean;
- ...
- Timeout := False; {Initialisierung}
- IF QueueTimer(@Timeout,Seconds(1)) {Timer anfordern}
- THEN BEGIN
- REPEAT {Warteschleife}
- ... {Aktionen}
- UNTIL Timeout OR Ok;
- IF CancelTimer(@Timeout) THEN; {Timer ggf. löschen}
- ... {weitere Aktionen}
- END;
-
- Siehe auch
-
- QueueTimer
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE CancelWatchdog
- Deklaration
-
- FUNCTION CancelWatchDog(TWatchDog:WatchDogType):BOOLEAN;
-
- Funktion
-
- Löschen eines WatchDog Timers aus dem System, sofern dieser
- vorhanden ist.
- Der Parameter "TWatchDog" muß derselbe sein, der auch beim
- entsprechenden Aufruf von "QueueWatchDog" angegeben wurde.
- Die Funktion liefert TRUE, wenn der Timer erfolgreich entfernt
- wurde. FALSE wird zurückgeliefert, wenn entweder kein entspre-
- chender Timer gefunden wurde oder aber ein ungültiger Parameter
- übergeben wurde.
-
- Anmerkungen
-
- Sind mehrere WatchDog Timer mit derselben Prozeduradresse aktiv,
- so wird durch jeden CancelWatchDog Aufruf der jeweils erste
- dieser Timer gelöscht.
-
- Siehe auch
-
- Typdefinitionen WatchDogType, QueueWatchDog
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE ChangePri
- Deklaration
-
- PROCEDURE ChangePri(Task:TaskNoType; Pri:Priority);
-
- Funktion
-
- Mittels "ChangePri" kann eine Task die Priorität einer anderen
- Task verändern, vorausgesetzt, sie kennt deren Task-Nummer.
- "Task" enthält die ID derjenigen Task, deren Priorität verändert
- werden soll; "Pri" enthält die neue Priorität.
-
- Siehe auch
-
- Typdefinitionen Priority und TaskNoType, SetPri
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE CreateRsc
- Deklaration
-
- FUNCTION CreateRsc(VAR RscPtr:Pointer):RscReturn;
-
- Funktion
-
- Erzeugen einer Resource. Das Handle, unter dem diese Resource
- zukünftig angesprochen werden kann, wird in der Variablen RscPtr
- abgelegt. Der Returncode zeigt Erfolg oder Mißerfolg der Aktion
- an.
-
- Beispiel
-
- VAR Resource : Pointer; {Handle}
- ...
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
-
- Siehe auch
-
- Typdefinition RscReturn, RemoveRsc, RemoveRscKill
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE CreateSem
- Deklaration
-
- FUNCTION CreateSem(VAR SemPtr:Pointer):SemReturn;
-
- Funktion
-
- Erzeugen einer Semaphore. Das Handle, unter dem diese Semaphore
- zukünftig angesprochen werden kann, wird in der Variablen SemPtr
- abgelegt. Der Returncode zeigt Erfolg oder Mißerfolg der Aktion
- an.
-
- Beispiel
-
- VAR Semaphore : Pointer; {Handle}
- ...
- IF CreateSem(Semaphore) <> Sem_Ok
- THEN Error;
-
- Siehe auch
-
- Typdefinition SemReturn, RemoveSem, RemoveSemKill
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE CreateTask
- Deklaration
-
- FUNCTION CreateTask(TaskPointer:TaskType; UserData:Pointer;
- Level:Priority; BytesStack:Word):TaskNoType;
-
- Funktion
-
- Mit dieser Funktion wird eine Task erzeugt. Der Parameter "Task-
- Pointer" muß die Adresse derjenigen Prozedur enthalten, die als
- Task aktiviert werden soll.
- Der zweite Parameter, "UserData", ist ein untypisierter Pointer,
- der der Task bei ihrem Start als Parameter übergeben wird.
- Der Parameter "Level" beschreibt die Prioritätsebene, in die die
- Task eingereiht werden soll. Durch den letzen Parameter wird
- schlußendlich die Größe des privaten Stacks in Byte mitgeteilt.
- Die Funktion CreateTask liefert einen positiven Wert vom Typ
- TaskNoType wenn die Task erfolgreich angelegt werden konnte.
- Anderenfalls zeigt der Returncode (dann negativ), die genaue
- Fehlerursache an.
-
- Beispiel
-
- {$F+}
- PROCEDURE SubTask(Parm:Pointer);
- {
- Dies ist eine eigene Task, die jede Sekunde einen Piepston
- erzeugt.
- }
- BEGIN {SubTask}
- REPEAT {Taskrumpf ─┐ }
- Sleep(Seconds(1)); { │ }
- Sound(1000); { Aktionen │ }
- Delay(20); { : │ }
- NoSound; { │ }
- UNTIL False; {Taskrumpf ─┘ }
- END; {SubTask}
- {$F-}
- ...
- ...
- BEGIN {Main}
- IF CreateTask(SubTask,NIL,Pri_User,300) < 0
- THEN Error;
- ...
- END. {Main}
-
- Anmerkungen
-
- Eine Task wird programmtechnisch grundsätzlich als Prozedur vom
- Typ "TaskType" realisiert, deren Rumpf aus einer Endlosschleife
- gebildet wird!!! Ferner muß die Prozedur als FAR-Prozedur codiert
- werden (siehe Turbo Pascal Handbuch, $F+/- Compiler-Schalter).
- Ab der Version 2.00 des Subsystems, darf eine Task sowohl durch
- expliziten Aufruf der Prozedur "Terminate", als auch durch
- Verlassen der Task-Prozedur beendet werden. Im letzteren Fall
- wird "Terminate" implizit aufgerufen.
- Eine Task kann durchaus lokale Variablen besitzen, jedoch ist bei
- der Dimensionierung des Stacks zu berücksichtigen, daß lokale
- Variable grundsätzlich auf dem Stack angelegt werden. Der für
- diese benötigte Speicherplatz ist also der Stackgröße, die für
- die Ausführung der Task erforderlich ist, hinzuzuaddieren.
- Ein weiteres Faktum, das Beachtung verdient, ist die Tatsache,
- daß der Parameter "BytesStack" in KEINER WEISE auf Gültigkeit
- geprüft wird. Es obliegt also dem Programmierer, sicherzustellen,
- daß dieser Wert stets größer als Null ist. Der Grund für diese
- Vorgehensweise ist der, daß für das Pascal-Hauptprogramm, welches
- ebenfalls als Task bei der Systeminitialisierung aktiviert wird,
- kein Stack auf dem Heap reserviert wird. In diesem Fall ist also
- eine Stackgröße von Null als Parameter zulässig.
-
- Siehe auch
-
- Typdefinitionen für TaskType, TaskNoType und Priority, globale
- Konstanten, Terminate
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE DoShutdown
- Deklaration
-
- PROCEDURE DoShutdown;
-
- Funktion
-
- Deaktivieren des Multi-Tasking; Wiederherstellen aller
- abgefangenen Interrupts.
-
- Anmerkungen
-
- Diese Prozedur wird in der Regel ausschließlich durch die
- Exit-Prozedur der Subsystem-Unit aufgerufen.
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE DumpTaskTable
- Deklaration
-
- PROCEDURE DumpTaskTable;
-
- Funktion
-
- Ausgabe des aktuellen Zustands aller aktiven Tasks auf dem
- Bildschirm. Dies Funktion kann zu Diagnosezwecken aufgerufen
- werden.
-
- Beispiel
-
- Beispielhafte Bildschirmausgabe:
-
- Nr. Status Priorität Slice Stack free CPU Excl.
- 1 Ready Pri_Nice 2 265 0 FALSE
- 2 Ready Pri_User 5 -1 151 FALSE
- 3 Waiting Pri_Kernel 1 235 1 FALSE
- 4 Running Pri_Kernel -8 213 8 FALSE
- 5 Waiting Pri_Kernel 0 33 2 FALSE
-
-
- Anmerkungen
-
- Der aktuelle Bildschirminhalt wird zerstört. Soll ein Statusfen-
- ster realisiert werden, so ist die Realisierung mittels einer
- eigenen Routine, die den TaskStatus selbst abfragt, vorzuziehen.
- Die Spalte CPU beschreibt die Anzahl Ticks, die diese Task
- bereits verbraucht hat.
- Negative Slice-Werte sind "Gutschriften".
-
- Siehe auch
-
- Globale Konstante StateText, GetTaskState
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE EnterExclusive
- Deklaration
-
- FUNCTION EnterExclusive:BOOLEAN;
-
- Funktion
-
- Eintritt der Task in den Exclusiv-Modus. Der Funktionswert
- liefert den vorhergehenden Inhalt des Exclusive-Flags. So
- ist es z. B. möglich, Prozeduren zu schreiben, die für die
- Dauer ihrer Ausführung den Exclusiv-Modus einschalten und
- abschließend den vorhergehenden Zustand wiederherstellen.
- Die Funktion RemoveRscKill gehört zu dieser Kategorie.
-
- Beispiel
-
- PROCEDURE XYZ;
-
- VAR Ex : Boolean;
-
- BEGIN {XYZ}
- Ex := EnterExclusive;
- SometingUseful;
- IF Not Ex
- THEN LeaveExclusive;
- END; {XYZ}
-
- Siehe auch
-
- LeaveExclusive
-
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE EventWait
- Deklaration
-
- FUNCTION EventWait(MemPtr:Pointer):Boolean;
-
- Funktion
-
- Diese Funktion existiert ab der Version 2.00 des Subsystem nicht
- mehr. Sie wurde durch dir Funktionen "MemoryEventWait" und
- "PortEventWait" ersetzt.
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE GetPID
- Deklaration
-
- FUNCTION GetPID:TaskNoType;
-
- Funktion
-
- Diese Funktion erlaubt es einer Task, ihre eigene Task-Nummer zu
- ermitteln.
-
- Siehe auch
-
- Typdefinition TaskNoType
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE GetPri
- Deklaration
-
- FUNCTION GetPri:Priority
-
- Funktion
-
- Ermitteln der Priorität der aufrufenden Task. Diese Funktion kann
- verwendet werden, um temporär in Unterroutinen, die die ursprüng-
- liche Prioritätseinstellung des Aufrufers nicht kennen, die
- Priorität zu verändern.
-
- Beispiel
-
- PROCEDURE SomeStuff;
-
- VAR OldPri : Priority;
-
- BEGIN {SomeStuff}
- OldPri := GetPri; { sichern }
- SetPri(Pri_Kernel); { anheben }
- SomethingVeryImportant;
- SetPri(OldPri); { zurücksetzen }
- END; {SomeStuff}
-
- Siehe auch
-
- Typedefinition Priority, SetPri
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE GetTaskState
- Deklaration
-
- FUNCTION GetTaskState(Task:TaskNoType):TaskStatus;
-
- Funktion
-
- GetTaskState liefert den Status der Task, deren Task-Nummer als
- Parameter übergeben wurde.
-
- Beispiel
-
- Ausgabe des eigenen Task-Status im Klartext:
-
- Writeln(StateText[GetTaskState(GetPID)]);
-
- Siehe auch
-
- Typdefinitionen TaskStatus und TaskNoType sowie Konstante State-
- Text
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE GetTimBusy
- Deklaration
-
- FUNCTION GetTimBusy:BPtr;
-
- Funktion
-
- Diese Funktion liefert einen Zeiger auf das Timer-Busy-Flag, das
- immer dann gesetzt ist, wenn gerade der Scheduler aktiv ist oder
- durch BindCPU das Kernsystem die CPU blockiert wurde.
- Dieses Busy-Flag muß zuweilen für externe Interrupt-Service
- Routinen zugänglich sein, wie sie z. B. auch in der Unit MtPopUp
- zu finden sind.
- Wann immer das Timer-Busy-Flag gesetzt ist, darf eine Inter-
- rupt-Routine KEINE Systemfunktion aufrufen!!
-
- Siehe auch
-
- Typdefinition BPtr
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE GrantRsc
- Deklaration
-
- FUNCTION GrantRsc(RscPtr:Pointer):RscReturn;
-
- Funktion
-
- Durch den Aufruf von GrantRsc gewährt der aktuelle
- Eigentümer der Resource der ersten, auf diese Rescource
- wartenden Task den Zugang zu derselben.
- Sobald diese die Resource freigibt, wird der Aufrufer wieder
- zum Eigentümer, ungeachtet weiterer wartender Tasks.
-
- Ist der Aufrufer nicht der Eigentümer der Resource, erhält
- er den Returncode Rsc_NotOwner zurückgeliefert; eine Aktion
- findet nicht statt.
-
- Beispiel
-
- VAR Resource : Pointer;
-
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
- .........
-
- IF RequestRsc(Resource,Wait) <> Rsc_Ok {Resource anfordern}
- THEN Error;
- .........
- { kritischer Bereich Teil 1 }
- .........
- IF GrantRsc(Resource) <> Rsc_Ok {laß jemand anderen}
- THEN Error; {zu Wort kommen}
- .........
- { kritischer Bereich Teil 2 }
- .........
- IF ReleaseRsc(Resource) <> Rsc_Ok {Resource freigeben}
- THEN Error;
- .........
-
- Siehe auch
-
- Typdefinition RscReturn, RequestRsc, ReleaseRsc
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Kill
- Deklaration
-
- PROCEDURE Kill(Task:TaskNoType);
-
- Funktion
-
- Die Funktion Kill bewirkt ein sofortiges Entfernen der Task mit
- der Task-Nummer "Task" aus dem System.
- Befindet sich die Task gerade in einer Warteschlange (z. B. der
- Warteschlange einer Semaphore), so wird sie aus dieser entfernt.
-
- Anmerkungen
-
- Ein Aufruf von "Kill" mit der eigenen Task-Nummer entspricht dem
- Aufruf der "Terminate" Prozedur.
-
- Siehe auch
-
- Typdefinition TaskNoType, Terminate
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE LeaveExclusive
- Deklaration
-
- PROCEDURE LeaveExclusive;
-
- Funktion
-
- Verlassen des Exclusiv-Modus.
-
- Siehe auch
-
- EnterExclusive
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE MemoryEventWait
- Deklaration
-
- FUNCTION MemoryEventWait(MemPtr:Pointer):Boolean;
-
- Funktion
-
- Warte auf die Veränderung der Speicherstelle, auf die MemPtr
- zeigt. Ein Funktionswert von TRUE zeigt eine erfolgreiche Been-
- digung des Vorhabens an; der Wert FALSE wird geliefert, wenn die
- maximale Anzahl gleichzeitig aktiver Ereignisse überschritten
- wurde.
-
- Beispiel
-
- VAR HeadPointer : Word; {Ringpufferzeiger}
- TailPointer : Word; { " }
- ...
- IF NOT MemoryEventWait(@TailPointer) {warte auf Änderung}
- THEN Error {des Zeigers}
- ELSE Verarbeite;
- ...
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE PortEventWait
- Deklaration
-
- FUNCTION PortEventWait(EPortNo:Word;VAR ENewValue:Byte):Boolean;
-
- Funktion
-
- Warte auf die Veränderung des Wertes, der an dem angegebenen Port
- anliegt. Der neue Wert wird in der Variablen "ENewValue" abgelegt
- und so der anfordernden Task verfügbar gemacht.
- Ein Funktionswert von TRUE zeigt eine erfolgreiche Beendigung des
- Vorhabens an; der Wert FALSE wird geliefert, wenn die maximale
- Anzahl gleichzeitig aktiver Ereignisse überschritten wurde.
-
- Beispiel
-
- VAR Taster : Byte; {Bit-Maske}
- ...
- IF NOT PortEventWait(TasterPort,Taster) {warte auf Druck}
- THEN Error {eines Tasters}
- ELSE Verarbeite(Taster);
- ...
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE QueueTimer
- Deklaration
-
- FUNCTION QueueTimer(MemPtr:Pointer; Counter:Word):BOOLEAN;
-
- Funktion
-
- Aktivieren eines Timers, der nach Ablauf von "Counter" Timer-
- Ticks das Byte an der Speicherstelle, auf die "MemPtr" zeigt,
- incrementiert.
- Ist ausreichen Speicherplatz zum Anlegen eines Timers vorhanden,
- so wird der Funktionswert TRUE geliefert; anderenfalls FALSE.
-
- Beispiel
-
- VAR Timeout : Boolean; {Timeout-Merker}
- ...
- Timeout := False; {Initialisieren}
- IF NOT QueueTimer(@Timeout,Seconds(1)) {Timeout nach 1 Sek.}
- THEN Error;
- ...
- IF CancelTimer(@Timeout) {Löschen des Timers}
- THEN ; {falls noch vorh.}
- ...
-
- Siehe auch
-
- CancelTimer
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE QueueWatchDog
- Deklaration
-
- FUNCTION QueueWatchDog(TWatchDog:WatchDogType; Counter:LongInt;
- WatchDogParm:Pointer):BOOLEAN;
-
- Funktion
-
- Aktivieren eines Timers, der nach Ablauf von "Counter" Timer-
- Ticks die Prozedur aufruft, auf die "TWatchDog" zeigt.
- Der Parameter "WatchDogParm" wird transparent an die aufgerufene
- Prozedur weitergegeben.
- Ist ausreichen Speicherplatz zum Anlegen eines Timers vorhanden,
- so wird der Funktionswert TRUE geliefert; anderenfalls FALSE.
-
- Beispiel
-
- {$F+}
- PROCEDURE Dog(P:Pointer);
- BEGIN {Dog}
- Writeln('Wau Wau!');
- END; {Dog}
- {$F-}
- ...
- IF NOT QueueWatchDog(Dog,Seconds(1),NIL) {Aufruf nach 1 Sek.}
- THEN Error;
- ...
- IF CancelWatchDog(WatchDog) {Löschen des Timers}
- THEN ; {falls noch vorh.}
- ...
-
- Anmerkungen
-
- Die WatchDog-Prozedur wird im "Kernel-Mode" aufgerufen, d. h.
- außerhalb jeglicher Task. Die CPU ist zu diesem Zeitpunkt
- blockiert und darf UNTER KEINEN UNSTÄNDEN freigegeben werden.
- Da nahezu jede Systemfunktion des Multi-Tasking Subsystems
- zeitweise die CPU blockiert und hinterher wieder freigibt
- (BindCPU / ReleaseCPU), darf KEINE dieser Funkionen/Prozeduren
- innerhalb des WatchDog verwendet werden.
- Bitte beachten Sie, daß das Subsystem KEINERLEI Möglichkeit
- besitzt, dies zu verhindern, so daß im Ernstfall ein System-
- absturz (durch reentrantes Aufrufen des Schedulers) oder zumin-
- dest ein höchst merkwürdiges Verhalten des Systems die Folge ist.
- Weiterhin ist zu beachten, daß alle Operationen des WatchDog den
- Kernel-Stack benutzen, der recht klein bemessen ist. Außerdem
- findet keine Unterbrechung des WatchDog durch den Scheduler
- statt und die verbrauchte CPU-Zeit wird keiner Task berechnet.
- Es empfiehlt sich also, WatchDog-Prozeduren sehr klein zu halten
- und nur dort einzusetzen, wo keine Subsystem-Aufrufe benötigt
- werden.
- Innerhalb des Subsystems wird diese Timer-Art dazu verwendet,
- einen schlafenden Prozeß nach Ablauf seiner Wartezeit wieder in
- seine Scheduler-Queue einzureihen. In dem bereits erwähnten
- Betriebssystem MINIX, wird ein WatchDog Timer z. B. dafür ein-
- gesetzt, um den Motor des Diskettenlaufwerkes auszuschalten, wenn
- seit dem letzten Zugriff eine bestimmte Zeitspanne verstrichen
- ist.
-
- Siehe auch
-
- Typdefinition WatchDogType, CancelWatchDog, BindCPU, ReleaseCPU
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE ReadySuspended
- Deklaration
-
- FUNCTION ReadySuspended(Task:TaskNoType):BOOLEAN;
-
- Funktion
-
- Versetze eine Task, die mittels Suspend aus der Scheduler-Queue
- entfernt wurde, wieder in den Ready-Zustand. Sie wird wieder an
- die Scheduler-Queue angehängt.
- Ein Funktionswert von TRUE, zeigt Erfolg an, ein Wert von FALSE
- signalisiert, daß die angesprochene Task nicht den erforderlichen
- Status hatte, d. h. nicht suspendiert war.
-
- Siehe auch
-
- Suspend
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Receive
- Deklaration
-
- FUNCTION Receive(FromTask:TaskNoType; MsgBuff:Pointer;
- WaitFlag:WaitFlagType):TaskReturn;
-
- Funktion
-
- Die Funktion Receive ermöglicht es einer Task, eine Nachricht von
- einer anderen Task zu empfangen. Hierbei enthält der Parameter
- "FromTask" die Task-Nummer des gewünschten Kommunikationspar-
- tners, oder "AnyTask", wenn der Sender der Nachricht ohne Belang
- ist. Wann immer eine gezielte Task-Nummer als Kommunikationspart-
- ner angegeben wird, bleiben Nachrichten, die von anderen Tasks
- gesendet werden unberücksichtigt.
- Der Parameter "MsgBuff" enthält die Anfangsadresse des Puffers,
- in den die empfangene Nachricht hineinkopiert wird. Hierbei wird
- KEINE Längenprüfung durchgeführt, d. h. es obliegt dem Program-
- mierer, sicherzustellen, daß der Pufferplatz ausreichend groß
- gewählt wird.
- Der Parameter "WaitFlag" entscheidet darüber, ob die empfangende
- Task blockiert wird, falls derzeit keine Nachricht ansteht, oder
- ob sie augenblicklich einen entsprechenden Returncode liefert.
- Der Wert "Wait" bewirkt eine Blockade; "NoWait" veranlaßt
- sofortige Rückkehr.
-
- Beispiel
-
- VAR Puffer : String; {Nachrichtenpuffer}
- ...
- IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok
- THEN Error
- ELSE Writeln(Puffer);
- ...
-
- Anmerkungen
-
- Mittels der Konstante "AnyTask", kann eine Task realisiert
- werden, die ihr gesamtes Leben damit verbringt, auf Nachrichten
- von der Außenwelt zu lauschen und diese ggf. zu verarbeiten. Von
- wem sie gerade eine Nachricht erhalten hat, kann sie durch Aufruf
- von ReceivedFrom ermitteln, um dem Sender auch eine Antwort
- zukommen zu lassen.
- Ein denkbarer Anwendungsfall wäre die Meßwerterfassung mittels
- mehrerer Meßfühler. Letztere werden durch jeweils eine eigene
- Task überwacht, die periodisch an die zentrale Verarbeitung eine
- Nachricht versendet.
-
- Siehe auch
-
- Konstante AnyTask, Typdefinition TaskReturn, Send, ReceivedFrom
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE ReceivedFrom
- Deklaration
-
- FUNCTION ReceivedFrom:TaskNoType;
-
- Funktion
-
- Diese Funktion ermittelt den Sender der zuletzt empfangenen
- Nachricht. Dies kann u. U. von Interesse sein, wenn eine Task
- ständig Nachrichten von ihrer Umwelt entgegennimmt, ungeachtet
- der Task-Nummern der Sender (AnyTask).
- Für die Reaktion auf die Nachrichten, insbesondere wenn eine
- Nachricht eine Antwort-Nachricht an den Sender erfordert, muß nun
- die Task-Nummer des Senders ermittelt werden können.
-
- Siehe auch
-
- Typdefinition TaskNoType, Receive
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE ReleaseCPU
- Deklaration
-
- PROCEDURE ReleaseCPU;
-
- Funktion
-
- Diese Prozedur macht eine CPU-Blockade durch BindCPU wieder
- rückgängig.
-
- Beispiel
-
- ...
- BindCPU; {CPU blockieren}
- GotoXY(1,10); {untrennbare Aktion}
- Write('*');
- ReleaseCPU; {CPU freigeben}
- ...
-
- Siehe auch
-
- BindCPU;
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE ReleaseRsc
- Deklaration
-
- FUNCTION ReleaseRsc(RscPtr:Pointer):RscReturn;
-
- Funktion
-
- Freigabe einer zuvor mittels RequestRsc belegten Resource.
- Ist der Aufrufer dieser Funktion nicht der Eigentümer der
- Resource, so wird der Returncode Rsc_NotOwner zurückgelie-
- fert; weitere Aktionen erfolgen nicht.
-
- Beispiel
-
- VAR Resource : Pointer;
-
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
- .........
- IF RequestRsc(Resource,Wait) <> Rsc_Ok {Resource anfordern}
- THEN Error;
- .........
- { kritischer Bereich }
- .........
- IF ReleaseRsc(Resource) <> Rsc_Ok {Resource freigeben}
- THEN Error;
-
- Siehe auch
-
- Typdefinition RscReturn, RequestRsc, GrantRsc
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE RemoveRsc
- Deklaration
-
- FUNCTION RemoveRsc(RscPtr:Pointer):RscReturn;
-
- Funktion
-
- Entfernen einer Resource aus dem System und Freigabe des von
- ihr belegten dynamischen Speichers.
- Eine Resource kann nur dann gelöscht werden, wenn sie
- aktuell keiner Task gehört. Ist dies nicht der Fall, so wird
- der Returncode Rsc_NotFree geliefert und die Resource bleibt
- unverändert.
-
- Beispiel
-
- VAR Resource : Pointer;
-
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
- LotsOfUsefulStuff;
- IF RemoveRsc(Resource) <> Rsc_Ok
- THEN Error;
-
- Siehe auch
-
- Typdefinition RscReturn, CreateRsc, RemoveRscKill
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE RemoveRscKill
- Deklaration
-
- FUNCTION RemoveRscKill(RscPtr:Pointer):RscReturn;
-
- Funktion
-
- Entfernen einer Resource aus dem System und Freigabe des von
- ihr belegten dynamischen Speichers.
- Alle noch auf diese Resource wartenden Tasks werden aus dem
- System entfernt. Dies gilt auch für den Eigentümer sofern er
- nicht selbst der Aufrufer von RemoveRscKill ist.
-
- Beispiel
-
- VAR Resource : Pointer;
-
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
- LotsOfUsefulStuff;
- IF RemoveRscKill(Resource) <> Rsc_Ok
- THEN Error;
-
- Siehe auch
-
- Typdefinition RscReturn, CreateRsc, RemoveRsc
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE RemoveSem
- Deklaration
-
- FUNCTION RemoveSem(VAR SemPtr:Pointer):SemReturn;
-
- Funktion
-
- Physisches Entfernen einer Semaphore aus dem System; der belegte
- HeapSpeicher wird wieder freigegeben.
- Der Parameter "SemPtr" entspricht dem Handle, das von CreateSem
- zurückgeliefert wurde. Der Returncode zeigt Erfolg oder Mißerfolg
- der Aktion an.
-
- Beispiel
-
- VAR Semaphore : Pointer; {Handle}
- ...
- IF CreateSem(Semaphore) <> Sem_Ok {Semaphore erzeugen}
- THEN Error;
- ...
- IF RemoveSem(Semaphore) <> Sem_Ok {und wieder löschen}
- THEN Error;
- ...
-
- Anmerkungen
-
- Bitte beachten Sie, daß es nicht immer möglich ist, die Gültig-
- keit des übergebenen Pointers zu überprüfen. Es obliegt also dem
- Programmierer sicherzustellen, daß ein Zeiger auf eine aktive
- Semaphore im Funktionsaufruf angegeben wird.
- Eine Semaphore, deren Warteschlange noch Tasks enthält, kann
- nicht gelöscht werden.
-
- Siehe auch
-
- Typdefinition SemReturn, CreateSem
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE RemoveSemKill
- Deklaration
-
- FUNCTION RemoveSemKill(VAR SemPtr:Pointer):SemReturn;
-
- Funktion
-
- Entfernen einer Semaphore aus dem System.
- Diese Funktion bedient sich RemoveSem, entfernt jedoch evtl. wartende
- Prozesse aus dem System.
-
- Anmerkungen
-
- Wenngleich diese Funktion die meiste Zeit über die CPU blockiert,
- so kann bei heftigem Zugriff auf die zu entfernende Semaphore u. U.
- dennoch der Fehler Sem_NotFree zurückgeliefert werden. Darauf kann
- der Aufrufer allerdings mit wiederholtem Aufruf von RemoveSemKill
- reagieren.
-
- Siehe auch
-
- Typdefinition SemReturn, RemoveSem
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE RequestRsc
- Deklaration
-
- FUNCTION RequestRsc(RscPtr:Pointer,
- WaitFlag:WaitFlagType):RscReturn;
-
- Funktion
-
- Anfordern einer Resource. Ist als Waitflag der Wert "Wait"
- angegeben, so blockiert der Aufrufer solange, bis die
- Resource verfügbar ist. Anderenfalls wird der Returncode
- Rsc_NotFree geliefert und die Verarbeitung fortgesetzt.
-
- Beispiel
-
- VAR Resource : Pointer;
-
- IF CreateRsc(Resource) <> Rsc_Ok
- THEN Error;
- .........
- IF RequestRsc(Resource,Wait) <> Rsc_Ok {Resource anfordern}
- THEN Error;
- .........
- { kritischer Bereich }
- .........
- IF ReleaseRsc(Resource) <> Rsc_Ok {Resource freigeben}
- THEN Error;
-
- Siehe auch
-
- Typdefinitionen RscReturn und WaitFlagType, RequestRsc, GrantRsc
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Sched
- Deklaration
-
- PROCEDURE Sched;
-
- Funktion
-
- Bedingungslose Aufgabe der Zeitscheibe durch sofortigen Taskwech-
- sel. Die aufrufende Task wird vom Zustand "Running" in den
- Zustand "Ready" versetzt.
- Sched dient vornehmlich zu internen Zwecken; in der praktischen
- Anwendung sollte zur Aufgabe der Zeitscheibe lieber Sleep(1)
- verwendet werden.
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Seconds
- Deklaration
-
- FUNCTION Seconds(Sec:Word):LongInt;
-
- Funktion
-
- Die Funktion Seconds liefert als Funktionswert die Anzahl Timer-
- Ticks, die die gewünschte Anzahl von Sekunden ausmachen. Dies hat
- den Vorteil, daß die Zeitscheibengröße verändert werden kann,
- ohne daß Ihre Programme davon beeinflußt werden.
-
- Beispiel
-
- IF QueueTimer(@Timeout,Seconds(1) SHR 1) { Timer auf eine }
- THEN; { halbe Sekunde }
-
- Siehe auch
-
- QueueTimer, CancelTimer
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemClear
- Deklaration
-
- PROCEDURE SemClear(SemPtr:Pointer);
-
- Funktion
-
- Setze den Signal-Count der angesprochenen Semaphore auf Null.
-
- Beispiel
-
- VAR Semaphore : Pointer; {Handle}
- ...
- IF CreateSem(Semaphore) <> Sem_Ok {Semaphore erzeugen}
- THEN Error
- ELSE SemClear(Semaphore); {und löschen}
-
- Anmerkungen
-
- Der Aufruf von SemClear wird intern als SemSet(Semaphore,0)
- realisiert.
-
- Siehe auch
-
- SemSignal, SemWait, SemClearWait, SemSet
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemClearWait
- Deklaration
-
- PROCEDURE SemClearWait(SemPtr:Pointer);
-
- Funktion
-
- Setze den Signal-Count der angesprochenen Semaphore auf Null und
- warte, bis er durch einen SemSignal-Aufruf einer anderen Task
- erhöht wird. Dieser Aufruf führt im Gegensatz zu SemWait IMMER zu
- einem Blockieren der aufrufenden Task.
-
- Beispiel
-
- VAR Ready : Pointer; {Semaphore }
- ...
- PROCEDURE SubTask;
- {
- Diese Task muß sich während ihrer Initialisierung Werte
- aus globalen Variablen merken, die dort vom Hauptprogramm
- für sie abgelegt wurden, jedoch im weiteren Verlauf des
- Hauptprogrammes verändert werden.
- Aus diesem Grunde darf das Hauptprogramm erst dann weiter
- ausgeführt werden, wenn es sicher sein kann, daß die Sub-
- task die Werte entnommen hat.
- }
- BEGIN {SubTask}
- ... {Initialisierung}
- SemSignal(Ready); {O.K. bin soweit}
- REPEAT
- ... {Taskrumpf }
- UNTIL False;
- END; {SubTask}
- ...
- ...
- BEGIN {Main}
- ...
- IF CreateSem(Ready) <> Sem_Ok {Semaphore erzeugen}
- THEN Error;
- IF CreateTask(@SubTask,Pri_User,300) < 0 {Task erzeugen }
- THEN Error
- ELSE SemClearWait(Ready); {warte auf Init. }
- ...
- END. {Main}
-
- Siehe auch
-
- SemClear, SemSet, SemSignal, SemWait
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemCut
- Deklaration
-
- FUNCTION SemCut(SemPtr:Pointer;Task:TaskNoType):BOOLEAN;
-
- Funktion
-
- Prüfe, ob die angegebene Task sich in der Warteschlange der
- Semaphore befindet und entferne sie ggf. aus dieser. Der Funk-
- tionswert TRUE zeigt Erfolg an, der Wert FALSE signalisiert
- Mißerfolg.
-
- Anmerkungen
-
- Der Zustand der Task wird von dieser Aktion in KEINER WEISE
- verändert. U. a. bedeutet dies auch, daß sie in keine andere
- Warteschlange eingereiht wird und demzufolge solgange "herrenlos"
- im System herumliegt, bis sie durch SemPaste() (und nur durch
- SemPaste) wieder in die Warteschlange einer Semaphore eingefügt
- wird. Diese Funktion ist ausschließlich für einen speziellen
- Anwendungsfall in der Unit MtPopUp hinzugefügt worden und sollte
- nur mit äußerster Vorsicht angewandt werden.
-
- Siehe auch
-
- SemPaste
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemDiag
- Deklaration
-
- PROCEDURE SemDiag(SemPtr:Pointer; Txt:String);
-
- Funktion
-
- Ausgabe des Zustands der angegebenen Semaphore. Es werden der
- Signal-Count sowie die Nummern der in der Warteschlagen
- befindlichen Tasks auf dem Bildschirm ausgegeben.
- Der als "Txt" übergebene String wird in der Überschrift der
- Diagnoseausgabe angezeigt. Der aktuelle Bildschirminhalt wird
- dabei überschrieben.
-
- Beispiel
-
- Beispielhafte Ausgabe eines Aufrufes von SemDiag:
-
-
- SemDiag(MySem,"Meine Semaphore");
-
- -------------[Meine Semaphore]---------------
- Signals: 0
- WaitQueue: 4, 6
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemGetSignals
- Deklaration
-
- FUNCTION SemGetSignals(SemPtr:Pointer):Word;
-
- Funktion
-
- Ermittle den Signal-Count der angesprochenen Semaphore.
-
- Beispiel
-
- FUNCTION SemBusy(S:Pointer):BOOLEAN;
- {
- Diese Funktion prüft, ob eine Semaphore gerade belegt ist.
- Ein Funktionswert von TRUE bedeutet "belegt".
- }
- BEGIN {SemBusy}
- SemBusy := (SemGetSignals(S) = 0); {Signal-Count = 0}
- END; {SemBusy}
-
- Siehe auch
-
- SemSignal, SemWait, SemClear, SemClearWait, SemSet
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemPaste
- Deklaration
-
- PROCEDURE SemPaste(SemPtr:Pointer; Task:TaskNoType);
-
- Funktion
-
- Die Task mit der angegebenen Nummer wird bedingungslos an die
- Warteschlange der Semaphore angehängt!!! Es findet keine Prüfung
- der Task-Nummer auf Gültigkeit statt.
-
- Anmerkungen
-
- Der Zustand der Task wird durch diesen Vorgang in keiner Weise
- verändert. Aus diesem Grunde sollte SemPaste() NUR nach einem
- vorhergehenden Aufruf von SemCut ausgeführt werden. Diese Funk-
- tion ist ausschließlich für einen speziellen Anwendungsfall
- innerhalb der Unit MtPopUp hinzugefügt worden und sollte nur mit
- äußerster Vorsicht angewandt werden.
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemSet
- Deklaration
-
- PROCEDURE SemSet(SemPtr:Pointer; Count:Word);
-
- Funktion
-
- Setze den Signal-Count der angesprochenen Semaphore bedingungslos
- auf den übergebenen Wert. Evtl. in der Warteschlange befindliche
- Tasks werden durch diese Aktion NICHT freigegeben.
-
- Beispiel
-
- CONST Elements = 100;
- VAR Buffer : ARRAY[1..Elements] OF Byte; {Puffer}
- Full : Pointer; {Anzahl voller Slots}
- Empty : Pointer; {Anzahl leerer Slots}
- ...
- BEGIN {Main}
- ...
- IF CreateSem(Full) = Sem_Ok
- THEN SemClear(Full); {nix voll}
- IF CreateSem(Empty) = Sem_Ok
- THEN SemSet(Empty,Elements); {alles leer}
- ...
- END. {Main}
-
- Siehe auch
-
- SemSignal, SemWait, SemClear, SemClearWait
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemSignal
- Deklaration
-
- PROCEDURE SemSignal(SemPtr:Pointer);
-
- Funktion
-
- Erhöhe den Signal-Count der angesprochenen Semaphore, falls die
- Warteschlange leer ist. Anderenfalls reaktiviere die erste Task
- der Warteschlange und lasse den Signal-Count unangetastet.
-
- Beispiel
-
- VAR Critical: Pointer;
- ...
- IF SemCreate(Critical) <> Sem_Ok
- THEN Error;
- ...
- SemWait(Critical);
- ...
- {kritischer Bereich}
- ...
- SemSignal(Critical);
- ...
-
- Siehe auch
-
- SemWait, SemClearWait, SemClear, SemSet
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemSoWaiting
- Deklaration
-
- FUNCTION SemSoWaiting(SemPtr:Pointer):BOOLEAN;
-
- Funktion
-
- Diese Funktion liefert den Wert TRUE, falls sich Tasks in der
- Warteschlange der angesprochenenen Semaphore befinden. Ist die
- Warteschlange leer, so wird FALSE geliefert.
-
- Siehe auch
-
- SemWait, SemSignal, SemClear, SemClearWait, SemSet, SemGetSignals
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SemWait
- Deklaration
-
- PROCEDURE SemWait(SemPtr:Pointer);
-
- Funktion
-
- Erniedrige den Signal-Count der angesprochenen Semaphore um 1,
- falls dieser derzeit größer als Null ist. Ist dies nicht der
- Fall, so wird die aufrufende Task solange blockiert, bis eine
- andere Task einen SemSignal-Aufruf auf dieselbe Semaphore ab-
- setzt.
-
- Beispiel
-
- VAR Critical: Pointer;
- ...
- IF SemCreate(Critical) <> Sem_Ok
- THEN Error;
- ...
- SemWait(Critical);
- ...
- {kritischer Bereich}
- ...
- SemSignal(Critical);
- ...
-
- Siehe auch
-
- SemSignal, SemClearWait, SemClear, SemSet
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Send
- Deklaration
-
- FUNCTION Send(ToTask:TaskNoType; Msg:Pointer; MsgSize:WORD;
- WaitFlag:WaitFlagType):TaskReturn;
-
- Funktion
-
- Die Funktion Send ermöglicht es einer Task, eine Nachricht an
- eine andere Task zu senden.
- Hierbei enthält der Parameter "To-Task" die Task-Nummer des
- gewünschten Kommunikationspartners. Dies muß in jedem Fall die
- Nummer einer aktiven Task sein, "AnyTask" ist hier NICHT zulässig.
- Der Parameter "Msg" enthält die Anfangsadresse eines Puffers, in
- dem sich die zu sendende Nachricht in der Länge "MsgSize"-Bytes
- befindet.
- Der Parameter "WaitFlag" entscheidet darüber, ob die sendende
- Task blockiert wird, falls der Kommunikationspartner derzeit
- nicht zu sprechen ist, oder ob sie augenblicklich mit einem
- entsprechenden Returncode zurückkehrt.
-
- Beispiel
-
- VAR Puffer : String;
- ...
- IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok
- THEN Error
- ELSE Writeln(Puffer);
- ...
-
- Anmerkungen
-
- Die Nachricht wird physisch in den Empfangspuffer kopiert. Vom
- Standpunkt der Performance her betrachet, ist dies sicherlich
- nicht die günstigste Lösung, jedoch wird so die größtmögliche
- Unabhängigkeit zwischen Sender und Empfänger erreicht.
-
- Siehe auch
-
- Typdefinition TaskReturn, Receive
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SetPri
- Deklaration
-
- PROCEDURE SetPri(Pri:Priority);
-
- Funktion
-
- Verändern der Priorität der aufrufenden Task zur Laufzeit.
-
- Anmerkungen
-
- Die Task wird umgehend ihrer neuen Prioritätsebene zugeordnet,
- jedoch behält sie bis zum Ablauf der aktuellen Zeitscheibe die
- Kontrolle, selbst wenn sie sich in ihrer Priorität herabgesetzt
- hat.
-
- Siehe auch
-
- Typdefinition Priority
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Sleep
- Deklaration
-
- PROCEDURE Sleep(Ticks:LongInt);
-
- Funktion
-
- Mit Hilfe von Sleep kann sich eine Task um "Ticks" Timer-Ticks
- suspendieren. Nach Ablauf dieser Wartezeit wird sie vom Subsystem
- automatisch wieder in den Ready-Zustand versetzt.
-
- Beispiel
-
- Writeln('Ich schlafe jetzt 3 Sekunden!');
- Sleep(Seconds(3));
- Writeln('Gähn... Ich bin wieder wach!');
-
- Siehe auch
-
- Seconds
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE SpeedUp
- Deklaration
-
- PROCEDURE SpeedUp(Factor:Word);
-
- Funktion
-
- Die Prozedur "SpeedUp" erlaubt es, einen Teiler für die Standard-
- Interruptfrequenz von ca. 55,5ms zu definieren. Dadurch wird die
- Taskwechselrate um den entsprechenden Faktor erhöht.
- Ein SpeedUp-Faktor von 5 ergibt demnach eine minimale
- Zeitscheibengröße von ca. 11,1ms, d. h. rund 90 Task-Wechsel pro
- Sekunde.
-
- Anmerkungen
-
- Eine Erhöhung der Taskwechelrate bringt in der Regel nur dann
- Vorteile, wenn zeitkritische Abfragen oder Ereignisse zu
- bearbeiten sind. Da der Task-Switch zu den zeitaufwendigsten
- Systemoperationen gehört, wächst auch der System-Overhead mit
- steigender Interruptfrequenz.
- Die PC Systemuhr wird von der Erhöhung der Interruptrate nicht
- beeinflußt; sie wird nach wie vor nur 18 Mal in der Sekunde
- weitergezählt.
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Suspend
- Deklaration
-
- FUNCTION Suspend(Task:TaskNoType):BOOLEAN;
-
- Funktion
-
- Suspendiere eine Task, die sich derzeit im Zustand Ready befindet
- auf unbegrenzte Zeit.
- "Task" enthält hierbei die Task-Nummer der zu suspendierenden
- Task. Eine durch Suspend deaktivierte Task kann AUSSCHLIESSLICH
- durch einen Aufruf von ReadySuspended wieder in den Ready-Zustand
- versetzt werden.
- Befindet sich die angesprochene Task nicht im Zustand Ready, so
- wird ein Funktionswert von FALSE geliefert; andernfalls TRUE.
- Eine Task kann sich NIEMALS selbst suspendieren!!!
-
- Siehe auch
-
- Typdefinition TaskNoType, ReadySuspended
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE Terminate
- Deklaration
-
- PROCEDURE Terminate;
-
- Funktion
-
- Durch den Aufruf von Terminate beendet sich eine Task selbst. Der
- belegte Task-Table-Eintrag wird freigegeben; ebenso der private
- Stack-Space.
-
- Beispiel
-
- VAR Semaphore : Pointer; {Handle}
- ...
- IF CreateSem(Semaphore) <> Sem_Ok {Semaphore erzeugen}
- THEN BEGIN
- Error; {Fehlermeldung}
- Terminate; {Task beenden}
- END;
-
- Siehe auch
-
- CreateTask
- .EE
- ──────────────────────────────────────────────────────────────────
- .RE TimeSlice
- Deklaration
-
- PROCEDURE TimeSlice(Slice, DynQ:Byte);
-
- Funktion
-
- TimeSlice dient zur Veränderung der Zeitscheibengröße sowie der
- Genze für die dynamische Zeitscheibensteuerung.
- Der Parameter "Slice" gibt die breite einer Zeitscheibe in Ticks
- an; der Parameter "DynQ" enthält den +/- Grenzwert für die
- dynamische Zeitscheibensteuerung ebenfalls in Ticks.
-
- Beispiel
-
- {
- Einstellen der minimalen Zeitscheibengröße von 55,5
- Millisekunden bei eine Begrenzung der Dynamik auf 1 Sekunde
- }
- TimeSlice(1,Seconds(1));
-
- Anmerkungen
-
- Vom System werden beim Start eine Zeitscheibenbreite von 2 Ticks
- à 55,5 ms und ein +/- Wert von 10 Ticks voreingestellt.
- .SE CPMULTI / Programmierhinweise
- 7. Programmierhinweise
-
- Tastaturbehandlung
-
- Verwenden Sie niemals die Standard-Funktionen Read/ReadLn/ReadKey
- für die Dateneingabe. Durch den direkten Aufruf der Funktion 0 des
- Interrupt 16H (der als nicht reentrant behandelt wird), werden alle
- Task-Wechsel bis zum nächsten Tastendruck verhindert.
- Eine Abfrageschleife (mit Keypressed) um ReadKey herum, behebt
- dieses Problem (siehe GlassTty, Pro_Con,...), verbraucht allerdings
- unnötig Rechnerzeit.
-
- Abhilfe
-
- Ist Tastatureingabe erforderlich, so binden Sie IMMER die Unit
- MTPopUp (nur registrierte Benutzer) ein.
- Diese sorgt dafür, daß eine Task, die auf Tastatureingaben wartet,
- bis zum Eintreffen von Daten suspendiert wird. - Die durch eine
- Abfrageschleife verbrauchte Rechnerzeit steht dadurch den übrigen
- Tasks zur Verfügung!
-
-
- Plattenzugriffe
-
- Konzentrieren Sie Plattenzugriffe auf eine einzelne Task in Ihrer
- Anwendung.
-
-
- Overlays
-
- Verwenden Sie NIEMALS Overlays in Programmen, die mit dem Multi-Tasking
- Subsystem übersetzt wurden.
-
- Beispielsituation
-
- Es existieren zwei Units, die als Overlay geladen werden. Es kann
- immer nur eine der beiden Units gleichzeitig im Speicher sein
- (vereinfachte Darstellung).
-
- OUnit1
- OProc11
- OProc12
-
- OUnit2
- OProc21
- OProc22
-
- Ferner werden die Routinen aus den beiden Overlays von zwei Tasks
- (Task1, Task2) angesprochen.
- Findet nun ein Task-Switch statt, während sich z. B. Task1 in der
- Routine OProc11 des ersten Overlays befindet, so würde dieses Overlay
- durch Aufruf von OProc22 innerhalb der Task2, die durch den
- Task-Wechsel die Kontrolle erhält, überschrieben.
- Im weitern Verlauf erhält nun wieder Task1 die Kontrolle, das
- Subsystem rekonstruiert den Registersatz und setzt die
- Programmausführung an der Adresse fort, an der es unterbrochen
- wurde..... Nur, dort befindet sich mittlerweile nicht mehr der
- Overlaybereich1, da dieser ja durch Nachladen des zweiten Overlays
- überschrieben wurde!
-
-
- Sonstiges
-
- Soll der Kernel möglichst schnell auf Ereignisse reagieren könne, so
- em- pfiehlt es sich, in Warteschleifen, in denen Funktionen aufgerufen
- werden, die nicht durch einen Task-Switch unterbrochen werden dürfen,
- einen Sleep(1) einzubauen, da hierdurch häufig die Zeitscheibe der
- wartenden Task durch ein für eine höherpriorisierte Task bestimmtes
- Ereignis abgebrochen werden kann.
-
- Beispiel: Nicht REPEAT UNTIL Keypressed;
- Sondern REPEAT Sleep(1); UNTIL Keypressed;
- .SE Reihenfolge / Abhängigkeiten
- 8. Reihenfolge der Einbindung und Abhängigkeiten der Units
-
- Reihenfolge der Eintragung in ein USES-Statement
-
- (Dos)
- (Crt)
- CpMulti
- MtPopUp
- CpMisc
- Queue
- MtPipe
- MtPrint
- V24
- V24Pipe
-
- Abhängigkeiten
-
- CpMulti
- Dos
- Crt
-
- MtPopUp
- Dos
- Crt
- CpMulti
-
- CpMisc
- CpMulti
-
- Queue
- CpMulti
- CpMisc
-
- MtPipe
- Dos
- CpMulti
- CpMisc
- Queue
-
- MtPrint
- Dos
- CpMulti
- MtPipe
-
- V24
- Dos
-
- V24Pipe
- Dos
- CpMulti
- MtPipe
- V24
- .SE Literaturhinweise
- a) Operating Systems, Design and Implementation
- Andrew S. Tanenbaum
- Verlag Prentice-Hall International Editions
- ISBN 0-13-637331-3
- 719 Seiten, ca. 64,- DM
-
- Dieses Buch ist mittlerweile auch in einer deutschen
- Übersetzung beim Carl Hanser Verlag, München erhältlich.
-
-
- b) Praxis des Multitasking
- Zilker
- Franzis Verlag
- ISBN 3-7723-8561-3
- 112 Seiten, ca. 38,- DM
- .SE Historie
- Version 1.10 / August 1988
-
- - Fehler in der Ereigsnissteuerung behoben
- - Erweiterung der Stackprüfung; jetzt wird auch erkannt, wenn einer
- Task beim Create zuwenig Stackspace für ihre lokalen Variablen
- zugewiesen wird. Dies führt ebenfalls zu einem Stack-Überlauf und
- einem Markieren der Task als Crashed.
- - Erweiterung der Prüfungen beim Reaktivieren von Tasks, die sich
- im Wartezustand befinden. Eine Task, die als Crashed markiert
- ist, wird nicht mehr reaktiviert.
- - Neugliederung der Quelle
- - SemCut und SemPaste hinzugefügt
- - Überarbeitung der Dokumentation
- .PA
- Version 1.30 / November 1988
-
- - Umstellung der Task-Verwaltung auf doppelt verkettete Listen.
- Dadurch Verkürzen der Zeit, die zum Entfernen einer Task aus einer
- Kette benötigt wird.
- - Von nun an werden Tasks, die einen Wartezustand verlassen, der
- sich durch eine Message-Passing Operation oder ein Warten auf ein
- Ereignis ergeben hat, an den Kopf ihrer Task-Queue gestellt. So
- kann insbes. bei der Ereignisverarbeitung unabhängig von der
- Anzahl zur Verarbeitung anstehender Prozesse, schnell auf das
- Ereignis reagiert werden.
- - Ein Fehler in der Delta-Berechnung beim QueueTimer wurde behoben.
- - Der Scheduler Stack wurde aus dem Datensegment auf den Heap
- verlagert.
- - Der bislang lokale Typ PointerType wurde global verfügbar
- gemacht.
- - Geringfügige Optimierungen im Assemblermodul wurden durchgeführt.
- - Neu hinzugekommen ist eine Prozedur SetPri, mittels derer ein
- Prozeß zur Laufzeit seine Priorität verändern kann.
- - Überarbeitung des Handbuches
- .PA
- Version 2.00 / März 1989
-
- - Neugliederung der Quelle; verbesserte Modularisierung
- - Überarbeitung der Dokumentation
- - Aufbereitung als Quelle für einen Dokumentationsprozessor
- - nun auf unterschiedliche Papierformate leicht anpaßbar
- - Bereitstellung des Dokument-Listers (auch als C-Quelle)
- - Zeitscheibengröße nicht mehr auf 55,5ms gegrenzt
- - neue Prozedur SpeedUp()
- - 9 Prioritätsebenen
- - Verbesserung der Systemeinbettung
- - PrtScr-Interrupt (INT 5H) wird nun als nicht-reentrant behandelt
- - Maustreiber-Interrupt (INT 33H) wird ebenfalls als nicht-
- reentrant behandelt
- - Verbesserung der Ereignisverwaltung
- - Performance-Steigerung durch Ersetzen der bisherigen Verwalt-
- ungsroutinen
- - nun max. 20 Ereignisse
- - Ereignisauswertung bei jedem Tick
- - Tritt ein Ereignis für eine Task ein, die eine höhere Priorität
- hat als diejenige Task, die gerade die CPU besitzt, so wird
- deren Zeitscheibe zwangsbeendet sobald dies ohne Gefahr möglich
- ist.
- - Einführung eines Port-Change Ereignisses
- - EventWait entfällt
- - Neu: MemoryEventWait und PortEventWait
- - Neu: EventKind
- - Änderung: EventType
- - Erweiterung der Timer-Verarbeitung
- - Einführung von Watch-Dog Timern
- - Änderung: TimerType
- - Neu: TimerKind, WatchDogType
- - Neu: QueueWatchDog
- - Änderung: Counter in QueueTimer/QueueWatchDog nun LongInt
- - Veränderung der Definition einer Task
- - Jede Task kann nun einen Parameter vom Typ Pointer erhalten
- - Jede Task MUSS nun als FAR-Prozedur codiert werden!
- - Eine Task kann sich durch Verlassen der Pascal-Prozedur z. B.
- mittels EXIT beenden und muß nicht mehr zwingend die System-
- funktion Terminate aufrufen
- - Neue Systemaufrufe allgemeiner Natur
- - ChangePri
- - Kill
- - SemDiag
- - RemoveSemKill
- - Implementierung der Sleep-Queue mit Hilfe von WatchDog-Timern
- - Änderung: TaskDescType (DelayCount fällt weg)
- - Änderung: DumpTaskTable
- - Textdatei-Gerätetreiber für Pipes
- - zeichenorientierter Print-Spooler als Ersatz für die Unit PRINTER
- - interruptgesteuerte V24 Routinen (Public Domaine) bis 115200 baud
- - Pipe-Unterstützung für die V24-Schnittstelle; Schreiben und Lesen
- der Schnittstelle über eine Pipe
- - weitere Demoprogramme hinzugefügt
-
- .PA
- Version 2.02 / September 1989
-
- - geringfügige Korrekturen
- - Erweiterung der Unit V24 für bis zu 6 serielle Schnittstellen
- (jedoch wie bisher nur je 1 zu einem Zeitpunkt)
-
- .PA
- Version 2.10 / April 1990
-
- - Fehlerbehebung in SemSignal()
- - Fehlerbehebung im Demoprogramm FINDER.PAS
- - Fehlerbehebung in der Berechnung der dynamischen CPU-Zeit
- - Erweiterung des Kernels
- - Resourcen
- - CreateRsc
- - RemoveRsc
- - RemoveRscKill
- - RequestRsc
- - GrantRsc
- - ReleaseRsc
- - Exclusiv-Modus
- - Enter Exclusive
- - Leave Exclusive
- - abschaltbares Time-Slicing
- - die Idle-Loop wird nun nach jedem Timer-Tick preempted;
- dadurch weniger vergeudete CPU-Zeit
- - Ausgliederung der Makros aus den Assemblermoduln in
- Include-Files
- - Demoprogramm PHILO.PAS
- - Demoprogramm RSCTEST.PAS
- - Online-Guide (optional)
- - Wegfall der Unterstützung des A86-Assemblers