News   Magazin   Börse   Links   ArcArchie   Homepages
 Magazin  Multitaskingprogrammierung unter RISC OS Home 
Hardware   Software   Praxis   Sonstiges  
Multitaskingprogrammierung unter RISC OS  Carlos Michael Santillán  3. 10. 2005

Das Betriebssystem des Archimedes, Risc PC's und deren Nachkommen Iyonix pc und A9home ist ein Multitaskingbetriebssystem. Multitasking bedeutet, daß der Computer in der Lage ist mehrere Programme quasi gleichzeitig abzuarbeiten. Der Benutzer kann z. B. einen Text schreiben, obwohl gleichzeitig eine Graphik gedruckt und eine Datei aus dem Internet heruntergeladen wird. Im Idealfall merkt der Textschreiber nichts von den beiden anderen Programmen bei seiner Texterstellung. Es gibt grundsätzlich zwei verschiedene Arten von Multitaskingbetriebssystemen.

Jedes Programm braucht den Zentralprozessor um seine Aufgabe zu bewerkstelligen. Während ein Programm, auch Prozeß genannt, den Zentralprozessor beansprucht, schlafen die anderen Prozesse. Damit man Multitasking erhält muß von Zeit zu Zeit ein anderer Prozeß die CPU erhalten. Wenn die CPU häufig jeden Prozeß kurz bedient meint man das alle Programme gleichzeitig ihren Dienst tuen. Wenn das Programm selbst die CPU für einen anderen Prozeß freigibt nennt man dies kooperatives Multitasking. Das Betriebssytem RISC OS ist ein kooperatives Betreibssytem und so muß jedes RISC OS Programm aktiv ab und zu den anderen Programmen etwas von dem Zentralprozessor abgeben.

Andere Betriebssyteme wie z. B. Unix oder Windows NT arbeiten mit einem preemptiven Multitasking. Dabei entscheiden nicht die Prozesse selbst wann die CPU freigegeben wird, sondern das Betriebssystem. Diese Entscheidung wann ein Programm den Zentralprozessor freigeben muß ist grundsätzlich zeitabhängig. Jedes Programm bekommt also nur in bestimmten Zeitabständen die CPU zugewiesen. Da das Betriebssystem bei preemptiven Multitasking die Verteilung der CPU-Zeit koordiniert, braucht sich der Programmierer in der Regel nicht um das Multitasking kümmern.

So weit die sehr knappe Einführung in die Arten von Multikasking. Wie schon beschrieben muß jedes Programm beim kooperativen Multitasking selbst die CPU freigeben, damit die anderen Prozesse auch zum Zuge kommen. Doch leider erlebt man unter RISC OS immer wieder Programme die den Rechner blockieren. Diese Programme sind nicht kooperativ. Dieser Artikel will zeigen, wie einfach es ist kooperative Programme zu schreiben.

Das folgende BASIC-Programm zeigt ein einfaches WIMP Programm. Hier findet kein Multitasking statt. In der Zeit der Berechnung wird kein anders Progamm ausgeführt. Um weiter zu Arbeiten muß man warten bis das Programm fertig ist. Für die Beispielprogramme habe ich BASIC genommen, da dies in jeden RISC OS Rechner vorhanden ist. Es sollte aber kein Problem sein die Programme in andere Programmiersprachen zu übertragen.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Singletask" TO , task%
start% = TIME
FOR i% = 1 TO rounds%
  PROCcalc(i%)
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Singeltask"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    x = x + SIN(ATN(COS(LOG(y))))
  NEXT j%
ENDPROC

Programm Singletask

Wie funktioniert das Programm, daß man wie die folgenden auch herunterladen kann? Zuerst wird das Programm zu einer Task bzw. Aufgabe gemacht (Wimp_Initialise). Dann wird mit der Funktion TIME die aktuelle Zeit gesichert. In der folgenden FOR-Schleife wird die Prozedur calc mehrfach aufgerufen. In dieser Prozedur gibt es nur eine sinnlose Berechnung die einfach nur Rechenzeit verschwendet. Am Schluß wird die Laufzeit des Programmes ausgegeben. Wenn die eigentliche Arbeit des Programmes sehr schnell abgearbeitet ist reicht die obige Konstruktion aus, denn die anderen Prozesse werden ja nicht ernsthaft in ihren Arbeit gestört. Braucht das Programm viel Rechenzeit, also mehr als ein paar Sekunden, dann blockiert dieses Programm merklich die anderen Programme. Nun sollte, nein muß, man das Programm kooperativ schreiben.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Multitask1" TO , task%
start% = TIME
FOR i% = 1 TO rounds%
  SYS "Wimp_Poll", &1ce3972, blk% TO event%
  CASE event% OF
    WHEN 0: PROCcalc(i%)
  ENDCASE
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Multitask1"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    x = x + SIN(ATN(COS(LOG(y))))
  NEXT j%
ENDPROC

Programm Multitask1

