home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / pascal / library / dos / multtsk / cpmult / doku / cpmulti.dok next >
Encoding:
Text File  |  1990-04-23  |  111.5 KB  |  3,120 lines

  1. .. //////////////////////////////////////////////////////////////////
  2. .. /// Dokumentation zum Multi-Tasking Subsystem V2.10 / April 1990
  3. .. /// Basissystem CPMULTI
  4. .. /// Format: DOCLIST 1.00-Format
  5. .. //////////////////////////////////////////////////////////////////
  6. .. /// Bitte passen Sie die nachfolgenden Zeile ggf. für Ihren
  7. .. /// Drucker an. Die Voreinstellung paßt auf einen EPSON FX-85
  8. .. /// mit Papierformat 12"
  9. .. //////////////////////////////////////////////////////////////////
  10. .PL 72      Seitenlänge 72 Zeilen
  11. .LL 80      Zeilenlänge 80 Zeichen
  12. .ID 5       Einrücken um 5 Zeichen vom linken Rand
  13. .RM 5       5 Zeichen bis zum rechten Rand Platz lassen -> 70
  14. ..          Zeichen Text je Zeile
  15. .HS =       Zeichen für Kopf-/Fuß-Trenner
  16. .RS -       Zeichen für Trennung von Referenz-Einträgen
  17. .CO (c) Copyright 1988..1990 Ch.Philipps Software-Technik
  18. .. //////////////////////////////////////////////////////////////////
  19. .SE Einleitung
  20. Lieber Anwender,
  21.  
  22. Ziel des Multi-Tasking Subsystems für Turbo-Pascal 5.x war es, die
  23. Programmierung paralleler Prozesse und das Studium der damit verbun-
  24. denen Probleme der Inter-Prozess-Kommunikation (kurz: IPC = Inter
  25. Process Communication) auf Basis einer weit verbreiteten
  26. Programmierumgebung unter MS-DOS/PC-DOS zu ermöglichen.
  27. Wenngleich das vorliegende Produkt zunächst lediglich als Experimen-
  28. tierwerkzeug gedacht war, entwickelte es sich schon bald zu einem
  29. umfangreichen und leistungsfähigen Produkt.
  30.  
  31. Mittlerweile hat das Multi-Tasking Subsystem einen weiten
  32. Anwenderkreis für sich gewonnen und wird in vielen Unternehmen
  33. erfolgreich zur Realisierung von Projekten z. B. in den Bereichen
  34. Datenkommunikation, Meß- und Regeltechnik und Gerätesteuerung
  35. sowie an zahlreichen Universitäten im In- und Auslang eingesetzt.
  36. Die überaus positive Resonanz von Seiten der Lizenznehmer war Anlaß
  37. zu einer neuerlichen Überarbeitung und Fortentwicklung des
  38. Produktes, das Ihnen nun in der Version 2.10 vorliegt.
  39.  
  40. Bedingt durch die Entstehungsgeschichte, wurde das Subsystem von
  41. Anfang an einem intensiven Test unterworfen, weshalb ich nun der
  42. festen Überzeugung bin, Ihnen ein ausgereiftes und (soweit man dies
  43. je von einer Software behaupten darf) fehlerfreies Produkt anbieten
  44. zu können.
  45.  
  46. Zur Einstimmung, kurz ein Überblick über den Funktionsumfang des
  47. Basissystems:
  48.  
  49. - maximal 50 (problemlos erweiterbar) parallel ablaufende Tasks in 9
  50.   Prioritätsebenen
  51. - Prioritäten zur Laufzeit änderbar
  52. - Code-Sharing
  53. - preemptiver Scheduler
  54. - sichere Verwendung von DOS-Funktionen innerhalb der Tasks möglich;
  55.   kein Taskswitch innerhalb einer DOS-Funktion oder während der
  56.   Ausführung eines kritischen Interrupts (Platten-I/O, ...)
  57. - dynamische Zeitscheibensteuerung
  58. - programmierbare Zeitscheibengröße (auch kleiner 55,5 ms)
  59. - Time-Slicing abschaltbar
  60. - Exclusiv-Modus für einzelne Tasks
  61. - Message-Passing
  62. - Semaphoren
  63. - "Named Pipes" (Turbo Pascal Text-Datei Gerätetreiber)
  64. - Resourcen
  65. - programmierbare Timer
  66. - einfache Ereignissteuerung (maximal 20 gleichzeitig aktive
  67.   Ereignisse, jedoch problemlos erweiterbar)
  68. - lauffähig auf allen PC, XT, AT, PS/2 und 100% kompatiblen System
  69.   mit DOS Version 2.xx, 3.xx und 4.0x, DR-DOS
  70. - Zeichenorientierter Druckerspooler für die Einheit LST
  71. - interruptgesteuerte V24 Unterstützung (PD) im Quellcode
  72. - Pipe-Unterstützung für die serielle Schnittstelle, d. h. Zugriff
  73.   auf die Schnittstelle über TEXT-Dateien
  74. - Quellcode verfügbar
  75.  
  76. Dies schließt noch nicht die zusätzlichen Möglichkeiten ein, die
  77. sich Ihnen durch die Einbindung einer weiteren, für registrierte
  78. Benutzer ebenfalls im Lieferumfang enthaltenen Unit erschließen:
  79.  
  80. - erweiterte Tastaturcodes (Zeichen, Scancode, Shift-Status)
  81. - Manipulation des Tastaturpuffers
  82. - Unterbindung von Tastatureingaben
  83. - DOS als Sub-Task
  84. - Aktivierung von Tasks per Hot-Key
  85.  
  86. Ferner erhalten registrierte Benutzer den Quellcode einiger
  87. Zusatzunits (siehe Hinweise in der Dokumentation), die in der
  88. ShareWare-Version nur als TPU enthalten sind.
  89.  
  90. Ab der Version 2.10 kann ein Norton-Guide ähnliches Online-
  91. Hilfesystem zum Multi-Tasking Subsystem direkt vom Autor
  92. erworben werden. Dieses hält die wichtigesten Informationen
  93. sowie den gesamten Referenzteil des Handbuchs übersichtlich
  94. gegliedert auf Knopfdruck für Sie bereit.
  95. Der Online-Guide basiert auf dem Programm GUIDE, welches zum
  96. Lieferumfang der Turbo Professional Toolbox gehört und wird
  97. incl. der Quelldateien aller Hilfetexte geliefert. So ist es
  98. Lizenznehmern o. g. Tools möglich, die Hilfe nach eigenen
  99. Vorstellungen zu erweitern.
  100.  
  101. Obwohl das Multi-Tasing Subsystem mittlerweile kaum noch
  102. Wünsche offenläßt, mag es dennoch sinnvolle Erweiterungen
  103. geben, auf die ich selbst bislang nicht gekommen bin.
  104. Sollten Sie also Funktionen benötigen, die das Multi-Tasking
  105. Subsystem in seiner heutigen Ausbaustufe noch nicht beinhaltet,
  106. so würde ich mich freuen, von Ihnen zu hören.
  107.  
  108. Ich möchte an dieser Stelle nicht versäumen, mich ausdrücklich bei
  109. L. David Baldwin und TurboPower Software für ihren einzigartigen
  110. Debugger TDEBUG 4.01 zu bedanken, der mir bei der Entwicklung
  111. der Ursprungsversion unter Turbo Pascal 4.0 eine unschätzbare
  112. Hilfe war. Auch die Turbo Analyst Toolbox aus dem Hause
  113. TurboPower Software hat mit das Tuning des Multi-Tasking
  114. Subsystems sehr erleichert.
  115.  
  116. Nun wünsche ich Ihnen viel Vergnügen mit Ihrem Multi-Tasking Sub-
  117. system und verbleibe
  118.  
  119.      mit freundlichen Grüßen,
  120.  
  121.        Christian Philipps
  122. .SE Multi-Tasking Subsystem für Turbo-Pascal 5.x Version 2.10
  123.  
  124.  
  125.  
  126.  
  127.  
  128.  
  129.  
  130.  
  131.             Turbo Pascal 5.x  Multi-Tasking Subsystem
  132.  
  133.  
  134.                         Benutzerhandbuch
  135.  
  136.  
  137.  
  138.  
  139.  
  140.  
  141.  
  142.  
  143.                  (c) Copyright 1988, 1989, 1990
  144.  
  145.                Christian Philipps Software-Technik
  146.  
  147.                      alle Rechte vorbehalten
  148.  
  149.  
  150.                    Version 2.10  / April 1990
  151.  
  152.  
  153.  
  154.  
  155.  
  156.  
  157.  
  158.  
  159.  
  160.  
  161.  
  162.  
  163.          Anschrift:  Christian Philipps
  164.                      Software-Technik
  165.                      Düsseldorfer Str. 316
  166.  
  167.                      D-4130 Moers 1
  168.  
  169.          Telefon..:  02841 / 35932
  170.          Fido-Netz:  509/3.4
  171.  
  172. .SE Warenzeichen
  173. Warenzeichen, die in diesem Handbuch erwähnt werden
  174.  
  175.  
  176. TurboPower Software ist Warenzeichen von TurboPower Software, Scotts
  177. Valley
  178.  
  179. Turbo Professional ist eingetragenes Warenzeichen von Sunny Hill
  180. Software, verwendet unter Lizenz von TurboPower Software
  181.  
  182. Turbo Pascal und TASM sind Warenzeichen von Borland International
  183.  
  184. MS-DOS, OS/2 und MASM sind Warenzeichen der Microsoft Corporation
  185.  
  186. A86 ist Warenzeichen von Eric Isaacson Software, Bloomington
  187.  
  188. UNIX ist Warenzeichen von AT & T
  189.  
  190. IBM PC, XT, AT, PS/2 und PC-DOS sind Warenzeichen der International
  191. Business Machines Corporation
  192.  
  193. DR-DOS ist Warenzeichen der Firma Digital Research
  194. .SE Lizenzvereinbarung
  195. Lizenzvereinbarung (ShareWare-Version)
  196. --------------------------------------
  197.  
  198. Die vorliegende Software und die dazugehörige Dokumentation sind
  199. urheberrechtlich geschützt. Jede Verletzung dieser Lizenzvereinbarung
  200. wird nach besten Kräften strafrechtlich verfolgt.
  201.  
  202. Das Multi-Tasking Subsystem, einschließlich im Lieferumfang
  203. enthaltener Zusatzprogramme und der zugehörigen Dokumentation, ist
  204.  
  205.                 Copyright (c) 1988..1990 von
  206.  
  207.             Christian Philipps Software-Technik
  208.  
  209.                    Düsseldorfer Str. 316
  210.                         4130 Moers 1
  211.  
  212. Teile der Software sind Copyright (c) von Borland International
  213. und TurboPower Software.
  214.  
  215. Dies ist ein Produkt, das u. a. mittels des ShareWare-Konzeptes
  216. vermarktet wird. Dies bedeutet, daß Sie das Recht haben, diese Software
  217. und diese Dokumentation nach Belieben an andere Interessenten wei-
  218. terzugeben, vorausgesetzt, dies geschieht in der ursprünglichen
  219. und unmodifizierten Form. Von dieser Auflage sind sowohl Software
  220. als auch Dokumentation betroffen.
  221. Der Vertrieb dieses Produktes ohne eine schriftliche Genehmigung des
  222. Autors sowie die Weitergabe in Verbindung mit einem kommerziellen
  223. Produkt sind strengstens untersagt.
  224.  
  225. Sie haben das Recht, das vorliegende Softwarepaket für die Dauer von
  226. 30 Tagen kostenlos zu testen und anzuwenden. Sollten Sie sich nach
  227. Ablauf dieser Testperiode entschließen, das Programm weiterhin
  228. einzusetzen, so erwarte ich, daß Sie sich als ordnungsgemäßer
  229. Benutzer registrieren lassen.
  230.  
  231. Die Lizenzgebühren schlüsseln sich wiefolgt auf:
  232.  
  233.    Objektcode    171,-- DM
  234.    Quellcode     342,-- DM (nur nach/bei Erwerb des Objektcodes)
  235.  
  236.    Online-Guide   35,-- DM (nur nach/bei Erwerb einer Objekt-
  237.                             oder Quellcode-Lizenz)
  238.  
  239. Diese Preise verstehen sich incl. Mehrwertsteuer.
  240.  
  241. Für Verpackung und Versand werden je Bestellung 6,- DM zusätzlich
  242. berechnet.
  243.  
  244. Als schnellster Zahlungsweg hat sich die Einsendung eines
  245. Verrechnungsschecks erwiesen.
  246.  
  247. Anschrift:      Christian Philipps
  248.                 Software-Technik
  249.                 Düsseldorfer Str. 316
  250.                 D-4130 Moers 1
  251.  
  252. Gewerbliche Anwender und öffentlich-rechtliche Einrichtungen können
  253. das Multi-Tasking Subsystem auch auf Rechnung bestellen (Bestellung
  254. auf Papier mit Firmenkopf). Der Rechnungsbetrag ist in diesem Fall
  255. ohne Abzug innerhalb von 14 Tagen nach Erhalt der Ware zahlbar. Auf
  256. der Rechnung werden 14% MwSt ausgewiesen. Anderslautende AGB werden
  257. nur dann akzeptiert, wenn sie Christian Philipps Software-Technik
  258. begünstigen.
  259.  
  260. Erfüllungsort für die beiderseitigen Leistungen und - soweit
  261. gesetzlich zulässig - Gerichtsstand ist Moers.
  262.  
  263. Die Rechtsbeziehung zwischen dem Autor und dem Lizenznehmer unter-
  264. liegen ausschließlich dem Recht der Bundesrepublik Deutschland.
  265.  
  266.  
  267.  
  268. Wenngleich die ShareWare-Version nicht in ihren Funktionen einge-
  269. schränkt ist, so enthält Sie doch als kleine Erinnerung für Sie,
  270. sich als Benutzer registrieren zu lassen, einen Textbildschirm, der
  271. automatisch nach Beendigung jedes mit dieser Unit erstellten Pro-
  272. grammes aufgeblendet wird. Unmittelbar nach Eingang Ihrer Lizenz-
  273. gebühr, erhalten Sie eine Version des Subsystems, die diesen Bild-
  274. schirm nicht mehr enthält, d. h. mit der Sie eigene Programme zur
  275. kommerziellen Nutzung erstellen können.
  276.  
  277. Zusätzlich SCHENKE ich Ihnen eine lizensierte, auf dem Multi-Tasking
  278. Subsystem basierende Unit, mit deren Hilfe Sie volle Kontrolle über
  279. die Tastatur ausüben, Tasks per Hot-Key aktivieren, ein DOS-Programm
  280. (z. B. einen zweiten COMMAND.COM) als Task ausführen und PopUp-Tasks
  281. während laufender DOS-Task verwalten können. Auch erhalten Sie den
  282. Quellcode des Print-Spoolers (Unit MtPrint) und der V24-Pipe-Unter-
  283. stützung (Unit V24Pipe). Weiterhin steht jedem registrierten Benut-
  284. zer für die Dauer von 1 Jahr telefonische Unterstützung zur Verfü-
  285. gung.
  286. .SE Garantie- und Haftungsausschluß
  287. Garantie- und Haftungsausschluß
  288. -------------------------------
  289.  
  290. Der Autor garantiert NICHT die Eignung des Multi-Tasking Subsystems 
  291. incl. evtl. Zusatzprogramme für einen bestimmten Anwendungsfall, 
  292. weder nach ausdrücklicher Absprache, noch implizit durch den Inhalt 
  293. der Dokumentation oder der gelieferten Software. 
  294.  
  295. Weiterhin ist der Autor UNTER KEINEN UMSTÄNDEN für Schäden haftbar, 
  296. die sich aus der Nutzung oder Unfähigkeit zur Nutzung des vorlie-
  297. genden Produktes ergeben. Dies schließt den Verlust von Geschäfts-
  298. gewinnen, die Unterbrechung der geschäftlichen Abläufe, den Verlust 
  299. von Daten sowie alle übrigen materiellen und ideellen Verluste und 
  300. deren Folgeschäden ein und gilt selbst dann, wenn o. g. Person zuvor 
  301. ausdrücklich auf die Möglichkeit derartiger Schäden hingewiesen 
  302. worden ist. 
  303.  
  304. DURCH DIE NUTZUNG DER VORLIEGENDEN SOFTWARE ERKLÄRT DER ANWENDER 
  305. SEIN EINVERSTÄNDNIS MIT DIESEN LIZENZVEREINBARUNGEN SOWIE DEM
  306. GARANTIE- UND HAFTUNGSAUSSCHLUSS.
  307. .SE Systemvoraussetzungen
  308. I. Systemvoraussetzungen
  309. Um das Multi-Tasking Subsystem nutzen zu können, benötigen Sie 
  310. mindestens 
  311.  
  312.  
  313.    - einen IBM PC, XT, AT, PS/2 oder 100% kompatiblen Computer 
  314.  
  315.    - MS-DOS oder PC-DOS Version 2.x, 3.x oder 4.0x bzw. DR-DOS
  316.  
  317.    - Turbo-Pascal Version 5.x
  318.  
  319.    - 256 KB RAM 
  320.  
  321.    - 2 Diskettenlaufwerke, besser jedoch eine Festplatte 
  322.  
  323.  
  324.  
  325. Um Änderungen an den Assembler-Quellen vornehmen zu können, ist 
  326. zusätzlich der Microsoft Macroassembler (MASM) ab Version 4.0
  327. oder der Turbo Assembler V1.00 erforderlich.
  328.  
  329. Der Turbo-Debugger (intern wie auch stand-alone) ist in den meisten
  330. Fällen problemlos für die Fehlersuche zusammen mit dem Subsystem
  331. einsetzbar.
  332.  
  333.  
  334. Einschränkungen
  335. ---------------
  336.  
  337. Die Coprozessoren 8087, 80x87 werden NICHT unterstützt.
  338.  
  339. .SE Einbindung des Subsystems
  340. II. Einbindung der Units des Multi-Tasking Subsystems
  341.  
  342.  
  343. In der gesamten Dokumentation gehe ich davon aus, daß Sie mit dem 
  344. Umgang des Turbo-Pascal Compilers Version 5.x vertraut sind, das
  345. UNIT-Konzept dieses Compilers verstanden haben und den Umgang mit 
  346. den im Compilerhandbuch beschriebenen Features von Turbo-Pascal 
  347. beherrschen.
  348. Sollten Sie an einigen Stellen der Beschreibung grundlegende Ver-
  349. ständisschwierigkeiten haben, so schlage ich vor, daß Sie das 
  350. Turbo-Pascal Handbuch noch einmal zur Hand nehmen. 
  351.  
  352. Nützlich zum Verständnis der Beschreibung, aber nicht unbedingt 
  353. erforderlich, ist ein wenig Erfahrung im Umgang mit Interruptbehand-
  354. lung, Exit-Prozeduren und einem Macroassembler für den PC. 
  355.                             
  356.  
  357. Das Multi-Tasking Subsystem besteht aus insgesamt 8 Units:
  358.  
  359. a) CPMULTI                
  360.  
  361.    Dies ist das eigenliche Subsystem, das die Grundlage für die 
  362.    beiden anderen Units bildet. 
  363.  
  364. b) MTPOPUP (nur registrierte Version) 
  365.  
  366.    Die auf dem Basissystem aufbauende Unit, die alle Funktionen 
  367.    enthält, um vollständige Kontrolle über die Tastatur ausüben zu 
  368.    können, Tasks per Hot-Key aus dem Schlaf zu reißen und DOS als 
  369.    Sub-Task ausführen zu können.
  370.  
  371.    Diese Unit ist in der separaten Dokumentation MTPOPUP.DOK
  372.    beschrieben
  373.  
  374. c) QUEUE
  375.  
  376.    Eine zusätzliche Unit, die ich bereits von einiger Zeit als 
  377.    Public Domaine Zusatz zum Subsystem freigegeben habe. Diese 
  378.    enthält universell verwendbare Funktionen zur Verwaltung von 
  379.    doppelt verketteten Listen.
  380.    Der Einsatz dieser Unit setzt voraus, daß Sie sich im Umgang mit
  381.    untypisierten Pointer und der Typumwandlung sowie den
  382.    Prozedurparametern von Turbo Pascal 5.x sicher fühlen.
  383.    Die Routinen dieser Unit stellen die Konsistenz der Queues zu jedem
  384.    Zeitpunkt durch geeignete Semaphoren-Operationen sicher.
  385.  
  386. d) CPMISC (in der registrierten Version mit Quellcode)
  387.  
  388.    Eine Reihe nützlicher Routinen, die teilweise von der Unit QUEUE 
  389.    verwendet werden.
  390.  
  391.  
  392. e) MTPIPE
  393.  
  394.    Text-Datei Gerätetreiber für "Named-Pipes". Diese Unit ermöglicht
  395.    einen einfachen Datenaustausch zwischen mehreren Tasks über einen
  396.    hauptspeicherinternen Ringpuffer. Das Lesen bzw. Schreiben der
  397.    Pipe erfolgt mittels der Standard-Prozeduren
  398.    Read/Readln und Write/Writeln.
  399.  
  400.    Diese Unit ist in der separaten Dokumentation MTPIPE.DOK be-
  401.    schrieben.
  402.  
  403.  
  404. f) MTPRINT (in der registrierten Version mit Quellcode)
  405.  
  406.    Text-Datei Gerätetreiber für die Einheit "Lst". Diese Unit ersetzt
  407.    die Standard-Unit "Printer". Alle Ausgaben auf den Drucker (z. B.
  408.    mittels Writeln(Lst,'Dies ist ein Text');) werden über eine Pipe
  409.    gepuffert und von einem Hintergrundprozeß zum Drucker geschickt.
  410.    MTPrint stellt somit einen einfachen Drucker-Spooler auf
  411.    Zeichenebene zur Verfügung.
  412.    Die größe des Druckpuffers sowie die Priorität des Hintergrund-
  413.    prozesses sind konfigurierbar.
  414.  
  415.    Diese Unit ist in der separaten Dokumentation MTPRINT.DOK be-
  416.    schrieben.
  417.  
  418.  
  419. g) V24 (Public Domaine; im Quellcode)
  420.  
  421.    Diese Unit enthält einen Satz interruptgesteuerter V24-Routinen,
  422.    die ich als Public-Domaine Software freigegeben habe. Diese Rou-
  423.    tinen werden speziell für den Einsatz in Verbindung mit dem Sub-
  424.    system durch die Unit V24Pipe unerstützt.
  425.  
  426.  
  427. h) V24PIPE (in der registrierten Version mit Quellcode)
  428.  
  429.    Auf Basis der Public-Domaine V24-Routinen (siehe Punkt g), stellt
  430.    diese Unit eine Verbindung zur seriellen Schnittstelle über den
  431.    Pipe-Treiber (siehe Punkt e) her.
  432.    Sie ermöglicht Ihnen, Daten mittels Read/ReadLn von der
  433.    Schnittstelle zu lesen und mittels Write/Writeln an diese zu
  434.    senden.
  435.  
  436.    Diese Unit ist in der separaten Dokumentation V24PIPE.DOK be-
  437.    schrieben.
  438.  
  439.  
  440. Kommen wir nun zur Anwendung: Die Einbindung des Multi-Tasking
  441. Subsystems in eigene Programme gestaltet sich sehr einfach. Voraus-
  442. setzung ist, daß die im Lieferumfang enthaltenen Units dür den
  443. Turbo-Compiler verfügbar gemacht werden. Bitte entnehmen Sie die im
  444. Einzelnen hierfür erforderlichen Schritte Ihrem Turbo-Pascal Handbuch.
  445.  
  446. Mittels einer USES-Anweisung, binden Sie die jeweils benötigten 
  447. Units dann in Ihr Anwendungsprogramm ein. Hierbei ist darauf zu 
  448. achten, daß CPMULTI stets die erste der Units des Subsystems sein
  449. muß. Die Reihenfolge, in der die übrigen Units eingebunden werden
  450. müssen sowie die Abhängigkeiten zwischen Ihnen, entnehmen Sie bitte
  451. dem Kapitel 8 (Abhängigkeiten).
  452.  
  453. Sollten Sie zusätzlich Units aus dem Turbo-Pascal Basispaket (z. B. 
  454. Crt oder DOS) benötigen, so sollten diese VOR CPMULTI in der 
  455. USES-Liste stehen. 
  456.  
  457. Beispiel:   USES Crt, Dos, CPMulti, MtPopUp, Queue; 
  458.  
  459. ACHTUNG! 
  460.  
  461. Die Units CPMULTI und MTPOPUP verwenden ihrerseits die Unit CRT. 
  462. Dies führt zu Konflikten, wenn Sie das Turbo Professional Paket von 
  463. TurboPower Software verwenden, welches eine alternative Unit TpCRT 
  464. zur Bildschirmbehandlung anbietet. In diesem Fall sind die vorge-
  465. nannten Units unter Definition des Compiler-Conditionals TpCRT neu
  466. zu übersetzen. In den vorangegangenen Subsystemversionen,
  467. befanden sich im Lieferumfang jeweils auch UNITS, die unter
  468. Einbindung von TpCRT übersetzt wurden. Da jedoch die Turbo
  469. Professional Tools mittlerweile nicht mehr als UNITS, sondern
  470. nur noch im Quellcode ausgeliefert werden und zudem mehrere
  471. verschiedene Versionen im Umlauf sind, waren diese UNITS nur
  472. selten mit den jeweiligen Turbo Professional UNITS der Anwender
  473. funktionsfähig (UNIT version mismatch). Die Version 2.10
  474. enthält daher diese speziellen Versionen nicht mehr.
  475.  
  476. Sollten Sie die Objektcode-Version des Subsystems erworben
  477. haben und daher nicht die Möglichkeit haben, die Subsystem-UNITS
  478. neu zu übersetzen, biete ich Ihnen an, mir Ihre Version von TPCrt
  479. zuzusenden. Sie erhalten dann gegen Erstattung der Unkosten
  480. (ca. 20 DM incl. Versand) eine für Ihre Konfiguration passende
  481. Objektversion zugesandt.
  482.  
  483. Bitte beachten Sie auch, daß TpCRT, wie auch die Standard-CRT Unit, 
  484. VOR CPMULTI in der USES-Liste erscheinen muß. Dies ist insbesondere
  485. bei der Verwendung von MTPOPUP erforderlich, da dort einige in TpCRT 
  486. enthaltene Funktionen redefiniert werden, um den Systemdurchsatz zu
  487. erhöhen.
  488. Die .PRO-Versionen wurden unter Einbindung der Version 5.x des Turbo
  489. Professional Paketes erstellt. Sollten Sie bei der Einbindung Ihrer
  490. Version von TPCRT Schwierigkeiten haben, so müssen Sie den Quellcode
  491. des Subsystems erwerben und die Units neu übersetzen.
  492.  
  493. Weiterhin ist zu beachten, daß jede Task über einen eigenen, priva-
  494. ten Stack verfügt und nur das PASCAL-Hauptprogramm den Turbo-Stack 
  495. verwendet. Daher wird das Turbo-Laufzeitsystem unmittelbar nach dem 
  496. Erzeugen der ersten Task einen Stack-Fehler feststellen, sofern Sie 
  497. keine besonderen Vorkehrungen treffen, um dies zu vermeiden. Hierzu 
  498. ist es erforderlich, GRUNDSÄTZLICH den Compilerschalter S- zu 
  499. verwenden. Dieser unterbindet die Generierung von Code zur Prüfung 
  500. auf Stack-Überlauf. Dadurch geht Ihnen zwar in der Entwicklungsphase 
  501. eine zusätzlicher Kontrollmechanismus verloren, jedoch wäre ihnen 
  502. dieser ohnehin nur im Hinblick auf das Hauptprogramm, nicht jedoch 
  503. auf die Subtasks von Nutzen. 
  504. Das Multi-Tasking Subsystem enthält aber eine Prozedur, die Ihnen 
  505. den aktuellen Zustand der Task-Table auf dem Bildschirm anzeigt. 
  506. Diese Auflistung enthält für alle Tasks, mit Ausnahme des Hauptpro-
  507. grammes, die Anzahl noch freier Bytes auf dem Stack. Weiterhin 
  508. meldet Ihnen das System den Stacküberlauf einer Task zur Laufzeit 
  509. sowohl optisch als auch akustisch, bevor es die betreffende Task als 
  510. "Crashed" markiert und deaktiviert. 
  511. Ich denke, unter diesen Umständen läßt sich der Verlust der Stack-
  512. Prüfung durch Turbo-Pascal durchaus verschmerzen. 
  513.  
  514.  
  515. Bereits durch die Eintragung der Unit CPMULTI in Ihre USES-Klausel, 
  516. aktivieren Sie das Multi-Tasking für Ihr Programm. Der Initialisie-
  517. rungsteil der Unit startet unmittelbar nach dem Programmaufruf das 
  518. Subsystem und aktiviert Ihr Pascal-Hauptprogramm als Task Nr. 2. Die 
  519. Tasknummer 1 wird durch eine niedrig priorisierte Endlosschleife 
  520. belegt, die immer dann ausgeführt wird, wenn gerade keine Nutztask 
  521. zur Bearbeitung zur Verfügung steht. 
  522.  
  523. Für das Abschalten des Multi-Tasking brauchen gleichermaßen keine 
  524. Vorkehrungen getroffen werden, da dies automatisch durch die Exit-
  525. Prozedur der Subsystem-Unit erledigt wird. Dies gilt sowohl für ein
  526. normales Programmende, als auch für einen Programmabbruch durch 
  527. Ctrl-Break oder einen Laufzeitfehler. 
  528.  
  529. Zu einem totalen Systemabsturz wird es nur in außergewöhnlich 
  530. schweren Fällen kommen, wenn durch einen Programmfehler z. B. die 
  531. internen Verwaltungsstrukturen beschädigt, oder der private Stackbe-
  532. reich einer Task durch z. B. eine fehlerhafte Pointeroperation 
  533. überschrieben wurde. 
  534.  
  535. Bevor Sie nun aber bis über beide Ellenbogen in die Programmierung 
  536. einsteigen, empfehle ich Ihnen, das gesamte Handbuch gründlich zu 
  537. studieren!!! 
  538. Nicht nur wird dieses Ihnen ein tieferes Verständis von den Hinter-
  539. gründen und Möglichkeiten des Subsystems geben, sondern Ihnen auch 
  540. kostbare Stunden der Fehlersuche ersparen!
  541. .SE CPMULTI / Einleitung
  542. III. Das Basissystem (CPMULTI)
  543.  
  544.  
  545. 1. Einleitung 
  546.  
  547. Im Hinblick auf die immer größere Verbreitung von UNIX und UNIX-ähn-
  548. lichen Betriebssystemen und insbesondere in Anbetracht der bevorste-
  549. henden OS/2-Invasion, kann es für denjenigen, dem das Interesse an 
  550. Multitaskingsystemen nicht fehlt, wohl aber das zur Anschaffung 
  551. eines solchen erforderliche Kapital, nur nützlich sein, wenn er den 
  552. Umgang mit parallelen Prozessen und deren Synchronisation bereits 
  553. unter DOS mit Hilfe einer so preisgünstigen Entwicklungsumgebung wie 
  554. Turbo-Pascal erlernen kann. 
  555. Das Turbo-Pascal Multi-Tasking Subsystem stellt Ihnen alle Funktio-
  556. nen zur Verfügung, die Sie dazu benötigen. Auch für die Entwicklung
  557. von Anwendungssystemen aus dem Bereich der Meß- und Regeltechnik sowie
  558. der Datenkommunikation, ist dieses Produkt hervorragend geeignet.
  559.  
  560. Die folgenden Abschnitte der Dokumentation geben einen Abriß der 
  561. internen Arbeitsweise des Multi-Tasking Subsystems. Bedingt durch 
  562. den Umfang und die Vielschichtigkeit der Thematik, bleibe ich 
  563. allerdings die ganze Zeit über recht nahe an der Oberfläche. Dies 
  564. stellt zwar die Verständlichkeit meiner Ausführungen sicher, wird 
  565. jedoch die Tüftler unter Ihnen kaum richtig befriedigen. Aus diesem 
  566. Grunde habe ich am Ende dieser Dokumentation einige Quellen genannt, 
  567. in denen weitaus tiefgehendere und umfassendere Informationen zu den 
  568. angesprochenen Themen zu finden sind und die mir selbst bei der 
  569. Entwicklung dieses Produktes eine unschätzbare Hilfe waren.  
  570. .SE CPMULTI / Konzept
  571. 2. Konzept
  572.  
  573. Entsprechend dem Vorbild von UNIX und OS/2, wurde das Subsystem als
  574. Time-Sharing System und NICHT für Real-Time Programmierung entwor-
  575. fen. Dennoch ist die Reaktionszeit des Subsystems, insbes. im
  576. Bereich der Ereignisverarbeitung, vielfach auch für Echtzeitanwen-
  577. dungen ausreichend kurz.
  578. In erster Linie geht es jedoch darum, die vorhandene CPU-Leistung
  579. möglichst gerecht auf alle im System vorhandenen Prozesse zu vertei-
  580. len. Sobald ein Prozeß, der sich gerade im Zustand der Abarbeitung
  581. befindet, seine Zeitscheibe aufgebraucht hat, wird ihm zwangsweise
  582. die CPU entzogen und ein anderer auf CPU-Zuteilung wartender Prozeß
  583. aktiviert (preemptive scheduling).
  584. Eine in den Hardware-Timer-Interrupt eingeklinkte Systemroutine
  585. kontrolliert mehrmals in der Sekunde die Zeitzuteilung und leitet
  586. ggf. einen Task-Wechsel (Task-Switch) ein.
  587.  
  588. In den Vorgängerversionen war die minimale Größe einer Zeitscheibe
  589. auf ca. 55,5 Millisekunden festgelegt, wobei ein Standardwert von
  590. 110 Millisekunden voreingestellt war. Dies entspricht der Interrupt-
  591. frequenz der Systemuhr des PC.
  592. Auch die Version 2.02 behält diese Voreinstellung bei, da sie sich
  593. in der Mehrzahl der Anwendungsfälle als optimal herausgestellt hat.
  594. Gerade im Bereich der Datenkommunikation jedoch, ist selbst ein 18
  595. Mal in der Sekunde ausgeführter Taskwechsel (ca. 55,5ms je Zeit-
  596. scheibe) zu groß. Aus diesem Grunde wurde in der vorliegenden
  597. Version die Möglichkeit geschaffen, die Zeitscheibengröße "beliebig"
  598. zu verringern.
  599. "Beliebig", da ab dem Faktor 6 (ca. 9,25ms je Zeitscheibe) auf
  600. langsameren Rechnern Interrupt-Überläufe auftreten können, die zwar
  601. vom System abgefangen werden, jedoch unnötig CPU-Zeit verbrauchen,
  602. ohne dadurch eine weitere Geschwindigkeitssteigerung zu erzielen.
  603.  
  604. Ab der Version 2.10 des Subsystems wurde die Möglichkeit
  605. eingeführt, das preemptive Verhalten des Schedulers zu
  606. unterbinden. Eine globale Konstante (Preempt) kontrolliert, ob
  607. der Ablauf einer Zeitscheibe zu einem zwangsweise CPU-Entzug
  608. führen darf oder nicht. Wird diese auf FALSE gesetzt, obliegt
  609. es allein dem Programmierer durch geeignete Synchronisation
  610. seiner Tasks das gewünschte Taskwechselverhalten zu erzeugen.
  611.  
  612.  
  613. Dynamisches Scheduling 
  614.  
  615. Nun kann es aber aufgrund der Ausführung unter DOS passieren, daß 
  616. sich zu demjenigen Zeitpunkt, an dem ein Prozeß sein Quantum aufge-
  617. braucht hat, das Betriebssystem DOS in einem kritischen Zustand 
  618. befindet. Dies ist z. B. immer dann der Fall, wenn gerade ein 
  619. DOS-Funktionsaufruf bearbeitet wird. 
  620. Würde nun der aktive Prozeß ungeachtet dieses Umstands suspendiert, 
  621. so führte ein nachfolgender DOS-Funktionsaufruf durch einen anderen 
  622. Prozeß unweigerlich zu einem Systemabsturz übelster Sorte. Aus 
  623. diesem Grunde, darf ein Task-Switch immer nur dann erfolgen, wenn 
  624. sichergestellt ist, daß sich das Betriebssystem in einem konsisten-
  625. ten Zustand befindet selbst dann, wenn dadurch der aktive Prozeß 
  626. sein Quantum um einen nicht vorhersehbaren Wert überschreitet. 
  627. Die Zeitüberschreitung ist deshalb nicht vorhersehbar, weil theo-
  628. retisch ein Programm, welches sehr viele DOS-Aufrufe absetzt, in der 
  629. Zeit bis zur nächsten Überprüfung durch den Scheduler bereits die 
  630. nächste DOS-Funktion aufgerufen haben könnte, was dann wiederum zu 
  631. einer neuerlichen Überschreitung des Quantums um mindestens einen
  632. Timer-Tick führt.
  633.  
  634. Das andere Extrem wäre ein Prozeß, der jede Sekunde genau ein Mal
  635. eine zeitunkritische Abfrage auf einen bestimmten Port durchführt 
  636. (Meßdatenerfassung, Alarmanlagensteuerung, ...) und sich dann, 
  637. sollte kein Eingriff erforderlich werden, wieder eine Sekunde lang 
  638. schlafen legt. Ein derartiger Prozeß würde nur sehr selten seine 
  639. Zeitscheibe aufbrauchen, jedoch im Falle eines notwendig gewordenen 
  640. Eingriffs, ebenfalls nach Ablauf seines Zeitquantums suspendiert. Er 
  641. würde also, insgesamt gesehen, gegenüber einem "Dauerläufer" in 
  642. Hinblick auf die Zeitzuteilung benachteiligt. 
  643.  
  644. Um die Konsequenzen dieser Extremfälle, von denen letzterer weit 
  645. häufiger auftreten dürfte, ein wenig abzuschwächen, wurde ein in 
  646. gewissen Grenzen dynamisches Scheduling realisiert. 
  647. Ein Prozeß, der sein Quantum überzieht, wird solange bei der CPU-
  648. Zuteilung übergangen, bis die zuvor zuviel erhaltene Zeit verbraucht 
  649. ist. Andererseits erhält ein Prozeß, der seine Zeitscheibe permanent 
  650. nicht aufbraucht, die überzählige Zeit gutgeschrieben und darf bei 
  651. der nächsten Zuteilung entsprechend länger laufen.
  652. Um die zu "sühnende" bzw. gutgeschriebene Zeit nicht ins Unendliche 
  653. anwachsen zu lassen (dies kann insbes. bei der Gutschrift gesche-
  654. hen), wird eine maximale Anzahl +/- Ticks definiert, auf die diese 
  655. Dynamik beschränkt wird. 
  656. Beide Werte, Zeitscheibengröße und Grenze für dynamisches Schedu-
  657. ling, sind voll programmierbar. Standardmäßig ist eine Zeitschei-
  658. bengröße von zwei Timer-Ticks mit eine Grenze für die dynamische 
  659. Zeitzuteilung von +/- 10 Ticks eingestellt. 
  660.  
  661.  
  662. Prioritätensteuerung 
  663.  
  664. Ein weiterer Mechanismus zur Tasksteuerung, ist die Vergabe von
  665. Prioritäten.
  666. Das Subsystem kennt 9 verschiedene Prioritätsebenen. Für drei dieser
  667. Ebenen sind symbolische Namen verfügbar:
  668.  
  669. Pri_Nice   = Priorität 1
  670. Pri_User   = Priorität 5
  671. Pri_Kernel = Priorität 9
  672.  
  673. Die übrigen Ebenen lassen sich entweder als absoluter Zahlenwert
  674. oder als "Nuance" einer der drei Standard-Ebenen darstellen (z. B.
  675. Pri_User+1). Die mittlere Priorität ist "Pri_User" und wird auto-
  676. matisch auch für das Pascal-Hauptprogramm vergeben.
  677.  
  678. Der Scheduler behandelt die unterschiedlichen Prioritätsebenen
  679. wiefolgt: Solange sich in der Ebene "Pri_Kernel" ausführungsbereite
  680. Tasks befinden, wird die CPU NIEMALS einer Task zugeteilt, die sich
  681. in den Ebenen darunter befindet.
  682. Innerhalb einer Prioritätsebene werden die Tasks nach dem Round-
  683. Robin Prinzip zur Verarbeitung ausgewählt, d. h. sie kommen reihum
  684. zum Zuge.
  685.  
  686. Für den Programmierer hat diese Art des Schedulings natürlich
  687. Konsequenzen. Beim Design der Anwendung, muß er die einzelnen Tasks
  688. entwerfen und einer der 9 Prioritätsebenen zuteilen. Hierbei wird u.
  689. U. eine Entscheidung getroffen, die für den späteren Systemdurchsatz
  690. von grundlegender Bedeutung ist. Dies Erstentscheidung ist jedoch
  691. nicht endgültig, da die Prioritäten zur Laufzeit beliebig geändert
  692. werden können.
  693.  
  694. An dieser Stelle möchte ich nur einmal kurz anmerken, daß ich im 
  695. Folgenden die Begriffe "Task" und "Prozeß" als Synonyme verwende, 
  696. wenngleich dies vermutlich nicht bei allen Lesern auf Zustimmung 
  697. stoßen wird. Da man sich jedoch auch in den einschlägigen Veröffent-
  698. lichungen zum Thema Multi-Tasking über die Wortwahl nicht ganz einig 
  699. zu sein scheint, denke ich, mir diese Freiheit nehmen zu dürfen. 
  700.  
  701. Es empfielt sich, Tasks, die die meiste Zeit ihrer Existenz damit 
  702. verbringen, auf das Eintreten eines Ereignisses zu warten (Zeichen 
  703. von der Schnittstelle, Tastatureingaben, ...), mit sehr hoher 
  704. Priorität zu versehen. 
  705. Andererseits sollten Tasks, die beständig durchlaufen und ihre 
  706. Zeitscheibe vermutlich stets voll auskosten werden, in die Standard-
  707. ebene User gestellt werden. 
  708. Tasks mit niedrigster Priorität werden wohl nur sehr selten zur 
  709. Anwendung kommen. In der Ebene Nice läuft in der Regel nur ein 
  710. Null-Prozess ab, der die Aufgabe hat, die CPU zu beschäftigen, 
  711. solange keine anderen Prozesse verfügbar sind.
  712.  
  713.  
  714. Exclusivrechte
  715.  
  716. Befinden sich mehrere Task mit gleicher Priorität im System,
  717. von denen einzelne keinesfalls durch den Ablauf einer
  718. Zeitscheibe unterbrochen werden dürfen, so war es bisher nur
  719. möglich, für den gesamten kritischen Verarbeitungsabschnitt die
  720. CPU zu blockieren. Dies führte dazu, daß auch Timer und
  721. Ereignisse in dieser Zeit nicht erkannt wurden. Auch wurde die
  722. verbrauchte Zeit nicht im Task-Descriptor angerechnet.
  723.  
  724. Ab der Version 2.10 ist es möglich, für jede Task einen sog.
  725. Exclusiv-Modus zu aktivieren. Solange die Task diesen nicht
  726. wieder verläßt, wird sie nicht durch Ablauf ihrer Zeitscheibe
  727. und nicht durch eintretende Ereignisse unterbrochen. Timer und
  728. Ereignisse werden aber bearbeitet und die verbrauchte CPU-Zeit
  729. wird im Task-Descriptor vermerkt.
  730.  
  731. Der Exclusiv-Modus wirkt grundsätzlich nur auf Task-Ebene.
  732. Gibt eine Task im Exclusiv-Modus die CPU durch Aufruf einer
  733. blockierenden System-Funktion (z. B. SemWait()) auf, so wird
  734. dies in ihren Task-Descriptor eingetragen. Wird sie zu einem
  735. späteren Zeitpunkt reaktiviert, so setzt sie die Verarbeitung
  736. im Exclusiv-Modus fort.
  737.  
  738. Tasks, die sich nicht im Exclusiv-Modus befinden, werden
  739. weiterhin durch Ablauf ihrer Zeitscheibe unterbrochen (es sei
  740. denn, das Time-Slicing wurde generell abgeschaltet).
  741.  
  742.  
  743. Ein wunder Punkt mit Konsequenzen 
  744.  
  745. Ein wunder Punkt des vorliegenden Multi-Tasking Subsystems ist das 
  746. Fehlen von interruptgesteuertem Platten-I/O. Multi-Tasking Betriebs-
  747. systeme verfahren bei I/O-Operationen von und zu Plattenlaufwerken 
  748. so, daß nach Absetzen der I/O-Anforderung an die entsprechende 
  749. Hardware, der anfordernde Prozeß solange in Wartezustand versetzt 
  750. wird, bis ein die Hardware per Interrupt signalisiert, daß die 
  751. Anforderung vollständig bearbeitet ist (vereinfachte Darstellung!). 
  752. In der Zwischenzeit kann die CPU an andere Prozesse vergeben werden. 
  753. Aus diesem Grunde wird der Systemdurchsatz u. U. erheblich verbes-
  754. sert, wenn I/O-intensive Prozesse mit sehr hoher Priorität abgear-
  755. beitet werden; sie warten die meiste Zeit auf die Beendigung ihrer
  756. I/O-Anforderungen und behindern so die übrigen Tasks nicht. 
  757.  
  758. DOS, als Single-Tasking Betriebssystem, kennt diese Problematik 
  759. nicht und besitzt somit keinerlei Möglichkeiten, die CPU während des 
  760. Wartens auf das Beenden einer I/O-Anforderung an andere Prozesse zu 
  761. vergeben. Das Multi-Tasking Subsystem verfügt nicht über ein eige-
  762. nes, interruptgesteuertes Disk-Handling, bedient sich also der BIOS, 
  763. bzw. DOS-Routinen zur Durchführung von Plattenoperationen. Da 
  764. grundsätzlich kein Task-Switch während der Ausfürung einer DOS 
  765. Funktion oder des Platten-Interrupts 13H erfolgen darf, wird eine 
  766. I/O-intensive Task, die mit hoher Priorität gefahren wird, voraus-
  767. sichtlich das ganze System lahmlegen!!!!
  768.  
  769. Mittlerweile verfügen viele AT-Systeme über einen Interrupt-
  770. Mechanismus (Post/Wait-Interrupt 15H), der des einem
  771. Betriebssystem erlauben soll, bei Verwendung der
  772. BIOS-Plattenroutinen dennoch die CPU in Wartephasen an andere
  773. Prozesse zu vergeben.  MS-DOS, als Single-Task System, ignoriert
  774. diesen Mechanismus selbstverständlich.
  775.  
  776. Eine interne Version des Multi-Tasking Subsystems, die über
  777. einen eigenen Post/Wait-Handler verfügte, hat gezeigt, daß die
  778. Ausnutzung der Wartezeiten bei Plattenzugriffen zwar möglich
  779. ist, jedoch eine vollständig anderen Mechanismus zur
  780. Überwachung des internen DOS-Zustands erfordert und insgesamt
  781. weniger sicher ist.
  782.  
  783. Es war letztendlich eine Gewissensentscheidung, zugunsten
  784. erhöhter Betriebssicherheit des Produktes, eine geringfügig
  785. reduzierte Systemauslastung bei starkem Aufkommen von
  786. Plattenzugriffen in Kauf zu nehmen. Ich denke, hiermit in Ihrem
  787. Sinne gehandelt zu haben.
  788.  
  789.  
  790. Interprozeßkommunikation 
  791.  
  792. Was die Mechanismen zur Interprozeßkommunikation anbelangt, so 
  793. stehen Ihnen neben Semaphoren auch Message-Passing-Funktionen
  794. und Resourcen zur Verfügung.
  795. Das so unkomplizierte und daher auch so beliebte "Shared-Memory" 
  796. (gemeinsam genutzte Speicherbereiche) erfordert keine gesonderte 
  797. Systemunterstützung, da, bedingt durch die Struktur eines Turbo-
  798. Pascal Programmes, alle Tasks ein gemeinsames Datensegment und einen 
  799. gemeinsamen Heap besitzen. Der gleichzeitige Zugriff auf gemeinsam 
  800. genutzte Speicherbereiche muß allerdings durch geeignete 
  801. Semaphoren-Operationen synchronisiert werden (siehe hierzu Demopro-
  802. gramm PRO_CON).
  803. Vielfach kann bereits die Unit MtPipe mit ihrem Pipe-Treiber für den
  804. Datenaustausch zwischen einzelnen Tasks genutzt werden, wodurch der
  805. überwiegede Teil des Synchronisationsaufwands bereits automatisch
  806. erledigt wird.
  807.  
  808. Programmierbare Timer
  809.  
  810. Eine weitere, häufig benötigte Funktion ist der Timer. Das Multi-
  811. Tasking Subsystem stellt eine nur durch den zur Verfügung stehenden
  812. dynamischen Speicher begrenzte Anzahl von Timern mit einer
  813. Auflösung, die der jeweils aktuell eingestellten minimalen
  814. Zeitscheibengröße entspricht, zur Verfügung. Bedingt durch die
  815. Konzeption der Timer-Überwachung, bleibt der hierfür erforderliche
  816. Aufwand ungeachtet der Anzahl gleichzeitig aktiver Timer konstant
  817. (und minimal).
  818.  
  819. Es stehen Ihnen zwei verschiedene Arten von Timern zur Verfügung.
  820. Die Eine bewirkt, daß nach Ablauf eines Timers, das an der bei der
  821. Anforderung übergebenen Speicherstelle befindliche Byte incremen-
  822. tiert wird. Diese Form des Timers ist z. B. hervorragend für die
  823. Timeout-Überwachung in DFUE-Anwendungen geeignet.
  824.  
  825. Weiterhin haben Sie die Möglichkeit, sog. Watchdog-Timer zu
  826. definieren, die nach Ablauf des Timers den automatischen Aufruf
  827. einer bei der Definition des Timers übergebenen Prozedur bewirken.
  828. Diese Form der Timer wurde z. B. innerhalb des Kernels verwendet, um
  829. den Systemaufruf "Sleep" zu realisieren.
  830.  
  831.  
  832. Ereignisverarbeitung 
  833.  
  834. Als zeitweise recht nützlich, hat sich eine weitere Systemfunktion
  835. erwiesen: Das Ereignis. Ein Prozeß, der ein Ereignis anfordert,
  836. wird solange blockiert, bis das angeforderte Ereignis eingetreten
  837. ist.
  838.  
  839. Es stehen Ihnen zwei verschiedene Ereignisformen zur Verfügung.
  840. Die erste Form ermöglicht das Warten auf die Veränderung einer
  841. bestimmten Speicherstelle. Entstanden ist diese Funktion, um den
  842. Tail-Pointer eines Ringpuffers überwachen zu können, der durch eine
  843. Interrupt-Routine mit Daten gefüllt wurde.
  844.  
  845. Die zweite Variante wartet darauf, daß der an einem beliebigen Port
  846. anliegende Wert sich verändert (z. B. durch die Betätigung eines
  847. Tasters).
  848.  
  849. Da bei jedem Timer-Tick (d. h. mit der Frequenz der aktuell
  850. eingestellten minimalen Zeitscheibe) den Eintritt des Ereignisses
  851. überprüft wird, ist insbes. für diesen Bereich die Möglichkeit der
  852. Erhöhung der Interrupt-Frequenz interessant.
  853.  
  854. Ab der Version 1.30 sorgt der Systemkern dafür, daß eine Task, die
  855. durch ein Ereignis aus einem Wartezustand geweckt wird, an den Kopf
  856. der Scheduler-Queue ihrer Pritoritätsebene gestellt wird. So kann
  857. sie sehr rasch auf das eingetretene Ereignis reagieren, auch wenn
  858. noch andere Tasks derselben Priorität ausführungsbereit sind.
  859. Ab der Version 2.00 steht eine optimierte Verwaltung der Ereignista-
  860. belle zur Verfügung, wodurch die maximale Anzahl gleichzeitig
  861. aktiver Ereignisanforderungen erheblich erhöht wurde. Die Zeiter-
  862. sparnis bei der Ereignisprüfung wird jedoch durch eine geringfügig
  863. erhöhten Zeitbedarf bei der Einfügung/Löschung einer Ereignisanfor-
  864. derung in die/aus der Ereignistabelle erkauft.
  865. .SE CPMULTI / Realisierung
  866. 3. Realisierung
  867.  
  868. Mit Ausnahme eines ca. 2,5 KB großen Assembler-Kerns, der die 
  869. besonders maschinennahen Programmteile enthält, ist das gesamte 
  870. Subsystem als Turbo-Pascal 5.x Unit (TPU) realisiert worden.
  871.  
  872.  
  873. Das Herz des Systems, die Task-Table 
  874.  
  875. Das Kernstück des Subsystems bildet die Prozeßtabelle (Task-Table). 
  876. Diese ist aus Gründen der Performance als statische Tabelle im 
  877. Datensegment definiert und derzeit auf 50 Einträge begrenzt. Durch 
  878. Änderung der Konstanten MaxTasks und Neuübersetzung der Unit, läßt 
  879. sich dieser Wert problemlos erhöhen. 
  880.  
  881. Dies Task-Table enthält für jede mögliche Task einen Eintrag, den 
  882. sogenannten Task-Deskriptor. Der Aufbau dieser Verwaltungsstruktur 
  883. wird im Referenzteil näher betrachtet; für den Augenblick soll die 
  884. Feststellung genügen, daß in dieser Struktur alle Angaben abgelegt 
  885. sind, die den jeweiligen Ausführungszustand einer Task wiederspie-
  886. geln. 
  887.  
  888. Eine Task kann sich zu jedem Zeitpunkt in einem von 8 Zuständen 
  889. befinden. Der jeweilige Zustand ist in einem Feld des Task-Deskrip-
  890. tors abgelegt und über einen Systemaufruf zugänglich. Folgende 
  891. Zustände sind bekannt: 
  892.  
  893. Running..:  Diese Task besitzt gerade die Kontrolle. 
  894.    
  895. Ready....:  Diese Task ist ausführungsbereit, wartet jedoch 
  896.             augenblicklich auf CPU-Zuteilung. 
  897.             
  898. Sleeping.:  Diese Task hat sich selbst für eine gewisse Zeit 
  899.             suspendiert 
  900.             
  901. Crashed..:  Das Subsystem hat einen Stacküberlauf für diese Task 
  902.             erkannt und sie aus dem Verkehr gezogen; der Stack ist 
  903.             jedoch erhalten und kann z.B. mit einem Debugger unter-
  904.             sucht werden. 
  905.             
  906. Waiting..:  Diese Task wartet auf eine Semaphore oder ein 
  907.             Ereignis. 
  908.             
  909. Sending..:  Diese Task wartet darauf, daß diejenige Task, an 
  910.             die sie eine Nachricht gesendt hat, die Nachricht in 
  911.             Empfang nimmt. 
  912.             
  913. Receiving:  Diese Task wartet auf eine Nachricht von einer 
  914.             anderen Task. 
  915.             
  916. Suspended:  Diese Task wurde von einer anderen Task zeitweise 
  917.             inaktiv gesetzt. Sie kann auch nur von einer andern Task 
  918.             wieder in den Ready-Zustand versetzt werden.
  919.  
  920. Ein weiterer Zustand (Available) zeigt einen leeren Slot der Task-
  921. Table an. 
  922.  
  923. Der nun folgende Zustandsgraph zeigt alle Zustände und die möglichen
  924. Übergänge zwischen ihnen auf. Hierbei ist ein Übergang immer nur in 
  925. Richtung einer Pfeilspitze denkbar. 
  926.  
  927.                                                                             
  928.                        ┌───────────┐            13 ┌───────────┐            
  929.                ┌──────>│  Running  ├───────┬──────>│  Crashed  │            
  930.                │       └─────┬─────┘       │       └───────────┘            
  931.                │             │             │                                
  932.               2│            1│             │                                
  933.                │             │             └────────────┐                   
  934.                │             │                          │                   
  935.          ┌─────┴─────┐       │     4 ┌───────────┐      │                   
  936.          │   Ready   │<──────┴─────┬─┤ Waiting   │<─┐3  │                   
  937.          └───────────┘             │ ├───────────┤  │   │                   
  938.                                   6├─┤ Sleeping  │<─┤5  │                   
  939.                                    │ ├───────────┤  │7  │                   
  940.                                   8├─┤ Sending   │<─┼───┘                   
  941.                                    │ ├───────────┤  │                       
  942.                                  10├─┤ Receiving │<─┤9                      
  943.                                    │ ├───────────┤  │                       
  944.                                  12└─┤ Suspended │<─┘11                     
  945.                                      └───────────┘                          
  946.                                                                             
  947.             Zustandsgraph, Multi-Tasking Subsystem Version 2.10
  948.                     
  949.                                                         
  950. Verwaltung der Tasks 
  951.  
  952. Im System befindliche Tasks werden logisch über Queues verwaltet,
  953. die als verkettete Listen realisiert sind. Das Subsystem
  954. unterscheidet hierbei
  955.  
  956. a) die Task-Queues je Prioritätsebene, die ausschließlich Tasks in
  957.    den Zuständen "Running" und "Ready" enthalten,
  958. b) die Sender-Queue je Task-Deskriptor, in die diejenigen Tasks
  959.    eingereiht werden, die eine Message an den entsprechenden Prozeß
  960.    senden und auf deren Empfang warten möchten (Zustand "Sending").
  961.  
  962. Tasks in den Zuständen "Sleeping", "Suspended", "Receiving" und
  963. "Crashed" sind in keine Queue eingereiht.
  964. Tasks im Zustand "Waiting" hängen entweder in der Event-Table oder
  965. in der Warteschlange zu einer Semaphore bzw. Resource.
  966.  
  967. Wann immer eine neue Task erzeugt wird, wird ihr ein freier Slot der 
  968. Task-Table zugewiesen, dessen Nummer gleichzeitig zur Task-Nummer 
  969. wird. Einige der Systemfunktionen erhalten diese Task-Nummer als 
  970. Parameter. 
  971. Weiterhin erhält jede Task einen privaten Stack, der automatisch auf 
  972. dem Heap angelegt und initialisiert wird. 
  973. Nur wenn ein freier Task-Table-Slot verfügbar ist und die für den 
  974. Stack angeforderte Speichermenge vom Heap zugeordnet werden kann, 
  975. verläuft ein CreateTask-Systemaufruf erfolgreich. Er liefert dann 
  976. die Task-Nummer als Funktionswert. 
  977.  
  978.  
  979. Code-Sharing
  980.  
  981. Man beachte, daß Turbo-Pascal in hohem Maße Code generiert, der 
  982. reentrant ist und sich somit durchaus für Code-Sharing eignet. 
  983. Code-Sharing bedeutet einerseits, daß ein und dieselben Unterrouti-
  984. nen von mehreren Tasks gleichzeitig genutzt werden, wobei zu einem 
  985. Zeitpunkt ein und derselbe Code-Bereich theoretisch mehrmals paral-
  986. lel durchlaufen werden kann. 
  987. Andererseits kann Code-Sharing jedoch auch bedeuten, daß ein und 
  988. dieselbe PASCAL-Prozedur mehrmals als Task aktiviert werden kann, 
  989. wodurch sich zu einem Zeitpunkt ein und derselbe Programmcode, mit 
  990. jeweils einem eigenen Registersatz und einem privaten Stack, mehr-
  991. mals nebeneinander in Bearbeitung sein kann. 
  992.  
  993. Es ist bei der Verwendung von Code-Sharing allerdings durch geeig-
  994. nete Synchronistationsmechanismen, z. B. Semaphoren, dafür Sorge zu 
  995. tragen, daß kritische Bereiche immer nur von einer Task zu einem 
  996. Zeitpunkt durchlaufen werden können. Bei den Prozeduren/Funktionen 
  997. der Standard-Units ist dies nicht gewährleistet. 
  998. Nehmen wir die WriteLn-Prozedur als Beispiel, so kann es durchaus 
  999. passieren, daß während der Ausgabe eines Strings ein Task-Wechsel 
  1000. auftritt. Die Task, welche als nächste die Kontrolle erhält, ruft 
  1001. nun ihrerseits ebenfalls WriteLn auf, um einen String auszugeben. 
  1002. Nun, das Ergebnis ist zwar kein Programmabsturz, jedoch mit Sicher-
  1003. heit auch nicht das, was ursprünglich beabsichtigt war. 
  1004.  
  1005. Mittlerweile habe ich beide beschriebenen Arten von Code-Sharing 
  1006. ausprobiert und bin bislang auf keine technischen Probleme gestoßen. 
  1007.  
  1008.  
  1009. Programmierbare Timer 
  1010.  
  1011. Kommen wir nun zum Thema Timer: Ein Timer, der bei jedem Tick 
  1012. heruntergezählt und auf Ablauf untersucht werden muß, stellt für den 
  1013. Systemkern natürlich eine zusätzliche Belastung dar. Die CPU-Zeit, 
  1014. die für derartige Verwaltungsaufgaben verbraucht wird, steht auf der 
  1015. anderen Seite konsequenterweise den Anwendungsprozessen nicht mehr 
  1016. zur Verfügung. 
  1017. Bedenkt man nun, daß z. B. bei Kommunikationsanwendung mitunter 
  1018. recht viele Timer gleichzeitig aktiv sein können, so stellt sich 
  1019. schon die Frage nach einer "CPU-schonenden" Implementierung. 
  1020. Das in diesem System verwirklichte Konzept ist NICHT auf meinem 
  1021. eigenen Mist gewachsen; es ist der Timer-Behandlung in MINIX nachem-
  1022. pfunden, einem UNIX-ähnlichen Betriebssystem für IBM-PC/XT/AT und 
  1023. 100% kompatible Clones von Andrew S. Tanenbaum. 
  1024. MINIX sei übrigens jedem, der tiefer in die Betriebssystemprogram-
  1025. mierung einsteigen möchte, wärmstens als herrvorragendes Studienma-
  1026. terial empfohlen. Ohne dieses System und die zugehörige Grundlagen-
  1027. literatur, wäre mir die Programmierung des vorliegenden Subsystems 
  1028. sicherlich nicht innerhalb so kurzer Zeit möglich gewesen. 
  1029.  
  1030. Die Timer-Queue ist so organisiert, daß immer nur ihr erstes Element 
  1031. bei einem Timer-Tick überprüft werden muß. Ist der erste Timer 
  1032. abgelaufen, so wird er bearbeitet und aus der Queue entfernt. Der 
  1033. nachfolgende Timer rückt an seine Stelle usw., usw. Dies erfordert 
  1034. zwar etwas mehr Aufwand bei der einmaligen Einfügung eines Timers in 
  1035. die Queue, reduziert jedoch den bei jedem Uhreninterrupt erforder-
  1036. lich werdenden Aufwand auf ein Minimum. 
  1037.  
  1038.  
  1039. Beispiel:
  1040.                 ┌─────┬─┐    ┌─────┬─┐    ┌─────┬─┐ 
  1041. Timer-Queue ───>│  3  │─┼───>│  5  │─┼───>│  1  │─┼─┤ 
  1042.                 └─────┴─┘    └─────┴─┘    └─────┴─┘
  1043. Wartezeit:         3 Ticks      8 Ticks      9 Ticks 
  1044.                
  1045.  
  1046. Ereignisse 
  1047.  
  1048. Bevor ich näher auf die zur Verfügung stehenden Mechanismen zur 
  1049. Inter-Prozeß-Kommunikation eingehe, noch ein Wort zu den Ereignis-
  1050. sen. 
  1051. Sicherlich ist eine ganze Reihe von Ereignissen denkbar, die es
  1052. lohnt, vom Subsystem her zu unterstützen. In der vorliegenden
  1053. Version des Subsystems wird allerdings lediglich die Überwachung
  1054. einer Speicherstelle bzw. eines Ports auf Veränderung unterstützt.
  1055.  
  1056. Wozu benötige ich ein Ereignis?  Ein Ereignis kann den Systemdurch-
  1057. satz bisweilen erheblich steigern, da der auf das Eintreten dieses 
  1058. Ereignis wartende Prozeß nicht in einer Warteschleife CPU-Zeit 
  1059. verbraucht, sondern solange suspendiert wird, bis der Systemkern das 
  1060. Ereignis registriert. 
  1061. Der konkrete Anwendungsfall, der mich zur Implementierung des 
  1062. Ereignisses anregte, war ein Kommunikationsprogramm, in dem eine 
  1063. Task einen Ringpuffer auslesen sollte, der durch eine Interrupt-
  1064. Routine gefüllt wird. 
  1065.  
  1066. Die Interrupt-Routine wird von der Hardware jedesmal aktiviert, wenn 
  1067. ein Zeichen im Schnittstellenbaustein zum Auslesen bereitsteht, ein 
  1068. Zustand also, der sehr oft sehr schnell hintereinander auftreten 
  1069. kann. Demzufolge ist die Interrupt-Routine derart kurz zu halten, 
  1070. daß für das Pflegen einer Semaphore keine Zeit bleibt. 
  1071. Um eine schnelle Bearbeitung der in den Ringpuffer eingespeisten 
  1072. Zeichen zu erreichen (und einen Pufferüberlauf zu vermeiden), muß 
  1073. die Task, die für die Behandlung dieses Puffer zuständig ist, mit 
  1074. höchster Priorität gefahren werden. Dies jedoch setzt voraus, daß es 
  1075. Punkte gibt, an denen sie die Kontrolle abgibt und Tasks auf einer 
  1076. niedrigeren Prioritätsebene zu Zuge kommen läßt. Das wiederum wird 
  1077. niemals der Falls sein, wenn sie stets in einer Endlosschleife den 
  1078. Zustand der Puffer-Indizes abfragt. 
  1079. Sicher könnte sich die "Puffer-Task" immer dann für einen gewissen 
  1080. Zeitraum suspendieren (Sleep()), wenn der Zustand der Indizes 
  1081. anzeigt, daß keine Daten zur Bearbeitung anstehen, jedoch führt dies 
  1082. dazu, daß eine schnelle Reaktion auf während einer "Schlafphase" 
  1083. hereinkommende Zeichen unmöglich wird. Ja es könnte sogar der 
  1084. Ringpuffer übergelaufen sein, bevor die "Puffer-Task" wieder aus 
  1085. ihrem Schlaf erwacht. 
  1086.  
  1087. Ich denke, es dürfte klar geworden sein, daß die gerade geschilderte 
  1088. Situation ohne weitergehende Systemunterstützung nicht befriedigend 
  1089. lösbar ist. Mit Hilfe des Ereignisses ist es nun möglich, die 
  1090. "Puffer-Task" auf eine Veränderung desjenigen Rinpuffer-Index warten 
  1091. zu lassen, der von der Interrupt-Routine verändert wird. Solange, 
  1092. bis diese Veränderung eintritt, bleibt die betreffende Task inaktiv. 
  1093. Bei jedem Timer-Tick überprüft nun das System die Ereignistabelle 
  1094. und reaktiviert ggf. die Task deren Ereignis eingetreten ist. 
  1095. Auch hierbei muß mit einer gewissen Verzögerung gerechnet werden, 
  1096. jedoch liegt diese zumeist (wenn gerade keine DOS-Aktivitäten einen 
  1097. Task-Switch verhindern) nur im Bereich eines Timer-Ticks, also in
  1098. einem vertretbaren Rahmen.
  1099.  
  1100.  
  1101. Inter-Prozeß-Kommunikation 
  1102.  
  1103. Kommen wir nun zum interessantesten Funktionsbereich, den Funktionen 
  1104. zur Inter-Prozeß-Kommunikation, im Folgenden nur noch mit IPC 
  1105. abgekürzt. 
  1106.  
  1107. Wie bereits erwähnt, stellt das Multi-Tasking-Subsystem hier sowohl 
  1108. das Message-Passing, als auch Semaphoren zur Verfügung. Es würde zu 
  1109. weit führen, an dieser Stelle die Hintergründe dieser Mechanismen 
  1110. erschöpfend behandeln zu wollen, weshalb ich hierzu auf geeignete 
  1111. Literatur hinweisen möchte (siehe hierzu Literaturhinweise). 
  1112.  
  1113.  
  1114. A. Message-Passing 
  1115.  
  1116. Die Funktionen des Message-Passing sind sehr einfach und klar in 
  1117. ihrer Arbeitsweise. Wünscht eine Task an eine andere eine Nachricht 
  1118. zu senden, so wird sie in die Sende-Queue der Empfänger-Task einge-
  1119. reiht, sofern der Empfänger gerade nicht empfangsbereit ist. 
  1120. "Empfangsbereit" ist eine Task immer dann, wenn sie mittels eines 
  1121. Systemaufrufes den Empfang einer Nachricht von diesem SPEZIELLEN 
  1122. SENDER oder aber von IRGENDEINER Task angefordert hat. Wollte der 
  1123. Empfänger von einer ganz bestimmten Task empfangen, so bleiben alle 
  1124. anderen Sender unberücksichtigt!!  Soll von irgendeiner anderen Task 
  1125. empfangen werden, so werden die Sender in derjenigen Reihenfolge 
  1126. bearbeitet, in der sie ihre Sendeanforderung gestellt haben. 
  1127.  
  1128. Zusätzlich wurde die Möglichkeit realisiert, augenblicklich mit 
  1129. einem entsprechenden Returncode zurückzukehren, wenn die Gegenstelle 
  1130. nicht empfangsbereit ist oder (im Falle des Empfangsaufrufes) keine 
  1131. Sendeanforderung zur Bearbeitung ansteht. 
  1132.  
  1133.  
  1134. B. Semaphoren 
  1135.  
  1136. Semaphoren sind ein Thema, über das mittlerweile derart viel ge-
  1137. schrieben wurde, daß ich an dieser Stelle nur ihre interne Darstel-
  1138. lung näher erläutern werde. 
  1139.  
  1140. Intern besteht eine Semaphore aus einem zwei Byte langen Signal-
  1141. Count, der beim Anlegen der Semaphore auf den Wert 1 gesetzt wird 
  1142. und einer Warteschlange, in die anfordernde Prozesse bei Bedarf 
  1143. eingereiht werden. 
  1144. Ein Wert größer Null repräsentiert grundsätzlich einen Frei-Status, 
  1145. der Wert Null selbst stellt den Belegt-Status dar. 
  1146. Wann immer eine Semaphore, deren Signal-Count derzeit den Wert Null 
  1147. besitzt mit einem Wait-System-Call angesprochen wird, wird der 
  1148. anfordernde Prozeß solange suspendiert, bis der Signal-Count durch 
  1149. einen anderen Prozeß logisch erhöht wird. Letzteres führt dann zu 
  1150. einer Reaktivierung des ersten wartenden Prozesses, bzw. zu einer 
  1151. tatsächlichen Erhöhung des Signal-Count, falls keine wartenden 
  1152. Prozesse anstehen. 
  1153.  
  1154. Ungeachtet der internen Darstellung, werden Semaphoren innerhalb der 
  1155. Anwendung nur mit Hilfe von untypisierten Pointern (Typ Pointer) 
  1156. angesprochen. Ein derartiger Pointer wird beim Erzeugen einer
  1157. Semaphore zurückgeliefert und dient fortan als Handle in allen 
  1158. Funktionsaufrufen, die sich auf diese Semaphore beziehen. Die 
  1159. maximale Anzahl gleichzeitig aktiver Semaphoren ist nur durch den 
  1160. zur Verfügung stehenden Heap-Space begrenzt. 
  1161.  
  1162. C. "Named Pipes"
  1163.  
  1164. Pipes (Röhren) sind Kommunikationskanäle zwischen einzelnen Tasks.
  1165. Hierbei verläuft die Kommunikation immer in einer Richtung, d. h.
  1166. ein Partner schreibt in die Pipe und der andere liest daraus. Für
  1167. eine bidirektionale Kommunikation zwischen zwei Tasks sind also immer
  1168. zwei Pipes erforderlich.
  1169.  
  1170. Pipes sind ein sehr einfaches Medium für den Datenaustausch zwischen
  1171. unterschiedlichen Tasks. Der Pipe-Treiber übernimmt hierbei die
  1172. Aufabe, den Zugriff auf den Datenbereich der Pipe zu synchronisieren,
  1173. so daß die Kommunikationspartner selbst von dieser Aufgabe befreit
  1174. sind.
  1175.  
  1176. Ähnlich den UNIX Named Pipes, können auch im Multi-Tasking Subsystem
  1177. durchaus mehrere Prozesse gleichzeitig in eine Pipe schreiben bzw.
  1178. aus einer Pipe lesen. Die Pipe wird wie eine TEXT-Datei angesprochen,
  1179. d. h. alle Zugriffe auf die Pipe werden mittels der Pascal
  1180. Standardprozeduren für Textdateizugriffe durchgeführt.
  1181.  
  1182. Bitte entnehmen Sie die Einzelheiten der separaten Dokumentation zur
  1183. Unit MTPipe.
  1184.  
  1185. D. Resourcen
  1186.  
  1187. Resourcen verhalten sich ähnlich wie Semaphoren, jedoch ist in
  1188. der internen Datenstruktur die Prozeßnummer des jeweiligen
  1189. Eigentümers der Resource vermerkt. Fordert der Eigentümer
  1190. selbst die Resource erneut an (z. B. durch rekursiven Aufruf),
  1191. so wird er dadurch nicht blockiert, sondern der Aufruf wird
  1192. ignoriert.
  1193.  
  1194. Weiterhin hat der Programmierer bei der Verwendung von
  1195. Resourcen die Möglichkeit, beim Request-Aufruf anzugeben, ob
  1196. die betreffende Task auf das Freiwerden der Resource warten,
  1197. oder mit einem Fehlercode vom Aufruf zurückkehren und ihre
  1198. Verarbeitung fortsetzen soll.
  1199.  
  1200. Der Eigentümer einer Resource hat neben der vollständigen
  1201. Freigabe der Resource die Möglichkeit, diese zwischenzeitlich an
  1202. exakt eine wartende Task abzugeben und unmittelbar nach der
  1203. Freigabe durch letztere selbst wieder die Eigentumsrechte zu
  1204. erwerben. Diese Funktionalität entstand allerdings aus einem
  1205. speziellen Anwendungsfall heraus und wird nur selten sinnvoll
  1206. einsetzbar sein.
  1207.  
  1208.  
  1209. Hinweis zur Prozeßsynchronisation 
  1210.  
  1211. Zum Abschluß dieses Abschnitts noch ein Hinweis: Wie bereits er-
  1212. wähnt, sind für die Verwendung der Funktionen aus den Standard-Units 
  1213. (z. B. WriteLn) besondere Vorkehrungen zu treffen was die Absiche-
  1214. rung kritischer Bereiche betrifft. Dies gilt mitunter nicht nur für 
  1215. einzelne Funktionen, sondern auch für logisch zusammengehörige 
  1216. Operationen. 
  1217. Möchte z. B. eine Task an der Bildschirmposition 1,10 ein Zeichen 
  1218. ausgeben, eine andere Task jedoch ein Zeichen an der Position 20,12, 
  1219. so muß sichergestellt sein, daß der Ablauf "Cursorpositionierung 
  1220. (GotoXY) und Ausgabe des Zeichens (Write)" nicht durch eine Aktion 
  1221. unterbrochen werden kann, die in irgendeiner Art und Weise in diesen 
  1222. Vorgang eingreift. Es darf also niemals geschehen, daß genau zwi-
  1223. schen der Cursorpositionierung auf 1,10 und VOR der Ausgabe des 
  1224. Zeichens an dieser Stelle, die zweite Task den Cursor mal eben 
  1225. schnell für eigene Zwecke auf die Bildschirmposition 20,12 bewegt. 
  1226.  
  1227. Diese Konfliktsituation läßt sich auf zwei verschiedene Arten lösen: 
  1228. Einerseits kann man, wie in der Prozedur WriteCharXY im Demoprogramm 
  1229. PRO_CON.PAS gezeigt, die CPU für den kritischen Zeitraum blockieren. 
  1230. Diese Vorgehensweise ist jedoch nur bei sehr kleinen Abschnitten 
  1231. ratsam, da durch die Blockierung der CPU auch Funktionen wie z. B. 
  1232. Timer- und Ereignisüberwachung sowie die Zeitscheibensteuerung 
  1233. zeitweise außer Kraft gesetzt werden. 
  1234. In der Mehrzahl der Fälle ist einer Lösung mit Hilfe von Semaphoren, 
  1235. wie in den Funktionen RBuffGet und RBuffPut gezeigt, der Vorzug zu 
  1236. geben. Hier wird diejenige Task, die in den kritischen Bereich 
  1237. eintreten möchte, während dieser gerade von einer anderen Task 
  1238. durchlaufen wird, solange suspendiert, bis letztere den kritschen 
  1239. Bereich verläßt. 
  1240. .SE CPMULTI / Exportierte Typ- und Datendefinitionen
  1241. 4. Exportierte Typ- und Datendefinitionen
  1242.  
  1243. Globale Datentypen: 
  1244.  
  1245. Priority = Byte;
  1246.  
  1247.    Dieser Typ wird zur Festlegung der Task-Priorität bei der Erzeu-
  1248.    gung der Task angegeben.
  1249.  
  1250.  
  1251. WaitFlagType = (Wait, NoWait); 
  1252.  
  1253.    Dieser Typ wird als Parameter in den Funktionen des Message-Pas-
  1254.    sing verwendet und legt dort fest, ob im Falle fehlender Sende-/
  1255.    Empfangsbereitschaft gewartet wird, oder eine sofortige Rückkehr 
  1256.    mit Fehlercode gewünscht ist.
  1257.  
  1258.  
  1259. TaskType      = PROCEDURE(TaskParm:Pointer);
  1260.  
  1261.    Jede Task muß als FAR-Prozedur dieses Typs codiert werden.
  1262.  
  1263.  
  1264. TaskReturn = (Task_Crashed, Task_NotFound, Task_NoMsg,
  1265.               Task_NotReady, Task_OK, Task_Invalid); 
  1266.  
  1267.    Returncodes für die Message-Passing Funktionen. 
  1268.    
  1269.    Task_Crashed...: Die angesprochene Task steht aufgrund eines 
  1270.                     Stacküberlaufs nicht mehr zur Verfügung 
  1271.    Task_NotFound..: Es existiert keine aktive Task mit 
  1272.                     dieser Nummer 
  1273.    Task_NoMsg.....: Es steht keine Nachricht an (Receive 
  1274.                     mit NoWait) 
  1275.    Task_NotReady..: Die angesprochene Task ist nicht 
  1276.                     empfangsbereit (Send mit NoWait) 
  1277.    Task_OK........: Aktion erfolgreich 
  1278.    Task_Invalid...: ungültige Task-Nummer; wahrscheinlich 
  1279.                     außerhalb des zulässigen Bereiches 1..MaxTasks 
  1280.  
  1281.  
  1282. TaskStatus = (Running, Ready, Sleeping, Available, Crashed, 
  1283.               Waiting, Sending, Receiving, Suspended); 
  1284.  
  1285.    Returncode, der von der Funktion GetTaskState geliefert wird und 
  1286.    den Zustand der angesprochenen Task wiederspiegelt. 
  1287.    
  1288.  
  1289. SemReturn = (Sem_NoSpace, Sem_NotFree, Sem_OK, Sem_Invalid); 
  1290.  
  1291.    Returncodes der Semaphoren-Funktionen. 
  1292.    
  1293.    Sem_NoSpace..: Kein dynamischer Speicherplatz zum Anlegen der 
  1294.                   Semaphore mehr vorhanden 
  1295.    Sem_NotFree..: Versuch eine Semaphore zu löschen, in deren 
  1296.                   Warteschlange sich noch Tasks befinden 
  1297.    Sem_OK.......: Aktion erfolgreich 
  1298.    Sem_Invalid..: ungültiges Semaphoren-Handle (voraussichtlich 
  1299.                   NIL)
  1300.  
  1301.  
  1302. RscReturn = (Rsc_NoSpace, Rsc_NotFree, Rsc_OK, Rsc_NotOwner,
  1303.              Rsc_Invalid);
  1304.  
  1305.    Rsc_NoSpace..: kein Heap vorhanden
  1306.    Rsc_NotFree..: Belegt und/oder wartende Tasks bei DeleteResource
  1307.    Rsc_Ok.......: Aktion erfolgreich
  1308.    Rsc_NotOwner.: Versuchte Freigabe durch eine andere Task als den
  1309.                   Eigentümer
  1310.    Rsc_Invalid..: Übergabe eines NIL-Pointers
  1311.  
  1312.  
  1313. TaskNoType = Integer;
  1314.  
  1315.    Dies ist der Typ, in dem die Task-Nummer dargestellt wird. Die 
  1316.    Redefinition des Typs Integer wurde vorgenommen, um Ihr Programm 
  1317.    von einer evtl. später erforderlich werdenden Änderung in diesem 
  1318.    Bereich unabhängig zu machen.
  1319.            
  1320.  
  1321. BPtr          = ^Boolean; 
  1322.  
  1323.    Definition, die von GetTimBusy benötigt wird. 
  1324.  
  1325.  
  1326. PointerType   = RECORD CASE Integer OF 
  1327.                   1: (P    : Pointer); 
  1328.                   2: (POfs : Word;
  1329.                       PSeg : Word);
  1330.                 END;
  1331.  
  1332.    Redefinition eines Pointers, um an den Segment- und den Offset-
  1333.    teil zu gelangen.
  1334.  
  1335.  
  1336. WatchDogType  = PROCEDURE(WatchDogParm:Pointer);
  1337.  
  1338.    Eine Prozedur dieses Typs kann als Parameter an die Funktionen
  1339.    QueueWatchDog und CancelWatchDog übergeben werden.
  1340.  
  1341.  
  1342. Globale Konstanten 
  1343.  
  1344. Task_NoSpace 
  1345.  
  1346.    Returncode der Funktion CreateTask, der anstelle der Task-Nummer 
  1347.    zurückgeliefert wird, wenn kein dynamischer Speicher zum Anlegen 
  1348.    des Stacks vorhanden ist.
  1349.  
  1350.  
  1351. Task_NoSlot 
  1352.  
  1353.    Returncode der Funktion CreateTask, der anstelle der Task-Nummer 
  1354.    zurückgeliefert wird, wenn die maximale Anzahl gleichzeitig 
  1355.    aktiver Tasks überschritten wird.
  1356.  
  1357.  
  1358. StateText 
  1359.  
  1360.    Array welches den Task-Status im Klartext enthält und mit einen 
  1361.    Index vom Typ TaskStatus angesprochen wird. Diese Tabelle wird
  1362.    z. B. von der Prozedur DumpTaskTable benötigt.
  1363.  
  1364.  
  1365. AnyTask 
  1366.  
  1367.    Task-Nummer, die im Receive-Aufruf angegeben wird, wenn eine 
  1368.    Nachricht von einer beliebigen anderen Task empfangen werden 
  1369.    soll.
  1370.  
  1371. Preempt : Boolean = True;
  1372.  
  1373.    Standardmäßig führt das Multi-Tasking Subsystem einen
  1374.    Task-Wechsel durch, wann immer die Zeitscheibe einer Task
  1375.    abgelaufen ist und das System sich in einem konsistenten
  1376.    Zustand befindet. Für spezielle Anwendungen kann dieses
  1377.    Verhalten hinderlich, ja sogar gefährlich sein.
  1378.    Ist es erforderlich, daß der Programmierer volle Kontrolle
  1379.    darüber ausübt, wann ein Task-Wechsel durchgeführt werden
  1380.    darf, so kann Preempt auf FALSE gesetzt, und damit der
  1381.    Task-Wechsel durch Zeitscheibenablauf unterbunden werden.
  1382.  
  1383.  
  1384. Globale Variablen
  1385.  
  1386. InInt28 (Boolean)
  1387.  
  1388.    Dies ist ein Flag, das von einem externen Interrupt-Handler für 
  1389.    den DOS-Multitasking Interrupt 28H gesetzt werden kann, um dem 
  1390.    Subsystem anzuzeigen, daß ein Task-Wechsel nun gefahrlos möglich 
  1391.    ist, obwohl das DOS-Critical-Flag gerade gesetzt ist. Diese 
  1392.    Variable wird von der aufbauenden Unit MtPopUp benötigt. 
  1393. .SE CPMULTI / Interne Datenstrukturen
  1394. 5. Wichtige interne Datenstrukturen und Konstanten
  1395.  
  1396. Dieses Kapitel ist nur in der Dokumentation der lizenzierten Version
  1397. von CpMulti enthalten.
  1398. .SE CPMULTI / Referenzteil
  1399. 6. Funktionsbeschreibung (alphabetisch)
  1400.  
  1401.  
  1402. .RE BindCPU
  1403. Deklaration 
  1404.  
  1405.    PROCEDURE BindCPU; 
  1406.  
  1407. Funktion 
  1408.  
  1409.    Die Prozedur BindCPU dient zum zeitweisen Blockieren aller 
  1410.    Kernel-Funktionen. Insbesondere ist BindCPU für den Kernel selbst 
  1411.    vonnöten, wenn Operationen durchgeführt werden müssen, die eine 
  1412.    logisch unteilbare Einheit darstellen, jedoch aus mehreren 
  1413.    Anweisungen bestehen. Dies ist z. B. immer bei der Veränderung 
  1414.    einer Semaphore der Fall. 
  1415.  
  1416. Beispiel 
  1417.  
  1418.       ... 
  1419.    BindCPU;                                    {CPU blockieren}
  1420.    GotoXY(1,10);                               {untrennbare Aktion}
  1421.    Write('*');
  1422.    ReleaseCPU;                                 {CPU freigeben}
  1423.       ...
  1424.  
  1425. Anmerkungen 
  1426.  
  1427.    BindCPU legt SÄMTLICHE Kernel-Funktionen lahm, d. h. auch die 
  1428.    Timer- und Zeitscheibensteuerung sowie die Ereignisüberwachung. 
  1429.    Aus diesem Grunde sollte BindCPU nur für sehr kleine Programmab-
  1430.    schnitte verwendet werden, anderenfalls ist eine Lösung mit 
  1431.    Semaphoren vorzuziehen.
  1432.  
  1433. Siehe auch
  1434.  
  1435.    ReleaseCPU
  1436. .EE
  1437. ──────────────────────────────────────────────────────────────────
  1438. .RE CancelTimer
  1439. Deklaration
  1440.  
  1441.    FUNCTION CancelTimer(MemPtr:Pointer):BOOLEAN; 
  1442.  
  1443. Funktion 
  1444.  
  1445.    Ist ein Timer aktiv, der nach Ablauf die angegebene Speicherstel-
  1446.    le incrementiert, so wird dieser deaktiviert und der Funktions-
  1447.    wert TRUE geliefert. Der Funktionswert FALSE zeigt an, daß kein 
  1448.    derartiger Timer gefunden wurde. 
  1449.  
  1450. Beispiel 
  1451.  
  1452.    VAR Timeout : Boolean; 
  1453.        Ok      : Boolean; 
  1454.          ...
  1455.    Timeout := False;                           {Initialisierung}
  1456.    IF QueueTimer(@Timeout,Seconds(1))          {Timer anfordern}
  1457.       THEN  BEGIN
  1458.                REPEAT                          {Warteschleife}
  1459.                ...                             {Aktionen}
  1460.                UNTIL Timeout OR Ok; 
  1461.                IF CancelTimer(@Timeout) THEN;  {Timer ggf. löschen} 
  1462.                ...                             {weitere Aktionen}
  1463.             END; 
  1464.  
  1465. Siehe auch 
  1466.  
  1467.    QueueTimer
  1468. .EE
  1469. ──────────────────────────────────────────────────────────────────
  1470. .RE CancelWatchdog
  1471. Deklaration
  1472.  
  1473.    FUNCTION  CancelWatchDog(TWatchDog:WatchDogType):BOOLEAN;
  1474.  
  1475. Funktion
  1476.  
  1477.    Löschen eines WatchDog Timers aus dem System, sofern dieser
  1478.    vorhanden ist.
  1479.    Der Parameter "TWatchDog" muß derselbe sein, der auch beim
  1480.    entsprechenden Aufruf von "QueueWatchDog" angegeben wurde.
  1481.    Die Funktion liefert TRUE, wenn der Timer erfolgreich entfernt
  1482.    wurde. FALSE wird zurückgeliefert, wenn entweder kein entspre-
  1483.    chender Timer gefunden wurde oder aber ein ungültiger Parameter
  1484.    übergeben wurde.
  1485.  
  1486. Anmerkungen
  1487.  
  1488.    Sind mehrere WatchDog Timer mit derselben Prozeduradresse aktiv,
  1489.    so wird durch jeden CancelWatchDog Aufruf der jeweils erste
  1490.    dieser Timer gelöscht.
  1491.  
  1492. Siehe auch
  1493.  
  1494.    Typdefinitionen WatchDogType, QueueWatchDog
  1495. .EE
  1496. ──────────────────────────────────────────────────────────────────
  1497. .RE ChangePri
  1498. Deklaration
  1499.  
  1500.    PROCEDURE ChangePri(Task:TaskNoType; Pri:Priority);
  1501.  
  1502. Funktion
  1503.  
  1504.    Mittels "ChangePri" kann eine Task die Priorität einer anderen
  1505.    Task verändern, vorausgesetzt, sie kennt deren Task-Nummer.
  1506.    "Task" enthält die ID derjenigen Task, deren Priorität verändert
  1507.    werden soll; "Pri" enthält die neue Priorität.
  1508.  
  1509. Siehe auch
  1510.  
  1511.    Typdefinitionen Priority und TaskNoType, SetPri
  1512. .EE
  1513. ──────────────────────────────────────────────────────────────────
  1514. .RE CreateRsc
  1515. Deklaration
  1516.  
  1517.    FUNCTION  CreateRsc(VAR RscPtr:Pointer):RscReturn;
  1518.  
  1519. Funktion
  1520.  
  1521.    Erzeugen einer Resource. Das Handle, unter dem diese Resource
  1522.    zukünftig angesprochen werden kann, wird in der Variablen RscPtr
  1523.    abgelegt. Der Returncode zeigt Erfolg oder Mißerfolg der Aktion
  1524.    an.
  1525.  
  1526. Beispiel
  1527.  
  1528.    VAR  Resource : Pointer;       {Handle}
  1529.          ...
  1530.    IF CreateRsc(Resource) <> Rsc_Ok
  1531.       THEN Error;
  1532.  
  1533. Siehe auch
  1534.  
  1535.    Typdefinition RscReturn, RemoveRsc, RemoveRscKill
  1536. .EE
  1537. ──────────────────────────────────────────────────────────────────
  1538. .RE CreateSem
  1539. Deklaration
  1540.  
  1541.    FUNCTION CreateSem(VAR SemPtr:Pointer):SemReturn; 
  1542.  
  1543. Funktion 
  1544.  
  1545.    Erzeugen einer Semaphore. Das Handle, unter dem diese Semaphore
  1546.    zukünftig angesprochen werden kann, wird in der Variablen SemPtr 
  1547.    abgelegt. Der Returncode zeigt Erfolg oder Mißerfolg der Aktion 
  1548.    an. 
  1549.  
  1550. Beispiel 
  1551.  
  1552.    VAR  Semaphore : Pointer;       {Handle}
  1553.          ...
  1554.    IF CreateSem(Semaphore) <> Sem_Ok 
  1555.       THEN Error; 
  1556.  
  1557. Siehe auch 
  1558.  
  1559.    Typdefinition SemReturn, RemoveSem, RemoveSemKill
  1560. .EE
  1561. ──────────────────────────────────────────────────────────────────
  1562. .RE CreateTask
  1563. Deklaration
  1564.  
  1565.    FUNCTION  CreateTask(TaskPointer:TaskType; UserData:Pointer;
  1566.                      Level:Priority; BytesStack:Word):TaskNoType;
  1567.  
  1568. Funktion
  1569.  
  1570.    Mit dieser Funktion wird eine Task erzeugt. Der Parameter "Task-
  1571.    Pointer" muß die Adresse derjenigen Prozedur enthalten, die als
  1572.    Task aktiviert werden soll.
  1573.    Der zweite Parameter, "UserData", ist ein untypisierter Pointer,
  1574.    der der Task bei ihrem Start als Parameter übergeben wird.
  1575.    Der Parameter "Level" beschreibt die Prioritätsebene, in die die
  1576.    Task eingereiht werden soll. Durch den letzen Parameter wird
  1577.    schlußendlich die Größe des privaten Stacks in Byte mitgeteilt.
  1578.    Die Funktion CreateTask liefert einen positiven Wert vom Typ
  1579.    TaskNoType wenn die Task erfolgreich angelegt werden konnte.
  1580.    Anderenfalls zeigt der Returncode (dann negativ), die genaue
  1581.    Fehlerursache an.
  1582.  
  1583. Beispiel
  1584.  
  1585.    {$F+}
  1586.    PROCEDURE SubTask(Parm:Pointer);
  1587.    {
  1588.    Dies ist eine eigene Task, die jede Sekunde einen Piepston
  1589.    erzeugt.
  1590.    }
  1591.    BEGIN {SubTask}
  1592.       REPEAT                                   {Taskrumpf ─┐ }
  1593.          Sleep(Seconds(1));                    {           │ }
  1594.          Sound(1000);                          { Aktionen  │ }
  1595.          Delay(20);                            {    :      │ }
  1596.          NoSound;                              {           │ }
  1597.       UNTIL False;                             {Taskrumpf ─┘ }
  1598.    END;  {SubTask}
  1599.    {$F-}
  1600.          ...
  1601.          ...
  1602.    BEGIN {Main}
  1603.    IF CreateTask(SubTask,NIL,Pri_User,300) < 0
  1604.       THEN Error;
  1605.          ...
  1606.    END. {Main}
  1607.  
  1608.    Anmerkungen
  1609.  
  1610.    Eine Task wird programmtechnisch grundsätzlich als Prozedur vom
  1611.    Typ "TaskType" realisiert, deren Rumpf aus einer Endlosschleife
  1612.    gebildet wird!!! Ferner muß die Prozedur als FAR-Prozedur codiert
  1613.    werden (siehe Turbo Pascal Handbuch, $F+/- Compiler-Schalter).
  1614.    Ab der Version 2.00 des Subsystems, darf eine Task sowohl durch
  1615.    expliziten Aufruf der Prozedur "Terminate", als auch durch
  1616.    Verlassen der Task-Prozedur beendet werden. Im letzteren Fall
  1617.    wird "Terminate" implizit aufgerufen.
  1618.    Eine Task kann durchaus lokale Variablen besitzen, jedoch ist bei
  1619.    der Dimensionierung des Stacks zu berücksichtigen, daß lokale
  1620.    Variable grundsätzlich auf dem Stack angelegt werden. Der für
  1621.    diese benötigte Speicherplatz ist also der Stackgröße, die für
  1622.    die Ausführung der Task erforderlich ist, hinzuzuaddieren.
  1623.    Ein weiteres Faktum, das Beachtung verdient, ist die Tatsache,
  1624.    daß der Parameter "BytesStack" in KEINER WEISE auf Gültigkeit
  1625.    geprüft wird. Es obliegt also dem Programmierer, sicherzustellen,
  1626.    daß dieser Wert stets größer als Null ist. Der Grund für diese
  1627.    Vorgehensweise ist der, daß für das Pascal-Hauptprogramm, welches
  1628.    ebenfalls als Task bei der Systeminitialisierung aktiviert wird,
  1629.    kein Stack auf dem Heap reserviert wird. In diesem Fall ist also
  1630.    eine Stackgröße von Null als Parameter zulässig.
  1631.  
  1632. Siehe auch
  1633.  
  1634.    Typdefinitionen für TaskType, TaskNoType und Priority, globale
  1635.    Konstanten, Terminate
  1636. .EE
  1637. ──────────────────────────────────────────────────────────────────
  1638. .RE DoShutdown
  1639. Deklaration 
  1640.  
  1641.    PROCEDURE DoShutdown;
  1642.  
  1643. Funktion 
  1644.  
  1645.    Deaktivieren des Multi-Tasking; Wiederherstellen aller 
  1646.    abgefangenen Interrupts. 
  1647.  
  1648. Anmerkungen 
  1649.  
  1650.    Diese Prozedur wird in der Regel ausschließlich durch die 
  1651.    Exit-Prozedur der Subsystem-Unit aufgerufen.
  1652. .EE
  1653. ──────────────────────────────────────────────────────────────────
  1654. .RE DumpTaskTable
  1655. Deklaration
  1656.  
  1657.    PROCEDURE DumpTaskTable; 
  1658.    
  1659. Funktion
  1660.  
  1661.    Ausgabe des aktuellen Zustands aller aktiven Tasks auf dem 
  1662.    Bildschirm. Dies Funktion kann zu Diagnosezwecken aufgerufen 
  1663.    werden. 
  1664.  
  1665. Beispiel
  1666.  
  1667.    Beispielhafte Bildschirmausgabe:
  1668.    
  1669.    Nr.  Status      Priorität  Slice  Stack free  CPU     Excl.
  1670.      1     Ready      Pri_Nice     2         265       0  FALSE
  1671.      2     Ready      Pri_User     5          -1     151  FALSE
  1672.      3   Waiting    Pri_Kernel     1         235       1  FALSE
  1673.      4   Running    Pri_Kernel    -8         213       8  FALSE
  1674.      5   Waiting    Pri_Kernel     0          33       2  FALSE
  1675.  
  1676.  
  1677. Anmerkungen
  1678.  
  1679.    Der aktuelle Bildschirminhalt wird zerstört. Soll ein Statusfen-
  1680.    ster realisiert werden, so ist die Realisierung mittels einer 
  1681.    eigenen Routine, die den TaskStatus selbst abfragt, vorzuziehen.
  1682.    Die Spalte CPU beschreibt die Anzahl Ticks, die diese Task 
  1683.    bereits verbraucht hat. 
  1684.    Negative Slice-Werte sind "Gutschriften".
  1685.  
  1686. Siehe auch
  1687.  
  1688.    Globale Konstante StateText, GetTaskState
  1689. .EE
  1690. ──────────────────────────────────────────────────────────────────
  1691. .RE EnterExclusive
  1692. Deklaration
  1693.  
  1694.    FUNCTION  EnterExclusive:BOOLEAN;
  1695.  
  1696. Funktion
  1697.  
  1698.    Eintritt der Task in den Exclusiv-Modus. Der Funktionswert
  1699.    liefert den vorhergehenden Inhalt des Exclusive-Flags. So
  1700.    ist es z. B. möglich, Prozeduren zu schreiben, die für die
  1701.    Dauer ihrer Ausführung den Exclusiv-Modus einschalten und
  1702.    abschließend den vorhergehenden Zustand wiederherstellen.
  1703.    Die Funktion RemoveRscKill gehört zu dieser Kategorie.
  1704.  
  1705. Beispiel
  1706.  
  1707.    PROCEDURE XYZ;
  1708.  
  1709.    VAR Ex : Boolean;
  1710.  
  1711.    BEGIN {XYZ}
  1712.      Ex := EnterExclusive;
  1713.      SometingUseful;
  1714.      IF Not Ex
  1715.         THEN LeaveExclusive;
  1716.    END;  {XYZ}
  1717.  
  1718. Siehe auch
  1719.  
  1720.    LeaveExclusive
  1721.  
  1722. .EE
  1723. ──────────────────────────────────────────────────────────────────
  1724. .RE EventWait
  1725. Deklaration
  1726.  
  1727.    FUNCTION EventWait(MemPtr:Pointer):Boolean;
  1728.  
  1729. Funktion
  1730.  
  1731.    Diese Funktion existiert ab der Version 2.00 des Subsystem nicht
  1732.    mehr. Sie wurde durch dir Funktionen "MemoryEventWait" und
  1733.    "PortEventWait" ersetzt.
  1734. .EE
  1735. ──────────────────────────────────────────────────────────────────
  1736. .RE GetPID
  1737. Deklaration
  1738.  
  1739.    FUNCTION GetPID:TaskNoType; 
  1740.  
  1741. Funktion
  1742.  
  1743.    Diese Funktion erlaubt es einer Task, ihre eigene Task-Nummer zu 
  1744.    ermitteln.
  1745.  
  1746. Siehe auch
  1747.  
  1748.    Typdefinition TaskNoType
  1749. .EE
  1750. ──────────────────────────────────────────────────────────────────
  1751. .RE GetPri
  1752. Deklaration
  1753.  
  1754.    FUNCTION GetPri:Priority
  1755.  
  1756. Funktion
  1757.  
  1758.    Ermitteln der Priorität der aufrufenden Task. Diese Funktion kann
  1759.    verwendet werden, um temporär in Unterroutinen, die die ursprüng-
  1760.    liche Prioritätseinstellung des Aufrufers nicht kennen, die
  1761.    Priorität zu verändern.
  1762.  
  1763. Beispiel
  1764.  
  1765.    PROCEDURE SomeStuff;
  1766.  
  1767.    VAR OldPri : Priority;
  1768.  
  1769.    BEGIN {SomeStuff}
  1770.      OldPri := GetPri;                         { sichern }
  1771.      SetPri(Pri_Kernel);                       { anheben }
  1772.      SomethingVeryImportant;
  1773.      SetPri(OldPri);                           { zurücksetzen }
  1774.    END; {SomeStuff}
  1775.  
  1776. Siehe auch
  1777.  
  1778.    Typedefinition Priority, SetPri
  1779. .EE
  1780. ──────────────────────────────────────────────────────────────────
  1781. .RE GetTaskState
  1782. Deklaration
  1783.  
  1784.    FUNCTION GetTaskState(Task:TaskNoType):TaskStatus; 
  1785.  
  1786. Funktion
  1787.  
  1788.    GetTaskState liefert den Status der Task, deren Task-Nummer als 
  1789.    Parameter übergeben wurde.
  1790.  
  1791. Beispiel
  1792.  
  1793.    Ausgabe des eigenen Task-Status im Klartext:
  1794.  
  1795.    Writeln(StateText[GetTaskState(GetPID)]);
  1796.  
  1797. Siehe auch
  1798.  
  1799.    Typdefinitionen TaskStatus und TaskNoType sowie Konstante State-
  1800.    Text
  1801. .EE
  1802. ──────────────────────────────────────────────────────────────────
  1803. .RE GetTimBusy
  1804. Deklaration
  1805.  
  1806.    FUNCTION GetTimBusy:BPtr; 
  1807.  
  1808. Funktion
  1809.  
  1810.    Diese Funktion liefert einen Zeiger auf das Timer-Busy-Flag, das 
  1811.    immer dann gesetzt ist, wenn gerade der Scheduler aktiv ist oder 
  1812.    durch BindCPU das Kernsystem die CPU blockiert wurde. 
  1813.    Dieses Busy-Flag muß zuweilen für externe Interrupt-Service 
  1814.    Routinen zugänglich sein, wie sie z. B. auch in der Unit MtPopUp 
  1815.    zu finden sind.
  1816.    Wann immer das Timer-Busy-Flag gesetzt ist, darf eine Inter-
  1817.    rupt-Routine KEINE Systemfunktion aufrufen!!
  1818.  
  1819. Siehe auch 
  1820.  
  1821.    Typdefinition BPtr
  1822. .EE
  1823. ──────────────────────────────────────────────────────────────────
  1824. .RE GrantRsc
  1825. Deklaration
  1826.  
  1827.    FUNCTION  GrantRsc(RscPtr:Pointer):RscReturn;
  1828.  
  1829. Funktion
  1830.  
  1831.    Durch den Aufruf von GrantRsc gewährt der aktuelle
  1832.    Eigentümer der Resource der ersten, auf diese Rescource
  1833.    wartenden Task den Zugang zu derselben.
  1834.    Sobald diese die Resource freigibt, wird der Aufrufer wieder
  1835.    zum Eigentümer, ungeachtet weiterer wartender Tasks.
  1836.  
  1837.    Ist der Aufrufer nicht der Eigentümer der Resource, erhält
  1838.    er den Returncode Rsc_NotOwner zurückgeliefert; eine Aktion
  1839.    findet nicht statt.
  1840.  
  1841. Beispiel
  1842.  
  1843.    VAR Resource : Pointer;
  1844.  
  1845.    IF CreateRsc(Resource) <> Rsc_Ok
  1846.       THEN Error;
  1847.         .........
  1848.  
  1849.    IF RequestRsc(Resource,Wait) <> Rsc_Ok  {Resource anfordern}
  1850.       THEN Error;
  1851.         .........
  1852.    { kritischer Bereich Teil 1 }
  1853.         .........
  1854.    IF GrantRsc(Resource) <> Rsc_Ok         {laß jemand anderen}
  1855.       THEN Error;                          {zu Wort kommen}
  1856.         .........
  1857.    { kritischer Bereich Teil 2 }
  1858.         .........
  1859.    IF ReleaseRsc(Resource) <> Rsc_Ok       {Resource freigeben}
  1860.       THEN Error;
  1861.         .........
  1862.  
  1863. Siehe auch
  1864.  
  1865.    Typdefinition RscReturn, RequestRsc, ReleaseRsc
  1866. .EE
  1867. ──────────────────────────────────────────────────────────────────
  1868. .RE Kill
  1869. Deklaration
  1870.  
  1871.    PROCEDURE Kill(Task:TaskNoType);
  1872.  
  1873. Funktion
  1874.  
  1875.    Die Funktion Kill bewirkt ein sofortiges Entfernen der Task mit
  1876.    der Task-Nummer "Task" aus dem System.
  1877.    Befindet sich die Task gerade in einer Warteschlange (z. B. der
  1878.    Warteschlange einer Semaphore), so wird sie aus dieser entfernt.
  1879.  
  1880. Anmerkungen
  1881.  
  1882.    Ein Aufruf von "Kill" mit der eigenen Task-Nummer entspricht dem
  1883.    Aufruf der "Terminate" Prozedur.
  1884.  
  1885. Siehe auch
  1886.  
  1887.    Typdefinition TaskNoType, Terminate
  1888. .EE
  1889. ──────────────────────────────────────────────────────────────────
  1890. .RE LeaveExclusive
  1891. Deklaration
  1892.  
  1893.    PROCEDURE LeaveExclusive;
  1894.  
  1895. Funktion
  1896.  
  1897.    Verlassen des Exclusiv-Modus.
  1898.  
  1899. Siehe auch
  1900.  
  1901.    EnterExclusive
  1902. .EE
  1903. ──────────────────────────────────────────────────────────────────
  1904. .RE MemoryEventWait
  1905. Deklaration
  1906.  
  1907.    FUNCTION  MemoryEventWait(MemPtr:Pointer):Boolean;
  1908.  
  1909. Funktion
  1910.  
  1911.    Warte auf die Veränderung der Speicherstelle, auf die MemPtr
  1912.    zeigt. Ein Funktionswert von TRUE zeigt eine erfolgreiche Been-
  1913.    digung des Vorhabens an; der Wert FALSE wird geliefert, wenn die
  1914.    maximale Anzahl gleichzeitig aktiver Ereignisse überschritten
  1915.    wurde.
  1916.  
  1917. Beispiel
  1918.  
  1919.    VAR HeadPointer : Word;                     {Ringpufferzeiger}
  1920.        TailPointer : Word;                     {        "       }
  1921.             ...
  1922.    IF NOT MemoryEventWait(@TailPointer)        {warte auf Änderung}
  1923.       THEN Error                               {des Zeigers}
  1924.       ELSE Verarbeite;
  1925.             ...
  1926. .EE
  1927. ──────────────────────────────────────────────────────────────────
  1928. .RE PortEventWait
  1929. Deklaration
  1930.  
  1931.    FUNCTION  PortEventWait(EPortNo:Word;VAR ENewValue:Byte):Boolean;
  1932.  
  1933. Funktion
  1934.  
  1935.    Warte auf die Veränderung des Wertes, der an dem angegebenen Port
  1936.    anliegt. Der neue Wert wird in der Variablen "ENewValue" abgelegt
  1937.    und so der anfordernden Task verfügbar gemacht.
  1938.    Ein Funktionswert von TRUE zeigt eine erfolgreiche Beendigung des
  1939.    Vorhabens an; der Wert FALSE wird geliefert, wenn die maximale
  1940.    Anzahl gleichzeitig aktiver Ereignisse überschritten wurde.
  1941.  
  1942. Beispiel
  1943.  
  1944.    VAR Taster      : Byte;                     {Bit-Maske}
  1945.             ...
  1946.    IF NOT PortEventWait(TasterPort,Taster)     {warte auf Druck}
  1947.       THEN Error                               {eines Tasters}
  1948.       ELSE Verarbeite(Taster);
  1949.             ...
  1950. .EE
  1951. ──────────────────────────────────────────────────────────────────
  1952. .RE QueueTimer
  1953. Deklaration
  1954.  
  1955.    FUNCTION QueueTimer(MemPtr:Pointer; Counter:Word):BOOLEAN; 
  1956.  
  1957. Funktion
  1958.  
  1959.    Aktivieren eines Timers, der nach Ablauf von "Counter" Timer-
  1960.    Ticks das Byte an der Speicherstelle, auf die "MemPtr" zeigt, 
  1961.    incrementiert. 
  1962.    Ist ausreichen Speicherplatz zum Anlegen eines Timers vorhanden, 
  1963.    so wird der Funktionswert TRUE geliefert; anderenfalls FALSE. 
  1964.    
  1965. Beispiel
  1966.  
  1967.    VAR Timeout : Boolean;                      {Timeout-Merker}
  1968.             ...
  1969.    Timeout := False;                           {Initialisieren}
  1970.    IF NOT QueueTimer(@Timeout,Seconds(1))      {Timeout nach 1 Sek.}
  1971.       THEN Error;
  1972.             ...
  1973.    IF CancelTimer(@Timeout)                    {Löschen des Timers}
  1974.       THEN ;                                   {falls noch vorh.} 
  1975.             ...
  1976.  
  1977.  Siehe auch
  1978.  
  1979.    CancelTimer
  1980. .EE
  1981. ──────────────────────────────────────────────────────────────────
  1982. .RE QueueWatchDog
  1983. Deklaration
  1984.  
  1985.    FUNCTION  QueueWatchDog(TWatchDog:WatchDogType; Counter:LongInt;
  1986.                            WatchDogParm:Pointer):BOOLEAN;
  1987.  
  1988. Funktion
  1989.  
  1990.    Aktivieren eines Timers, der nach Ablauf von "Counter" Timer-
  1991.    Ticks die Prozedur aufruft, auf die "TWatchDog" zeigt.
  1992.    Der Parameter "WatchDogParm" wird transparent an die aufgerufene
  1993.    Prozedur weitergegeben.
  1994.    Ist ausreichen Speicherplatz zum Anlegen eines Timers vorhanden,
  1995.    so wird der Funktionswert TRUE geliefert; anderenfalls FALSE.
  1996.  
  1997. Beispiel
  1998.  
  1999.    {$F+}
  2000.    PROCEDURE Dog(P:Pointer);
  2001.    BEGIN {Dog}
  2002.      Writeln('Wau Wau!');
  2003.    END;  {Dog}
  2004.    {$F-}
  2005.             ...
  2006.    IF NOT QueueWatchDog(Dog,Seconds(1),NIL)    {Aufruf nach 1 Sek.}
  2007.       THEN Error;
  2008.             ...
  2009.    IF CancelWatchDog(WatchDog)                 {Löschen des Timers}
  2010.       THEN ;                                   {falls noch vorh.}
  2011.             ...
  2012.  
  2013. Anmerkungen
  2014.  
  2015.    Die WatchDog-Prozedur wird im "Kernel-Mode" aufgerufen, d. h.
  2016.    außerhalb jeglicher Task. Die CPU ist zu diesem Zeitpunkt
  2017.    blockiert und darf UNTER KEINEN UNSTÄNDEN freigegeben werden.
  2018.    Da nahezu jede Systemfunktion des Multi-Tasking Subsystems
  2019.    zeitweise die CPU blockiert und hinterher wieder freigibt
  2020.    (BindCPU / ReleaseCPU), darf KEINE dieser Funkionen/Prozeduren
  2021.    innerhalb des WatchDog verwendet werden.
  2022.    Bitte beachten Sie, daß das Subsystem KEINERLEI Möglichkeit
  2023.    besitzt, dies zu verhindern, so daß im Ernstfall ein System-
  2024.    absturz (durch reentrantes Aufrufen des Schedulers) oder zumin-
  2025.    dest ein höchst merkwürdiges Verhalten des Systems die Folge ist.
  2026.    Weiterhin ist zu beachten, daß alle Operationen des WatchDog den
  2027.    Kernel-Stack benutzen, der recht klein bemessen ist. Außerdem
  2028.    findet keine Unterbrechung des WatchDog durch den Scheduler
  2029.    statt und die verbrauchte CPU-Zeit wird keiner Task berechnet.
  2030.    Es empfiehlt sich also, WatchDog-Prozeduren sehr klein zu halten
  2031.    und nur dort einzusetzen, wo keine Subsystem-Aufrufe benötigt
  2032.    werden.
  2033.    Innerhalb des Subsystems wird diese Timer-Art dazu verwendet,
  2034.    einen schlafenden Prozeß nach Ablauf seiner Wartezeit wieder in
  2035.    seine Scheduler-Queue einzureihen. In dem bereits erwähnten
  2036.    Betriebssystem MINIX, wird ein WatchDog Timer z. B. dafür ein-
  2037.    gesetzt, um den Motor des Diskettenlaufwerkes auszuschalten, wenn
  2038.    seit dem letzten Zugriff eine bestimmte Zeitspanne verstrichen
  2039.    ist.
  2040.  
  2041. Siehe auch
  2042.  
  2043.    Typdefinition WatchDogType, CancelWatchDog, BindCPU, ReleaseCPU
  2044. .EE
  2045. ──────────────────────────────────────────────────────────────────
  2046. .RE ReadySuspended
  2047. Deklaration
  2048.  
  2049.    FUNCTION ReadySuspended(Task:TaskNoType):BOOLEAN; 
  2050.  
  2051. Funktion
  2052.  
  2053.    Versetze eine Task, die mittels Suspend aus der Scheduler-Queue 
  2054.    entfernt wurde, wieder in den Ready-Zustand. Sie wird wieder an 
  2055.    die Scheduler-Queue angehängt.
  2056.    Ein Funktionswert von TRUE, zeigt Erfolg an, ein Wert von FALSE 
  2057.    signalisiert, daß die angesprochene Task nicht den erforderlichen 
  2058.    Status hatte, d. h. nicht suspendiert war.
  2059.  
  2060. Siehe auch 
  2061.  
  2062.    Suspend
  2063. .EE
  2064. ──────────────────────────────────────────────────────────────────
  2065. .RE Receive
  2066. Deklaration
  2067.  
  2068.    FUNCTION Receive(FromTask:TaskNoType; MsgBuff:Pointer; 
  2069.    WaitFlag:WaitFlagType):TaskReturn; 
  2070.  
  2071. Funktion
  2072.  
  2073.    Die Funktion Receive ermöglicht es einer Task, eine Nachricht von 
  2074.    einer anderen Task zu empfangen. Hierbei enthält der Parameter 
  2075.    "FromTask" die Task-Nummer des gewünschten Kommunikationspar-
  2076.    tners, oder "AnyTask", wenn der Sender der Nachricht ohne Belang 
  2077.    ist. Wann immer eine gezielte Task-Nummer als Kommunikationspart-
  2078.    ner angegeben wird, bleiben Nachrichten, die von anderen Tasks 
  2079.    gesendet werden unberücksichtigt. 
  2080.    Der Parameter "MsgBuff" enthält die Anfangsadresse des Puffers, 
  2081.    in den die empfangene Nachricht hineinkopiert wird. Hierbei wird 
  2082.    KEINE Längenprüfung durchgeführt, d. h. es obliegt dem Program-
  2083.    mierer, sicherzustellen, daß der Pufferplatz ausreichend groß 
  2084.    gewählt wird. 
  2085.    Der Parameter "WaitFlag" entscheidet darüber, ob die empfangende 
  2086.    Task blockiert wird, falls derzeit keine Nachricht ansteht, oder 
  2087.    ob sie augenblicklich einen entsprechenden Returncode liefert.
  2088.    Der Wert "Wait" bewirkt eine Blockade; "NoWait" veranlaßt 
  2089.    sofortige Rückkehr.
  2090.  
  2091. Beispiel
  2092.  
  2093.    VAR Puffer : String;                        {Nachrichtenpuffer} 
  2094.                ...
  2095.    IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok 
  2096.       THEN Error 
  2097.       ELSE Writeln(Puffer); 
  2098.                ...
  2099.  
  2100. Anmerkungen 
  2101.  
  2102.    Mittels der Konstante "AnyTask", kann eine Task realisiert 
  2103.    werden, die ihr gesamtes Leben damit verbringt, auf Nachrichten 
  2104.    von der Außenwelt zu lauschen und diese ggf. zu verarbeiten. Von 
  2105.    wem sie gerade eine Nachricht erhalten hat, kann sie durch Aufruf 
  2106.    von ReceivedFrom ermitteln, um dem Sender auch eine Antwort 
  2107.    zukommen zu lassen.
  2108.    Ein denkbarer Anwendungsfall wäre die Meßwerterfassung mittels 
  2109.    mehrerer Meßfühler. Letztere werden durch jeweils eine eigene 
  2110.    Task überwacht, die periodisch an die zentrale Verarbeitung eine 
  2111.    Nachricht versendet. 
  2112.    
  2113. Siehe auch 
  2114.  
  2115.    Konstante AnyTask, Typdefinition TaskReturn, Send, ReceivedFrom
  2116. .EE
  2117. ──────────────────────────────────────────────────────────────────
  2118. .RE ReceivedFrom
  2119. Deklaration
  2120.  
  2121.    FUNCTION ReceivedFrom:TaskNoType; 
  2122.  
  2123. Funktion
  2124.  
  2125.    Diese Funktion ermittelt den Sender der zuletzt empfangenen 
  2126.    Nachricht. Dies kann u. U. von Interesse sein, wenn eine Task 
  2127.    ständig Nachrichten von ihrer Umwelt entgegennimmt, ungeachtet 
  2128.    der Task-Nummern der Sender (AnyTask). 
  2129.    Für die Reaktion auf die Nachrichten, insbesondere wenn eine 
  2130.    Nachricht eine Antwort-Nachricht an den Sender erfordert, muß nun 
  2131.    die Task-Nummer des Senders ermittelt werden können. 
  2132.    
  2133. Siehe auch
  2134.  
  2135.    Typdefinition TaskNoType, Receive
  2136. .EE
  2137. ──────────────────────────────────────────────────────────────────
  2138. .RE ReleaseCPU
  2139. Deklaration
  2140.  
  2141.    PROCEDURE ReleaseCPU; 
  2142.  
  2143. Funktion
  2144.  
  2145.    Diese Prozedur macht eine CPU-Blockade durch BindCPU wieder 
  2146.    rückgängig. 
  2147.  
  2148. Beispiel 
  2149.  
  2150.       ... 
  2151.    BindCPU;                                    {CPU blockieren}
  2152.    GotoXY(1,10);                               {untrennbare Aktion}
  2153.    Write('*'); 
  2154.    ReleaseCPU;                                 {CPU freigeben}
  2155.       ...
  2156.  
  2157. Siehe auch
  2158.  
  2159.    BindCPU;
  2160. .EE
  2161. ──────────────────────────────────────────────────────────────────
  2162. .RE ReleaseRsc
  2163. Deklaration
  2164.  
  2165.    FUNCTION  ReleaseRsc(RscPtr:Pointer):RscReturn;
  2166.  
  2167. Funktion
  2168.  
  2169.    Freigabe einer zuvor mittels RequestRsc belegten Resource.
  2170.    Ist der Aufrufer dieser Funktion nicht der Eigentümer der
  2171.    Resource, so wird der Returncode Rsc_NotOwner zurückgelie-
  2172.    fert; weitere Aktionen erfolgen nicht.
  2173.  
  2174. Beispiel
  2175.  
  2176.    VAR Resource : Pointer;
  2177.  
  2178.    IF CreateRsc(Resource) <> Rsc_Ok
  2179.       THEN Error;
  2180.         .........
  2181.    IF RequestRsc(Resource,Wait) <> Rsc_Ok  {Resource anfordern}
  2182.       THEN Error;
  2183.         .........
  2184.    { kritischer Bereich }
  2185.         .........
  2186.    IF ReleaseRsc(Resource) <> Rsc_Ok       {Resource freigeben}
  2187.       THEN Error;
  2188.  
  2189. Siehe auch
  2190.  
  2191.    Typdefinition RscReturn, RequestRsc, GrantRsc
  2192. .EE
  2193. ──────────────────────────────────────────────────────────────────
  2194. .RE RemoveRsc
  2195. Deklaration
  2196.  
  2197.    FUNCTION  RemoveRsc(RscPtr:Pointer):RscReturn;
  2198.  
  2199. Funktion
  2200.  
  2201.    Entfernen einer Resource aus dem System und Freigabe des von
  2202.    ihr belegten dynamischen Speichers.
  2203.    Eine Resource kann nur dann gelöscht werden, wenn sie
  2204.    aktuell keiner Task gehört. Ist dies nicht der Fall, so wird
  2205.    der Returncode Rsc_NotFree geliefert und die Resource bleibt
  2206.    unverändert.
  2207.  
  2208. Beispiel
  2209.  
  2210.    VAR Resource : Pointer;
  2211.  
  2212.    IF CreateRsc(Resource) <> Rsc_Ok
  2213.       THEN Error;
  2214.    LotsOfUsefulStuff;
  2215.    IF RemoveRsc(Resource) <> Rsc_Ok
  2216.       THEN Error;
  2217.  
  2218. Siehe auch
  2219.  
  2220.    Typdefinition RscReturn, CreateRsc, RemoveRscKill
  2221. .EE
  2222. ──────────────────────────────────────────────────────────────────
  2223. .RE RemoveRscKill
  2224. Deklaration
  2225.  
  2226.    FUNCTION  RemoveRscKill(RscPtr:Pointer):RscReturn;
  2227.  
  2228. Funktion
  2229.  
  2230.    Entfernen einer Resource aus dem System und Freigabe des von
  2231.    ihr belegten dynamischen Speichers.
  2232.    Alle noch auf diese Resource wartenden Tasks werden aus dem
  2233.    System entfernt. Dies gilt auch für den Eigentümer sofern er
  2234.    nicht selbst der Aufrufer von RemoveRscKill ist.
  2235.  
  2236. Beispiel
  2237.  
  2238.    VAR Resource : Pointer;
  2239.  
  2240.    IF CreateRsc(Resource) <> Rsc_Ok
  2241.       THEN Error;
  2242.    LotsOfUsefulStuff;
  2243.    IF RemoveRscKill(Resource) <> Rsc_Ok
  2244.       THEN Error;
  2245.  
  2246. Siehe auch
  2247.  
  2248.    Typdefinition RscReturn, CreateRsc, RemoveRsc
  2249. .EE
  2250. ──────────────────────────────────────────────────────────────────
  2251. .RE RemoveSem
  2252. Deklaration
  2253.  
  2254.    FUNCTION RemoveSem(VAR SemPtr:Pointer):SemReturn; 
  2255.  
  2256. Funktion
  2257.  
  2258.    Physisches Entfernen einer Semaphore aus dem System; der belegte 
  2259.    HeapSpeicher wird wieder freigegeben. 
  2260.    Der Parameter "SemPtr" entspricht dem Handle, das von CreateSem 
  2261.    zurückgeliefert wurde. Der Returncode zeigt Erfolg oder Mißerfolg
  2262.    der Aktion an. 
  2263.  
  2264. Beispiel
  2265.  
  2266.    VAR  Semaphore : Pointer;                   {Handle} 
  2267.                ...
  2268.    IF CreateSem(Semaphore) <> Sem_Ok           {Semaphore erzeugen}
  2269.       THEN Error; 
  2270.             ...
  2271.    IF RemoveSem(Semaphore) <> Sem_Ok           {und wieder löschen}
  2272.       THEN Error; 
  2273.             ...
  2274.  
  2275. Anmerkungen
  2276.  
  2277.    Bitte beachten Sie, daß es nicht immer möglich ist, die Gültig-
  2278.    keit des übergebenen Pointers zu überprüfen. Es obliegt also dem
  2279.    Programmierer sicherzustellen, daß ein Zeiger auf eine aktive 
  2280.    Semaphore im Funktionsaufruf angegeben wird. 
  2281.    Eine Semaphore, deren Warteschlange noch Tasks enthält, kann 
  2282.    nicht gelöscht werden. 
  2283.    
  2284. Siehe auch
  2285.  
  2286.    Typdefinition SemReturn, CreateSem
  2287. .EE
  2288. ──────────────────────────────────────────────────────────────────
  2289. .RE RemoveSemKill
  2290. Deklaration
  2291.  
  2292.    FUNCTION RemoveSemKill(VAR SemPtr:Pointer):SemReturn;
  2293.  
  2294. Funktion
  2295.  
  2296.    Entfernen einer Semaphore aus dem System.
  2297.    Diese Funktion bedient sich RemoveSem, entfernt jedoch evtl. wartende
  2298.    Prozesse aus dem System.
  2299.  
  2300. Anmerkungen
  2301.  
  2302.    Wenngleich diese Funktion die meiste Zeit über die CPU blockiert,
  2303.    so kann bei heftigem Zugriff auf die zu entfernende Semaphore u. U.
  2304.    dennoch der Fehler Sem_NotFree zurückgeliefert werden. Darauf kann
  2305.    der Aufrufer allerdings mit wiederholtem Aufruf von RemoveSemKill
  2306.    reagieren.
  2307.  
  2308. Siehe auch
  2309.  
  2310.    Typdefinition SemReturn, RemoveSem
  2311. .EE
  2312. ──────────────────────────────────────────────────────────────────
  2313. .RE RequestRsc
  2314. Deklaration
  2315.  
  2316.    FUNCTION  RequestRsc(RscPtr:Pointer,
  2317.                         WaitFlag:WaitFlagType):RscReturn;
  2318.  
  2319. Funktion
  2320.  
  2321.    Anfordern einer Resource. Ist als Waitflag der Wert "Wait"
  2322.    angegeben, so blockiert der Aufrufer solange, bis die
  2323.    Resource verfügbar ist. Anderenfalls wird der Returncode
  2324.    Rsc_NotFree geliefert und die Verarbeitung fortgesetzt.
  2325.  
  2326. Beispiel
  2327.  
  2328.    VAR Resource : Pointer;
  2329.  
  2330.    IF CreateRsc(Resource) <> Rsc_Ok
  2331.       THEN Error;
  2332.         .........
  2333.    IF RequestRsc(Resource,Wait) <> Rsc_Ok  {Resource anfordern}
  2334.       THEN Error;
  2335.         .........
  2336.    { kritischer Bereich }
  2337.         .........
  2338.    IF ReleaseRsc(Resource) <> Rsc_Ok       {Resource freigeben}
  2339.       THEN Error;
  2340.  
  2341. Siehe auch
  2342.  
  2343.    Typdefinitionen RscReturn und WaitFlagType, RequestRsc, GrantRsc
  2344. .EE
  2345. ──────────────────────────────────────────────────────────────────
  2346. .RE Sched
  2347. Deklaration
  2348.  
  2349.    PROCEDURE Sched; 
  2350.  
  2351. Funktion
  2352.  
  2353.    Bedingungslose Aufgabe der Zeitscheibe durch sofortigen Taskwech-
  2354.    sel. Die aufrufende Task wird vom Zustand "Running" in den 
  2355.    Zustand "Ready" versetzt. 
  2356.    Sched dient vornehmlich zu internen Zwecken; in der praktischen 
  2357.    Anwendung sollte zur Aufgabe der Zeitscheibe lieber Sleep(1) 
  2358.    verwendet werden.
  2359. .EE
  2360. ──────────────────────────────────────────────────────────────────
  2361. .RE Seconds
  2362. Deklaration
  2363.  
  2364.    FUNCTION Seconds(Sec:Word):LongInt; 
  2365.  
  2366. Funktion
  2367.  
  2368.    Die Funktion Seconds liefert als Funktionswert die Anzahl Timer-
  2369.    Ticks, die die gewünschte Anzahl von Sekunden ausmachen. Dies hat
  2370.    den Vorteil, daß die Zeitscheibengröße verändert werden kann,
  2371.    ohne daß Ihre Programme davon beeinflußt werden.
  2372.  
  2373. Beispiel
  2374.  
  2375.    IF QueueTimer(@Timeout,Seconds(1) SHR 1)    { Timer auf eine }
  2376.       THEN;                                    { halbe Sekunde  }
  2377.  
  2378. Siehe auch
  2379.  
  2380.    QueueTimer, CancelTimer
  2381. .EE
  2382. ──────────────────────────────────────────────────────────────────
  2383. .RE SemClear
  2384. Deklaration
  2385.  
  2386.    PROCEDURE SemClear(SemPtr:Pointer); 
  2387.  
  2388. Funktion
  2389.  
  2390.    Setze den Signal-Count der angesprochenen Semaphore auf Null. 
  2391.  
  2392. Beispiel
  2393.  
  2394.    VAR  Semaphore : Pointer;                   {Handle} 
  2395.                ...
  2396.    IF CreateSem(Semaphore) <> Sem_Ok           {Semaphore erzeugen}
  2397.       THEN Error
  2398.       ELSE SemClear(Semaphore);                {und löschen}
  2399.  
  2400. Anmerkungen
  2401.  
  2402.    Der Aufruf von SemClear wird intern als SemSet(Semaphore,0) 
  2403.    realisiert.
  2404.  
  2405. Siehe auch
  2406.  
  2407.    SemSignal, SemWait, SemClearWait, SemSet
  2408. .EE
  2409. ──────────────────────────────────────────────────────────────────
  2410. .RE SemClearWait
  2411. Deklaration
  2412.  
  2413.    PROCEDURE SemClearWait(SemPtr:Pointer); 
  2414.  
  2415. Funktion
  2416.  
  2417.    Setze den Signal-Count der angesprochenen Semaphore auf Null und 
  2418.    warte, bis er durch einen SemSignal-Aufruf einer anderen Task 
  2419.    erhöht wird. Dieser Aufruf führt im Gegensatz zu SemWait IMMER zu 
  2420.    einem Blockieren der aufrufenden Task. 
  2421.  
  2422. Beispiel
  2423.  
  2424.    VAR Ready : Pointer;                        {Semaphore      }
  2425.          ...
  2426.    PROCEDURE SubTask; 
  2427.    {
  2428.       Diese Task muß sich während ihrer Initialisierung Werte
  2429.       aus globalen Variablen merken, die dort vom Hauptprogramm
  2430.       für sie abgelegt wurden, jedoch im weiteren Verlauf des
  2431.       Hauptprogrammes verändert werden.
  2432.       Aus diesem Grunde darf das Hauptprogramm erst dann weiter
  2433.       ausgeführt werden, wenn es sicher sein kann, daß die Sub-
  2434.       task die Werte entnommen hat.
  2435.    }
  2436.    BEGIN {SubTask}
  2437.          ...                                   {Initialisierung}
  2438.       SemSignal(Ready);                        {O.K. bin soweit}
  2439.       REPEAT
  2440.          ...                                   {Taskrumpf      }
  2441.       UNTIL False;
  2442.    END;  {SubTask}                                           
  2443.          ...                    
  2444.          ...                    
  2445.    BEGIN {Main}
  2446.          ...
  2447.    IF CreateSem(Ready) <> Sem_Ok               {Semaphore erzeugen}
  2448.       THEN Error;
  2449.    IF CreateTask(@SubTask,Pri_User,300) < 0    {Task erzeugen     }
  2450.       THEN Error
  2451.       ELSE SemClearWait(Ready);                {warte auf Init.   }
  2452.          ...                    
  2453.    END.  {Main}
  2454.  
  2455. Siehe auch
  2456.  
  2457.    SemClear, SemSet, SemSignal, SemWait
  2458. .EE
  2459. ──────────────────────────────────────────────────────────────────
  2460. .RE SemCut
  2461. Deklaration
  2462.  
  2463.    FUNCTION SemCut(SemPtr:Pointer;Task:TaskNoType):BOOLEAN; 
  2464.  
  2465. Funktion
  2466.  
  2467.    Prüfe, ob die angegebene Task sich in der Warteschlange der 
  2468.    Semaphore befindet und entferne sie ggf. aus dieser. Der Funk-
  2469.    tionswert TRUE zeigt Erfolg an, der Wert FALSE signalisiert 
  2470.    Mißerfolg. 
  2471.    
  2472. Anmerkungen
  2473.  
  2474.    Der Zustand der Task wird von dieser Aktion in KEINER WEISE 
  2475.    verändert. U. a. bedeutet dies auch, daß sie in keine andere 
  2476.    Warteschlange eingereiht wird und demzufolge solgange "herrenlos" 
  2477.    im System herumliegt, bis sie durch SemPaste() (und nur durch 
  2478.    SemPaste) wieder in die Warteschlange einer Semaphore eingefügt 
  2479.    wird. Diese Funktion ist ausschließlich für einen speziellen 
  2480.    Anwendungsfall in der Unit MtPopUp hinzugefügt worden und sollte 
  2481.    nur mit äußerster Vorsicht angewandt werden. 
  2482.  
  2483. Siehe auch
  2484.  
  2485.    SemPaste
  2486. .EE
  2487. ──────────────────────────────────────────────────────────────────
  2488. .RE SemDiag
  2489. Deklaration
  2490.  
  2491.    PROCEDURE SemDiag(SemPtr:Pointer; Txt:String);
  2492.  
  2493. Funktion
  2494.  
  2495.    Ausgabe des Zustands der angegebenen Semaphore. Es werden der
  2496.    Signal-Count sowie die Nummern der in der Warteschlagen
  2497.    befindlichen Tasks auf dem Bildschirm ausgegeben.
  2498.    Der als "Txt" übergebene String wird in der Überschrift der
  2499.    Diagnoseausgabe angezeigt. Der aktuelle Bildschirminhalt wird
  2500.    dabei überschrieben.
  2501.  
  2502. Beispiel
  2503.  
  2504.    Beispielhafte Ausgabe eines Aufrufes von SemDiag:
  2505.  
  2506.  
  2507.    SemDiag(MySem,"Meine Semaphore");
  2508.  
  2509.    -------------[Meine Semaphore]---------------
  2510.    Signals: 0
  2511.    WaitQueue: 4, 6
  2512. .EE
  2513. ──────────────────────────────────────────────────────────────────
  2514. .RE SemGetSignals
  2515. Deklaration
  2516.  
  2517.    FUNCTION SemGetSignals(SemPtr:Pointer):Word; 
  2518.  
  2519. Funktion
  2520.  
  2521.    Ermittle den Signal-Count der angesprochenen Semaphore. 
  2522.  
  2523. Beispiel
  2524.  
  2525.    FUNCTION SemBusy(S:Pointer):BOOLEAN;
  2526.    {
  2527.       Diese Funktion prüft, ob eine Semaphore gerade belegt ist.
  2528.       Ein Funktionswert von TRUE bedeutet "belegt".
  2529.    }
  2530.    BEGIN {SemBusy}
  2531.       SemBusy := (SemGetSignals(S) = 0);       {Signal-Count = 0}
  2532.    END;  {SemBusy}
  2533.  
  2534. Siehe auch
  2535.  
  2536.    SemSignal, SemWait, SemClear, SemClearWait, SemSet
  2537. .EE
  2538. ──────────────────────────────────────────────────────────────────
  2539. .RE SemPaste
  2540. Deklaration
  2541.  
  2542.    PROCEDURE SemPaste(SemPtr:Pointer; Task:TaskNoType); 
  2543.  
  2544. Funktion
  2545.  
  2546.    Die Task mit der angegebenen Nummer wird bedingungslos an die 
  2547.    Warteschlange der Semaphore angehängt!!!  Es findet keine Prüfung 
  2548.    der Task-Nummer auf Gültigkeit statt. 
  2549.  
  2550. Anmerkungen
  2551.  
  2552.    Der Zustand der Task wird durch diesen Vorgang in keiner Weise 
  2553.    verändert. Aus diesem Grunde sollte SemPaste() NUR nach einem 
  2554.    vorhergehenden Aufruf von SemCut ausgeführt werden. Diese Funk-
  2555.    tion ist ausschließlich für einen speziellen Anwendungsfall 
  2556.    innerhalb der Unit MtPopUp hinzugefügt worden und sollte nur mit 
  2557.    äußerster Vorsicht angewandt werden.
  2558. .EE
  2559. ──────────────────────────────────────────────────────────────────
  2560. .RE SemSet
  2561. Deklaration
  2562.  
  2563.    PROCEDURE SemSet(SemPtr:Pointer; Count:Word); 
  2564.  
  2565. Funktion
  2566.  
  2567.    Setze den Signal-Count der angesprochenen Semaphore bedingungslos 
  2568.    auf den übergebenen Wert. Evtl. in der Warteschlange befindliche 
  2569.    Tasks werden durch diese Aktion NICHT freigegeben. 
  2570.  
  2571. Beispiel
  2572.                        
  2573.    CONST Elements = 100; 
  2574.    VAR   Buffer   : ARRAY[1..Elements] OF Byte; {Puffer}
  2575.          Full     : Pointer;                   {Anzahl voller Slots}
  2576.          Empty    : Pointer;                   {Anzahl leerer Slots}
  2577.          ...
  2578.    BEGIN {Main}
  2579.          ...
  2580.       IF CreateSem(Full) = Sem_Ok 
  2581.          THEN SemClear(Full);                  {nix voll}
  2582.       IF CreateSem(Empty) = Sem_Ok 
  2583.          THEN SemSet(Empty,Elements);          {alles leer}
  2584.          ...
  2585.    END.  {Main}
  2586.  
  2587. Siehe auch
  2588.  
  2589.    SemSignal, SemWait, SemClear, SemClearWait
  2590. .EE
  2591. ──────────────────────────────────────────────────────────────────
  2592. .RE SemSignal
  2593. Deklaration
  2594.  
  2595.    PROCEDURE SemSignal(SemPtr:Pointer); 
  2596.  
  2597. Funktion
  2598.  
  2599.    Erhöhe den Signal-Count der angesprochenen Semaphore, falls die 
  2600.    Warteschlange leer ist. Anderenfalls reaktiviere die erste Task 
  2601.    der Warteschlange und lasse den Signal-Count unangetastet. 
  2602.  
  2603. Beispiel
  2604.  
  2605.    VAR Critical: Pointer; 
  2606.          ...
  2607.    IF SemCreate(Critical) <> Sem_Ok 
  2608.    THEN Error; 
  2609.          ...
  2610.    SemWait(Critical); 
  2611.          ...
  2612.    {kritischer Bereich} 
  2613.          ...
  2614.    SemSignal(Critical); 
  2615.          ...
  2616.  
  2617. Siehe auch
  2618.  
  2619.    SemWait, SemClearWait, SemClear, SemSet
  2620. .EE
  2621. ──────────────────────────────────────────────────────────────────
  2622. .RE SemSoWaiting
  2623. Deklaration
  2624.  
  2625.    FUNCTION SemSoWaiting(SemPtr:Pointer):BOOLEAN; 
  2626.  
  2627. Funktion
  2628.  
  2629.    Diese Funktion liefert den Wert TRUE, falls sich Tasks in der 
  2630.    Warteschlange der angesprochenenen Semaphore befinden. Ist die 
  2631.    Warteschlange leer, so wird FALSE geliefert. 
  2632.    
  2633. Siehe auch
  2634.  
  2635.    SemWait, SemSignal, SemClear, SemClearWait, SemSet, SemGetSignals
  2636. .EE
  2637. ──────────────────────────────────────────────────────────────────
  2638. .RE SemWait
  2639. Deklaration
  2640.  
  2641.    PROCEDURE SemWait(SemPtr:Pointer); 
  2642.  
  2643. Funktion
  2644.  
  2645.    Erniedrige den Signal-Count der angesprochenen Semaphore um 1, 
  2646.    falls dieser derzeit größer als Null ist. Ist dies nicht der 
  2647.    Fall, so wird die aufrufende Task solange blockiert, bis eine 
  2648.    andere Task einen SemSignal-Aufruf auf dieselbe Semaphore ab-
  2649.    setzt. 
  2650.  
  2651. Beispiel
  2652.  
  2653.    VAR Critical: Pointer; 
  2654.          ...
  2655.    IF SemCreate(Critical) <> Sem_Ok 
  2656.    THEN Error; 
  2657.          ...
  2658.    SemWait(Critical); 
  2659.          ...
  2660.    {kritischer Bereich} 
  2661.          ...
  2662.    SemSignal(Critical); 
  2663.          ...
  2664.  
  2665. Siehe auch
  2666.  
  2667.    SemSignal, SemClearWait, SemClear, SemSet
  2668. .EE
  2669. ──────────────────────────────────────────────────────────────────
  2670. .RE Send
  2671. Deklaration
  2672.  
  2673.    FUNCTION Send(ToTask:TaskNoType; Msg:Pointer; MsgSize:WORD; 
  2674.             WaitFlag:WaitFlagType):TaskReturn; 
  2675.  
  2676. Funktion
  2677.  
  2678.    Die Funktion Send ermöglicht es einer Task, eine Nachricht an 
  2679.    eine andere Task zu senden. 
  2680.    Hierbei enthält der Parameter "To-Task" die Task-Nummer des 
  2681.    gewünschten Kommunikationspartners. Dies muß in jedem Fall die 
  2682.    Nummer einer aktiven Task sein, "AnyTask" ist hier NICHT zulässig. 
  2683.    Der Parameter "Msg" enthält die Anfangsadresse eines Puffers, in 
  2684.    dem sich die zu sendende Nachricht in der Länge "MsgSize"-Bytes 
  2685.    befindet. 
  2686.    Der Parameter "WaitFlag" entscheidet darüber, ob die sendende 
  2687.    Task blockiert wird, falls der Kommunikationspartner derzeit 
  2688.    nicht zu sprechen ist, oder ob sie augenblicklich mit einem 
  2689.    entsprechenden Returncode zurückkehrt.
  2690.  
  2691. Beispiel
  2692.  
  2693.    VAR Puffer : String; 
  2694.          ...
  2695.    IF Receive(AnyTask,@Puffer,Wait) <> Task_Ok 
  2696.       THEN Error 
  2697.       ELSE Writeln(Puffer); 
  2698.          ...
  2699.  
  2700. Anmerkungen
  2701.  
  2702.    Die Nachricht wird physisch in den Empfangspuffer kopiert. Vom 
  2703.    Standpunkt der Performance her betrachet, ist dies sicherlich 
  2704.    nicht die günstigste Lösung, jedoch wird so die größtmögliche 
  2705.    Unabhängigkeit zwischen Sender und Empfänger erreicht.
  2706.  
  2707. Siehe auch 
  2708.  
  2709.    Typdefinition TaskReturn, Receive
  2710. .EE
  2711. ──────────────────────────────────────────────────────────────────
  2712. .RE SetPri
  2713. Deklaration
  2714.  
  2715.    PROCEDURE SetPri(Pri:Priority);
  2716.  
  2717. Funktion
  2718.  
  2719.    Verändern der Priorität der aufrufenden Task zur Laufzeit. 
  2720.  
  2721. Anmerkungen
  2722.  
  2723.    Die Task wird umgehend ihrer neuen Prioritätsebene zugeordnet, 
  2724.    jedoch behält sie bis zum Ablauf der aktuellen Zeitscheibe die 
  2725.    Kontrolle, selbst wenn sie sich in ihrer Priorität herabgesetzt
  2726.    hat.   
  2727.  
  2728. Siehe auch 
  2729.  
  2730.    Typdefinition Priority
  2731. .EE
  2732. ──────────────────────────────────────────────────────────────────
  2733. .RE Sleep
  2734. Deklaration 
  2735.  
  2736.    PROCEDURE Sleep(Ticks:LongInt); 
  2737.  
  2738. Funktion
  2739.  
  2740.    Mit Hilfe von Sleep kann sich eine Task um "Ticks" Timer-Ticks 
  2741.    suspendieren. Nach Ablauf dieser Wartezeit wird sie vom Subsystem 
  2742.    automatisch wieder in den Ready-Zustand versetzt. 
  2743.  
  2744. Beispiel 
  2745.  
  2746.    Writeln('Ich schlafe jetzt 3 Sekunden!'); 
  2747.    Sleep(Seconds(3)); 
  2748.    Writeln('Gähn... Ich bin wieder wach!'); 
  2749.  
  2750. Siehe auch
  2751.  
  2752.    Seconds
  2753. .EE
  2754. ──────────────────────────────────────────────────────────────────
  2755. .RE SpeedUp
  2756. Deklaration
  2757.  
  2758.    PROCEDURE SpeedUp(Factor:Word);
  2759.  
  2760. Funktion
  2761.  
  2762.    Die Prozedur "SpeedUp" erlaubt es, einen Teiler für die Standard-
  2763.    Interruptfrequenz von ca. 55,5ms zu definieren. Dadurch wird die
  2764.    Taskwechselrate um den entsprechenden Faktor erhöht.
  2765.    Ein SpeedUp-Faktor von 5 ergibt demnach eine minimale
  2766.    Zeitscheibengröße von ca. 11,1ms, d. h. rund 90 Task-Wechsel pro
  2767.    Sekunde.
  2768.  
  2769. Anmerkungen
  2770.  
  2771.    Eine Erhöhung der Taskwechelrate bringt in der Regel nur dann
  2772.    Vorteile, wenn zeitkritische Abfragen oder Ereignisse zu
  2773.    bearbeiten sind. Da der Task-Switch zu den zeitaufwendigsten
  2774.    Systemoperationen gehört, wächst auch der System-Overhead mit
  2775.    steigender Interruptfrequenz.
  2776.    Die PC Systemuhr wird von der Erhöhung der Interruptrate nicht
  2777.    beeinflußt; sie wird nach wie vor nur 18 Mal in der Sekunde
  2778.    weitergezählt.
  2779. .EE
  2780. ──────────────────────────────────────────────────────────────────
  2781. .RE Suspend
  2782. Deklaration 
  2783.  
  2784.    FUNCTION Suspend(Task:TaskNoType):BOOLEAN; 
  2785.  
  2786. Funktion
  2787.  
  2788.    Suspendiere eine Task, die sich derzeit im Zustand Ready befindet 
  2789.    auf unbegrenzte Zeit. 
  2790.    "Task" enthält hierbei die Task-Nummer der zu suspendierenden 
  2791.    Task. Eine durch Suspend deaktivierte Task kann AUSSCHLIESSLICH 
  2792.    durch einen Aufruf von ReadySuspended wieder in den Ready-Zustand 
  2793.    versetzt werden. 
  2794.    Befindet sich die angesprochene Task nicht im Zustand Ready, so 
  2795.    wird ein Funktionswert von FALSE geliefert; andernfalls TRUE. 
  2796.    Eine Task kann sich NIEMALS selbst suspendieren!!! 
  2797.  
  2798. Siehe auch 
  2799.  
  2800.    Typdefinition TaskNoType, ReadySuspended
  2801. .EE
  2802. ──────────────────────────────────────────────────────────────────
  2803. .RE Terminate
  2804. Deklaration
  2805.  
  2806.    PROCEDURE Terminate; 
  2807.  
  2808. Funktion
  2809.  
  2810.    Durch den Aufruf von Terminate beendet sich eine Task selbst. Der 
  2811.    belegte Task-Table-Eintrag wird freigegeben; ebenso der private 
  2812.    Stack-Space. 
  2813.  
  2814. Beispiel
  2815.    
  2816.    VAR  Semaphore : Pointer;                   {Handle} 
  2817.                ...
  2818.    IF CreateSem(Semaphore) <> Sem_Ok           {Semaphore erzeugen}
  2819.       THEN BEGIN
  2820.              Error;                            {Fehlermeldung}
  2821.              Terminate;                        {Task beenden}
  2822.            END;
  2823.  
  2824. Siehe auch
  2825.  
  2826.    CreateTask
  2827. .EE
  2828. ──────────────────────────────────────────────────────────────────
  2829. .RE TimeSlice
  2830. Deklaration 
  2831.  
  2832.    PROCEDURE TimeSlice(Slice, DynQ:Byte); 
  2833.  
  2834. Funktion
  2835.  
  2836.    TimeSlice dient zur Veränderung der Zeitscheibengröße sowie der 
  2837.    Genze für die dynamische Zeitscheibensteuerung. 
  2838.    Der Parameter "Slice" gibt die breite einer Zeitscheibe in Ticks 
  2839.    an; der Parameter "DynQ" enthält den +/- Grenzwert für die 
  2840.    dynamische Zeitscheibensteuerung ebenfalls in Ticks. 
  2841.  
  2842. Beispiel
  2843.  
  2844.    { 
  2845.       Einstellen der minimalen Zeitscheibengröße von 55,5 
  2846.       Millisekunden bei eine Begrenzung der Dynamik auf 1 Sekunde 
  2847.    }
  2848.    TimeSlice(1,Seconds(1));
  2849.  
  2850. Anmerkungen
  2851.  
  2852.    Vom System werden beim Start eine Zeitscheibenbreite von 2 Ticks
  2853.    à 55,5 ms und ein +/- Wert von 10 Ticks voreingestellt.
  2854. .SE CPMULTI / Programmierhinweise
  2855. 7. Programmierhinweise
  2856.  
  2857. Tastaturbehandlung
  2858.  
  2859. Verwenden Sie niemals die Standard-Funktionen Read/ReadLn/ReadKey
  2860. für die Dateneingabe. Durch den direkten Aufruf der Funktion 0 des
  2861. Interrupt 16H (der als nicht reentrant behandelt wird), werden alle
  2862. Task-Wechsel bis zum nächsten Tastendruck verhindert.
  2863. Eine Abfrageschleife (mit Keypressed) um ReadKey herum, behebt
  2864. dieses Problem (siehe GlassTty, Pro_Con,...), verbraucht allerdings
  2865. unnötig Rechnerzeit.
  2866.  
  2867. Abhilfe
  2868.  
  2869. Ist Tastatureingabe erforderlich, so binden Sie IMMER die Unit
  2870. MTPopUp (nur registrierte Benutzer) ein.
  2871. Diese sorgt dafür, daß eine Task, die auf Tastatureingaben wartet,
  2872. bis zum Eintreffen von Daten suspendiert wird. - Die durch eine
  2873. Abfrageschleife verbrauchte Rechnerzeit steht dadurch den übrigen
  2874. Tasks zur Verfügung!
  2875.  
  2876.  
  2877. Plattenzugriffe
  2878.  
  2879. Konzentrieren Sie Plattenzugriffe auf eine einzelne Task in Ihrer
  2880. Anwendung.
  2881.  
  2882.  
  2883. Overlays
  2884.  
  2885. Verwenden Sie NIEMALS Overlays in Programmen, die mit dem Multi-Tasking
  2886. Subsystem übersetzt wurden.
  2887.  
  2888. Beispielsituation
  2889.  
  2890. Es existieren zwei Units, die als Overlay geladen werden. Es kann
  2891. immer nur eine der beiden Units gleichzeitig im Speicher sein
  2892. (vereinfachte Darstellung).
  2893.  
  2894. OUnit1
  2895.  OProc11
  2896.  OProc12
  2897.  
  2898. OUnit2
  2899.  OProc21
  2900.  OProc22
  2901.  
  2902. Ferner werden die Routinen aus den beiden Overlays von zwei Tasks
  2903. (Task1, Task2) angesprochen.
  2904. Findet nun ein Task-Switch statt, während sich z. B. Task1 in der
  2905. Routine OProc11 des ersten Overlays befindet, so würde dieses Overlay
  2906. durch Aufruf von OProc22 innerhalb der Task2, die durch den
  2907. Task-Wechsel die Kontrolle erhält, überschrieben.
  2908. Im weitern Verlauf erhält nun wieder Task1 die Kontrolle, das
  2909. Subsystem rekonstruiert den Registersatz und setzt die
  2910. Programmausführung an der Adresse fort, an der es unterbrochen
  2911. wurde..... Nur, dort befindet sich mittlerweile nicht mehr der
  2912. Overlaybereich1, da dieser ja durch Nachladen des zweiten Overlays
  2913. überschrieben wurde!
  2914.  
  2915.  
  2916. Sonstiges
  2917.  
  2918. Soll der Kernel möglichst schnell auf Ereignisse reagieren könne, so
  2919. em- pfiehlt es sich, in Warteschleifen, in denen Funktionen aufgerufen
  2920. werden, die nicht durch einen Task-Switch unterbrochen werden dürfen,
  2921. einen Sleep(1) einzubauen, da hierdurch häufig die Zeitscheibe der
  2922. wartenden Task durch ein für eine höherpriorisierte Task bestimmtes
  2923. Ereignis abgebrochen werden kann.
  2924.  
  2925. Beispiel:  Nicht    REPEAT UNTIL Keypressed;
  2926.            Sondern  REPEAT Sleep(1); UNTIL Keypressed;
  2927. .SE Reihenfolge / Abhängigkeiten
  2928. 8. Reihenfolge der Einbindung und Abhängigkeiten der Units
  2929.  
  2930. Reihenfolge der Eintragung in ein USES-Statement
  2931.  
  2932. (Dos)
  2933. (Crt)
  2934. CpMulti
  2935. MtPopUp
  2936. CpMisc
  2937. Queue
  2938. MtPipe
  2939. MtPrint
  2940. V24
  2941. V24Pipe
  2942.  
  2943. Abhängigkeiten
  2944.  
  2945. CpMulti
  2946.   Dos
  2947.   Crt
  2948.  
  2949. MtPopUp
  2950.   Dos
  2951.   Crt
  2952.   CpMulti
  2953.  
  2954. CpMisc
  2955.   CpMulti
  2956.  
  2957. Queue
  2958.   CpMulti
  2959.   CpMisc
  2960.  
  2961. MtPipe
  2962.   Dos
  2963.   CpMulti
  2964.   CpMisc
  2965.   Queue
  2966.  
  2967. MtPrint
  2968.   Dos
  2969.   CpMulti
  2970.   MtPipe
  2971.  
  2972. V24
  2973.   Dos
  2974.  
  2975. V24Pipe
  2976.   Dos
  2977.   CpMulti
  2978.   MtPipe
  2979.   V24
  2980. .SE Literaturhinweise
  2981. a) Operating Systems, Design and Implementation 
  2982.    Andrew S. Tanenbaum 
  2983.    Verlag Prentice-Hall International Editions 
  2984.    ISBN 0-13-637331-3 
  2985.    719 Seiten, ca. 64,- DM
  2986.  
  2987.    Dieses Buch ist mittlerweile auch in einer deutschen
  2988.    Übersetzung beim Carl Hanser Verlag, München erhältlich.
  2989.  
  2990.  
  2991. b) Praxis des Multitasking 
  2992.    Zilker 
  2993.    Franzis Verlag 
  2994.    ISBN 3-7723-8561-3 
  2995.    112 Seiten, ca. 38,- DM
  2996. .SE Historie
  2997. Version 1.10 / August 1988
  2998.  
  2999. -  Fehler in der Ereigsnissteuerung behoben 
  3000. -  Erweiterung der Stackprüfung; jetzt wird auch erkannt, wenn einer 
  3001.    Task beim Create zuwenig Stackspace für ihre lokalen Variablen 
  3002.    zugewiesen wird. Dies führt ebenfalls zu einem Stack-Überlauf und 
  3003.    einem Markieren der Task als Crashed.
  3004. -  Erweiterung der Prüfungen beim Reaktivieren von Tasks, die sich 
  3005.    im Wartezustand befinden. Eine Task, die als Crashed markiert 
  3006.    ist, wird nicht mehr reaktiviert.
  3007. -  Neugliederung der Quelle
  3008. -  SemCut und SemPaste hinzugefügt
  3009. -  Überarbeitung der Dokumentation
  3010. .PA
  3011. Version 1.30 / November 1988
  3012.  
  3013. -  Umstellung der Task-Verwaltung auf doppelt verkettete Listen. 
  3014.    Dadurch Verkürzen der Zeit, die zum Entfernen einer Task aus einer
  3015.    Kette benötigt wird. 
  3016. -  Von nun an werden Tasks, die einen Wartezustand verlassen, der 
  3017.    sich durch eine Message-Passing Operation oder ein Warten auf ein 
  3018.    Ereignis ergeben hat, an den Kopf ihrer Task-Queue gestellt. So 
  3019.    kann insbes. bei der Ereignisverarbeitung unabhängig von der 
  3020.    Anzahl zur Verarbeitung anstehender Prozesse, schnell auf das 
  3021.    Ereignis reagiert werden.
  3022. -  Ein Fehler in der Delta-Berechnung beim QueueTimer wurde behoben.
  3023. -  Der Scheduler Stack wurde aus dem Datensegment auf den Heap 
  3024.    verlagert.
  3025. -  Der bislang lokale Typ PointerType wurde global verfügbar 
  3026.    gemacht.
  3027. -  Geringfügige Optimierungen im Assemblermodul wurden durchgeführt.
  3028. -  Neu hinzugekommen ist eine Prozedur SetPri, mittels derer ein 
  3029.    Prozeß zur Laufzeit seine Priorität verändern kann. 
  3030. -  Überarbeitung des Handbuches
  3031. .PA
  3032. Version 2.00 / März 1989
  3033.  
  3034. - Neugliederung der Quelle; verbesserte Modularisierung
  3035. - Überarbeitung der Dokumentation
  3036.   - Aufbereitung als Quelle für einen Dokumentationsprozessor
  3037.   - nun auf unterschiedliche Papierformate leicht anpaßbar
  3038.   - Bereitstellung des Dokument-Listers (auch als C-Quelle)
  3039. - Zeitscheibengröße nicht mehr auf 55,5ms gegrenzt
  3040.   - neue Prozedur SpeedUp()
  3041. - 9 Prioritätsebenen
  3042. - Verbesserung der Systemeinbettung
  3043.   - PrtScr-Interrupt (INT 5H) wird nun als nicht-reentrant behandelt
  3044.   - Maustreiber-Interrupt (INT 33H) wird ebenfalls als nicht-
  3045.     reentrant behandelt
  3046. - Verbesserung der Ereignisverwaltung
  3047.   - Performance-Steigerung durch Ersetzen der bisherigen Verwalt-
  3048.     ungsroutinen
  3049.   - nun max. 20 Ereignisse
  3050.   - Ereignisauswertung bei jedem Tick
  3051.   - Tritt ein Ereignis für eine Task ein, die eine höhere Priorität
  3052.     hat als diejenige Task, die gerade die CPU besitzt, so wird
  3053.     deren Zeitscheibe zwangsbeendet sobald dies ohne Gefahr möglich
  3054.     ist.
  3055.   - Einführung eines Port-Change Ereignisses
  3056.   - EventWait entfällt
  3057.   - Neu: MemoryEventWait und PortEventWait
  3058.   - Neu: EventKind
  3059.   - Änderung: EventType
  3060. - Erweiterung der Timer-Verarbeitung
  3061.   - Einführung von Watch-Dog Timern
  3062.   - Änderung: TimerType
  3063.   - Neu:      TimerKind, WatchDogType
  3064.   - Neu:      QueueWatchDog
  3065.   - Änderung: Counter in QueueTimer/QueueWatchDog nun LongInt
  3066. - Veränderung der Definition einer Task
  3067.   - Jede Task kann nun einen Parameter vom Typ Pointer erhalten
  3068.   - Jede Task MUSS nun als FAR-Prozedur codiert werden!
  3069.   - Eine Task kann sich durch Verlassen der Pascal-Prozedur z. B.
  3070.     mittels EXIT beenden und muß nicht mehr zwingend die System-
  3071.     funktion Terminate aufrufen
  3072. - Neue Systemaufrufe allgemeiner Natur
  3073.   - ChangePri
  3074.   - Kill
  3075.   - SemDiag
  3076.   - RemoveSemKill
  3077. - Implementierung der Sleep-Queue mit Hilfe von WatchDog-Timern
  3078.   - Änderung: TaskDescType (DelayCount fällt weg)
  3079.   - Änderung: DumpTaskTable
  3080. - Textdatei-Gerätetreiber für Pipes
  3081. - zeichenorientierter Print-Spooler als Ersatz für die Unit PRINTER
  3082. - interruptgesteuerte V24 Routinen (Public Domaine) bis 115200 baud
  3083. - Pipe-Unterstützung für die V24-Schnittstelle; Schreiben und Lesen
  3084.   der Schnittstelle über eine Pipe
  3085. - weitere Demoprogramme hinzugefügt
  3086.  
  3087. .PA
  3088. Version 2.02 / September 1989
  3089.  
  3090. - geringfügige Korrekturen
  3091. - Erweiterung der Unit V24 für bis zu 6 serielle Schnittstellen
  3092.   (jedoch wie bisher nur je 1 zu einem Zeitpunkt)
  3093.  
  3094. .PA
  3095. Version 2.10 / April 1990
  3096.  
  3097. - Fehlerbehebung in SemSignal()
  3098. - Fehlerbehebung im Demoprogramm FINDER.PAS
  3099. - Fehlerbehebung in der Berechnung der dynamischen CPU-Zeit
  3100. - Erweiterung des Kernels
  3101.   - Resourcen
  3102.     - CreateRsc
  3103.     - RemoveRsc
  3104.     - RemoveRscKill
  3105.     - RequestRsc
  3106.     - GrantRsc
  3107.     - ReleaseRsc
  3108.   - Exclusiv-Modus
  3109.     - Enter Exclusive
  3110.     - Leave Exclusive
  3111.   - abschaltbares Time-Slicing
  3112.   - die Idle-Loop wird nun nach jedem Timer-Tick preempted;
  3113.     dadurch weniger vergeudete CPU-Zeit
  3114. - Ausgliederung der Makros aus den Assemblermoduln in
  3115.   Include-Files
  3116. - Demoprogramm PHILO.PAS
  3117. - Demoprogramm RSCTEST.PAS
  3118. - Online-Guide (optional)
  3119. - Wegfall der Unterstützung des A86-Assemblers
  3120.