Im Gegensatz zum ersten Programm ist dies ein Multitaskingprogramm. In der FOR-Schleife wird mit Wimp_Poll die CPU freigegeben, also können nun andere Programme ihr Werk verrichten. Tritt ein Ereignis für das Programm auf, wird mit der CASE-Anweisung das Ereignis ausgewertet. Im Beispielprogramm wird nur das Ereignis Null_Reason_Code ausgewertet. Dieses Ereignis tritt auf wenn die CPU kein anderes Programm bedienen muß. In einen realen Programm sollten aber schon einige Ereignisse mehr behandelt werden, aber das soll hier kein Thema sein. Um zu sehen ob überhaupt das Programm läuft, kann man einem Blick auf den Aufgabenmanager (Klick auf das Symbol ganz rechts auf der Iconbar) werfen.

Dieses Programm überläßt recht häufig die CPU anderen Prozessen. Wenn das Unterprogramm calc schnell abgearbeitet ist, kann man bevor das Programm schlafen geht calc auch mehrere Male hintereinander aufrufen. In dem nächsten Beispielprogramm wird in die FOR-Schleife eine Abfrage eingbaut.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Multitask2" TO , task%
start% = TIME
FOR i% = 1 TO rounds%
  IF i% MOD freq% = 0 THEN
    SYS "Wimp_Poll", &1ce3972, blk% TO event%
  ENDIF
  CASE event% OF
    WHEN 0: PROCcalc(i%)
  ENDCASE
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Multitask2"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    x = x + SIN(ATN(COS(LOG(y))))
  NEXT j%
ENDPROC

Programm Multitask2

Mit der IF-Abfrage wird erst bei jeden zehnten Durchgang (Variable freq%) der FOR-Schleife das Programm schlafen gelegt. Durch diese Maßnahme wird das Programm schneller abgearbeitet. Doch leider laufen die anderen Aufgaben der CPU, z. B. Fenster verschieben, nicht mehr so flüssig wie mit dem ersten Programm, insbesondere wenn man keinen StrongARM benutzt. Man kann aber mit der Variablen freq% die Häufigkeit des Schlafengehens verändern. So kann man die Laufzeit des Programmes beschleunigen oder verlangsamen und damit den andern Prozessen weniger bzw. mehr CPU-Zeit geben. Man sollte aber mit Rücksicht auf ältere Maschinen vorsichtig mit dieser Einstellung umgehen.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Multitask3" TO , task%
start% = TIME
diff% = start%
FOR i% = 1 TO rounds%
  IF TIME > diff% + freq% THEN
    SYS "Wimp_Poll", &1ce3972, blk% TO event%
    diff% = TIME
  ENDIF
  CASE event% OF
    WHEN 0: PROCcalc(i%)
  ENDCASE
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Multitask3"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    x = x + SIN(ATN(COS(LOG(y))))
  NEXT j%
ENDPROC

Programm Multitask3

In der dritten Multitaskingvariante wird Anhand der Laufzeit entschieden, wann die anderen Programme arbeiten dürfen. In der IF-Abfrage wird geprüft ob eine bestimmte Zeit (im Programm 1/10 Sekunde) vergangen ist. Wenn die Zeitspanne noch nicht erreicht worden ist wird das Unterprogramm calc abgearbeitet. Ist die Zeit verstrichen, wird mit Wimp_Poll die CPU freigegeben. Dieses Programm behindert die anderen Programme etwas weniger als das vorherige. Diese Konstruktion ist recht günstig, da dieses Programm unabhängig von der Prozessorgeschwindigkeit in mehr oder weniger regelmäßigen Abständen den Prozessor freigibt (hier maximal freq% hundertstel Sekunden plus Dauer von calc).

Acorn hat seinerzeit eine andere zeitliche Variante in RISC OS mit dem SWI Wimp_PollIdle eingeführt. Dieser Befehl regelt, wann frühestens das Programm wieder die CPU bekommt. Der Aufruf von Wimp_PollIdle entspricht fast dem von Wimp_Poll:

R0: Poll-Maske
R1: 256 Bytes Ereignis-Puffer
R2: Zeit nachdem ein Null_Reason_Code frühestens in das
    Programm zurückkehren darf, hundertstel Sekunden
R3: Zeiger auf das Poll-Wort (ab RISC OS 3)
						
Wimp_PollIdle

Mit dem Register 2, daß in Wimp_Poll unbenutzt ist, stellt man also die Dauer in hundertstel Sekunden ein die das Programm mindestens schläft und gibt solange den anderen Prozessen die Möglichkeit sich zu entfalten. Die gewünschte Zeit des Schlafens muß zu der Zeit des letzten harten Resets (OS_ReadMonotonicTime) addiert werden.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Multitask4" TO , task%
start% = TIME
diff% = start%
FOR i% = 1 TO rounds%
  SYS "Wimp_PollIdle", &1ce3972, blk%, FNsleep(freq%) TO event%
  CASE event% OF
    WHEN 0: PROCcalc(i%)
  ENDCASE
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Multitask4"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    x = x + SIN(ATN(COS(LOG(y))))
  NEXT j%
ENDPROC
DEF FNsleep(freq%)
  LOCAL time%
  SYS "OS_ReadMonotonicTime" TO time%
= time% + freq%

Programm Multitask4

Wie in den zweiten Programm ist die Konstruktion sehr einfach. Mit der Variablen freq% wird die minimale Schlafdauer eingestellt und in der Funktion FNsleep für Wimp_PollIdle berechnet. Ist die Schlafenszeit beendet und es tritt das Ereignis Null_Reason_Code auf, wird das Unterprogramm calc abgearbeitet. Diese Programm blockiert die anderen Aufgaben des Rechners sehr wenig, läuft aber am längsten von allen vorherigen Multitaskingprogrammen. Durch Änderungen in der Funktion FNsleep kann man auch einen bestimmten Zeitpunkt bestimmen, wann das Programm frühstens aufwachen soll.

DIM blk% 256
rounds% = 400
work% = 400
event% = 0
freq% = 10
SYS "Wimp_Initialise", 200, &4b534154, "Multitask5" TO , task%
start% = TIME
diff% = start%
FOR i% = 1 TO rounds%
  SYS "Wimp_PollIdle", &1ce3972, blk%, FNsleep(freq%) TO event%
  CASE event% OF
    WHEN 0: PROCcalc(i%)
  ENDCASE
NEXT i%
blk%!0 = 999
$(blk% + 4) = STR$((TIME - start%) / 100) + " Sekunden" + CHR$(0)
SYS "Wimp_ReportError", blk%, 17, "Multitask5"
SYS "Wimp_CloseDown", task%
END
DEF PROCcalc(x)
  y = 9.9
  FOR j% = 1 TO work%
    IF TIME > diff% + freq% THEN
      SYS "Wimp_PollIdle", &1ce3972, blk%, FNsleep(freq%) TO event%
    ENDIF
    IF event% = 0 THEN
      diff% = TIME
      x = x + SIN(ATN(COS(LOG(y))))
    ENDIF
  NEXT j%
ENDPROC
DEF FNsleep(freq%)
  LOCAL time%
  SYS "OS_ReadMonotonicTime" TO time%
= time% + freq%

Programm Multitask5

Im letzten Programm hier ist, wie im vierten, das Multitasking mit Wimp_PollIdle unabhängig von der CPU-Geschwindigkeit und eignet sich für unterschiedliche schnelle CPU's, da das Programm mindestens eine definierte Zeit schläft. Dies eignet sich insbesonders für langwierige Berechnungen, da dort innerhalb der Berechnung die CPU freigegeben wird. Hier wurde die Prozedur calc verändert, da hier Wimp_PollIdle aufgerufen wird. Es wird also in der eigentlich Berechnung die CPU für andere Programme freigegeben um ein flüssiges Multitasking bei langen Berechnungen zu erhalten. Auch hier muß man natürlich ein paar mehr Ereignisse auffangen als nur Null_Reason_Code. Das Beispiel zeigt auch, daß man Wimp_PollIdle an mehreren Stellen im Programm aufrufen kann.

Die Multitaskingprogramme drei und vier sind besser als die ersten beiden, weil sie nach einer bestimmten Zeit die CPU freigeben bzw. wieder erhalten. Sie sind also nicht so sehr abhängig von der CPU-Geschwindigkeit. Die Entscheidung welche der beiden Verfahren das richtige ist hängt von den Programmgegebenheit ab. Eine langwierige Berechnung wird man wohl in bestimmten Zeitabschnitten unterbrechen (Multitask3). Muß ein Programm in bestimmten Zeitintervallen die CPU haben, wird man nicht um den SWI Wimp_PollIdle (Multitask4) kommen. Zusätzlich kann man natürlich Varianten kombinieren, so wie im letzen Beispiel.

Ob und wie RISCOS Ltd. oder Castle den alten Ruf nach einem preemptiven RISC OS nachkommen, wird die Zukunft zeigen. Aber wie man sieht ist die Programmierung von Multitasking-Programmen gar nicht so schwer und ich hoffe, daß alle Programmierer sich an diese Programmierung heranwagen. Leider brauchen alle Multitasking-Programme länger für die Berechnung als ein Singletask-Programm, aber dafür kann man während eine langwierige Berechnung immer noch andere Aufgaben mit dem Rechner bearbeiten. Die Programmierer können natürlich eine Einstellung in ihren Programmen einbauen, um das Programm mehr oder weniger schnell ablaufen zu lassen und damit anderen Aufgaben mehr oder weniger CPU Zeit zu überlassen.

Am Schluß noch ein Tip: Man kann ein Singletask-Programm mit dem *TaskWindow Kommando oder mit einer TaskObey-Datei zu einem Multitasking-Programm zwingen. Doch leider funktioniert dies nur bei einfachen Programmen, die keine Task (SWI Wimp_Initialise) sind. Auch diese beiden Möglichkeiten sind in den Beispielen zu finden.

Beispiele laden (Zip, 5,2 kByte)

Hardware   Software   Praxis   Sonstiges  
ArcSite   News   Magazin   Börse   Links   ArcArchie   Homepages