Jöerg Wleklik <joerg@wleklik.de>
Der Ausdruck CNC steht für Computer Numerical Control und bezeichnet ein Verfahren zur Computer gestützten Steuerung von Maschinen. Dabei besteht die gesamte Steueranlage einer Maschine aus einer Vielzahl von Komponenten.
Die wichtigsten Elemente sind dabei:
Die Kommunikation der einzelnen Prozesse erfolgt dabei über Shared-Memory, wodurch zeitaufwändige System-Calls vermieden werden. Eine Ausnahme ist hierbei das Human Machine Interface (HMI). Siehe Abbildung:1
Die Kommunikation des HMI und der CNC erfolgt über das TCP/IP Protokoll und ist somit netzwerktransparent. Die direkte Verbindung zur CNC wird hier über den CNC-Server bereitgestellt. Auf diesen kann sowohl lokal als auch remote mit mehreren Instanzen des HMI zugegriffen werden.
Der PLC ist zwar im eigentlichen Sinne kein direkter Bestandteil der CNC, da aber auch die PLC-Prozesse über den CNC-Echtzeitscheduler gesteuert werden, stellt er sich für die Portierung als ein weiteres Modul dar. Der PLC besteht aus zwei Bereichen, dem Runtime-System und der Entwicklungsumgebung. Da die Entwicklungumgebung durch einen Drittanbieter geliefert wird, konnte diese bei der Portierung nicht berücksichtigt werden. Im praktischen Einsatz wird die Entwicklungsumgebung auf einem separaten Rechner ausgeführt und über eine serielle Verbindung an die Steuerung angebunden. Für die eigentliche Ausführung der PLC-Programme ist die Entwicklungsumgebung nicht mehr erforderlich.
Die Entscheidung für eine CNC auf der Basis von GNU/Linux wurde im wesentlichen durch die Möglichkeit der Kostenersparnis gesteuert. Dabei sind zum Einen die Anschaffungskosten der CNC zu nennen, die um die Lizenzkosten verringert werden können. Zum Anderen müssen die Kosten für die Lizenzverwaltung beim Kunden berücksichtigt werden.
Bei dem Preis für eine komplette CNC-Maschine stellen die Lizenzkosten zwar nur einen geringen Prozentsatz dar, aber gerade im Massenmarkt der Low-Cost-Maschinen kann diese Preisreduktion wettbewerbsentscheidend sein.
Weiterhin wurde durch mehrere Kunden, insbesondere aus der asiatischen Region, ein deutlichen Interesse an einer GNU/Linux basierenden Steuerung signalisiert.
Für die Portierung wurden folgende Werkzeuge verwendet:
Das Herzstück der CNC ist die Echtzeitverarbeitung. In einem Multitasking-Betriebssystem werden zwar verschiedene Prozesse quasi parallel ausgeführt, aber ein laufender Prozess kann zu jedem Zeitpunkt unterbrochen werden. Wann ein unterbrochener Prozess seine Arbeit wieder aufnehmen kann, hängt von vielen Faktoren ab und liegt im Bereich von einigen Millisekunden bis hin zu Sekunden auf einem überlasteten System. Dabei sind die Unterbrechungszeiten für den Prozess selbst im vorhinein nicht berechenbar. Für die Ansteuerung einer Maschine, die Präzisionsarbeit ermöglichen soll, ist ein solches Verhalten schlichtweg unbrauchbar. Um aber trotzdem die Vorteile eines modernen Betriebssystems nutzen zu können muss auf einen Echtzeitscheduler zurückgegriffen werden.
Darüber hinaus muss durch eine geeignete Konfiguration des Linux-Systems sicher gestellt werden, daß die Lattenzeiten, die Zeiten zwischen Auslösen und Behandeln eines Interrupts, auf ein akzeptables Maß reduziert werden. Ohne besondere Eingriffe in den Linux Kernels, sondern nur durch geeignete Konfiguration, kann eine Begrenzung der Lattenzeiten auf rund 50 Mikrosekunden erreicht werden. Dazu muss in dem IDE-Treiber das IRQ-Unmasking und DMA eingeschaltet werden, der Syslog darf nur asynchron schreiben und das Umschalten zwischen Grafik-und Text-Konsole muss vermieden werden. Insbesondere die Umschaltung zwischen Grafik- und Textkonsole kann Lattenzeiten bis in den Bereich von Millisekunden verursachen.[1]
Darüber hinaus muss die eigesetzte Hardware mit Bedacht ausgewählt werden. Eine drastische Erhöhung der Lattenzeit wurde festgestellt als ein Motherboard mit integriertem Graphik-Chipsatz aber ohne gesonderten Graphikspeicher eingesetzt wurde. Bei diesem Board wurde ein Teil des Hauptspeichers als Graphispeicher verwendet, was dazu führte, daß bei größere Bildänderungen, z.B. verschieben eines Fensters, die Speicherbänke für die CPU blockiert wurden.
Im Zuge der Portierungsarbeiten wurde entschieden einen eigenen Echtzeitscheduler für Linux zu entwickeln. Dafür wahren mehrere Gründe ausschlaggebend:
Ein generischer Echtzeitscheduller kann nur in den seltesten Fällen ohne Hardware spezifische Erweiterungen auskommen. Da der eigentliche Scheduler unter der GPL steht, müßten auch alle Hardware abhängigen Erweiterung unter dieser Lizenz veröffentlicht werden. Dies ist aus firmenpolitischen Gründen jedoch oftmals nicht möglich. Um dennoch eine Verwendung des Schedulers diesen Projekten zu ermöglichen besteht der Scheduler aus zwei getrennten Kernel-Modulen.
Das Modul ,,rt-scheduler'' enthält den eigentlichen Echtzeitscheduler und alle für die Behandlung von Echtzeitprozessen erforderlichen Funktionen und kann ohne weitere Abhängigkeiten in den Kernel geladen werden. Es unterliegt der GPL und erfordert, daß alle Änderungen oder Erweiterung ebenfalls unter der GPL veröffentlicht werden. Es exportiert einige Schnittstellen-Funktionen, auf die von weiteren Modulen zugegriffen werden kann. Über diese Schnittstellen wird der Scheduler an die eigentliche Interruptquelle gebunden und es können Hardware abhängige Funktionen zur Ausführung registriert werden. Eine typische Anwendung ist beispielsweise der IrqAcknowledge bei der auslösenden Hardware.
Das hardwareabhängige Modul kann unter einer vom Modul ,,rt-scheduler'' unabhängigen Lizenz stehen und greift auf den Scheduler nur über die definierten Schnittstellen zu. Über dieses Modul können unter anderem auch die File-Operations des Modules ,,rt-scheduler'' erweitert und überschrieben werden, so daß sich dem Anwendungsprogramm nur eine Schnittstelle päsentiert. Über diesen Weg ist es ebenfalls möglich die Device-Klasse des Schedulers von ,,Cahr-Device'' auf beispielsweise ,,USB-Device'' zu ändern.
Der grundsätzliche Ablauf eines Takts sieht wie folgt aus:
Damit eine Funktion als Echtzeitprozess verarbeitet werden kann, muss ein Userprozess diese Funktion bei dem Echtzeitscheduler registrieren. Dies geschieht über einen eigens definierten IOCTL Aufruf, dem neben anderen Parametern auch ein Zeiger auf die auszuführende Funktion übergeben wird. In der Behandlung dieses IOCTL wird eine Taskstruktur für die Funktion angelegt, die alle benötigten Angaben enthält. Dazu gehört auch die Adresse des "Page Directory's" des aufrufenden Prozesses. Diese Adresse wird später benötigt um den Adressraum des registrierenden Prozesses, der ja die Echtzeitfunktion enthält, wieder aufzusetzen.
Die Funktion, die in Echtzeit abgearbeitet werden soll, ist also eine ganz gewöhnliche Funktion eines Userprozesses. Sie kann auch in Echtzeit auf alle globalen und lokalen Variablen zugreifen und, wenn es sein muss, ist auch eine parallele Ausführung sowohl in Echtzeit als auch im Userprozess möglich. Die Verwendung von lokalen Variablen ist unkritisch, da diese auf dem Stack angelegt werden und die Funktion durch den Echtzeitscheduler einen eigenen, vom Userprozess unabhängigen, Stack bekommt. Anders sieht dies bei globalen Variablen aus, da diese im Datensegment des Prozesses angelegt werden, und somit auch in der Echtzeitverarbeitung erreichbar sind. Der Programmierer muss gegebenenfalls also berücksichtigen, daß globale Variablen des Userprozesses jederzeit durch die Echtzeitverarbeitung verändert werden können.
Wenn in der Funktion keine direkten Hardware-Zugriffe erfolgen, was durch Kapselung erreicht werden kann, ist es ohne weiteres möglich die Funktion komfortabel im Userprozess zu debuggen. Ebenso ist es leicht möglich einen simulierten Mode aufzubauen, bei dem lediglich direkte Hardware-Zugriffe ausgeblendet oder durch Simulatorfunktionen ersetzt werden.
Für einen Prozess, der Echtzeitfunktionen registrieren will, und insbesondere für die Echtzeitfunktionen selbst gelten bestimmte Einschränkungen.
Der Scheduler arbeitet die Prozesse über eine priorisierte Liste ab. Dabei kann jeder Prozess neben der Priorität auch noch einen Taktteiler enthalten. Die Priorität bestimmt die Reihenfolge in der die Prozesse, die lauffähig sind, aufgerufen werden, und entscheidet im Fall der Selbstunterbrechung ob ein anderer Prozess eingeschoben werden muss. Über den Taktteiler kann ein Prozess mit einer anderen Frequenz als der Schedulerfrequenz aufgerufen werden. Allerdings werden nur ganzzahlige Teiler zugelassen.
Der Scheduler läuft grundsätzlich in dem Systemkontext des Interrupthandlers, einem so genannten "Kernel control path", das heißt, er hat die Privileg-Ebene des Kernels. In diesem Zustand hat der Scheduler Zugriff auf den gesamten Adressbereich des Kernels und auf den Adressbereich des gerade unterbrochenen Userprozesses.
Der unterbrochene Prozess ist aber in der Regel nicht der Prozess dessen Echtzeitfunktion ausgeführt werden soll. Damit nun die Echtzeitfunktion ausgeführt werden kann, muss erst der Adressraum des entsprechenden Prozesses aktiviert werden. Dazu wird das "Page-Register CR3" mit der Adresse des entsprechenden "Page Directory's" geladen.
Damit ein fehlerhafter Echtzeitprozess nicht die Stabilität des gesamten Systems gefährdet, wird jedem Prozess ein eigener Stack zur Verfügung gestellt.
Sind diese Vorbereitungen abgeschlossen, kann die Echtzeitfunktion des Userprozesses aufgerufen werden. Nach der Rückkehr der Funktion werden die vorhergehenden Einstellungen restauriert, damit die normale Programmausführung fortgesetzt werden kann.
Da der Echtzeitscheduler asynchron zum Hauptscheduler des Systems läuft, kann auch der Fall eintreten, daß der Hauptscheduler unterbrochen wird. Das kann dann problematisch werden, wenn der Hauptscheduler einen Prozess verändert, dessen Echtzeitfunktion jetzt aufgerufen werden soll. Dies ist beispielsweise der Fall, wenn der Nichtechtzeitteil des Prozesses eine Exception verursacht hat und der Prozess beendet werden soll. In einem solchen Fall ist es möglich, daß der Prozess zwar noch in der Liste der lauffähigen Prozesse eingetragen ist, aber sein Adressraum bereits ungültig ist.
Bevor also der Adressraum für die Echtzeitverarbeitung aufgesetzt werden kann, muss geprüft werden ob der Userprozess lauffähig und vollständig gültig ist.
Um Fehlerzustände in der Echtzeitverarbeitung richtig zu behandeln, wird jeweils für die Dauer des Schedulerlaufs das Exceptionhandling des Betriebssystems durch ein eigenes Exceptionhandling ersetzt. Betroffen sind hiervon folgende Exceptions:
Exception | Bezeichnung |
---|---|
1 | Devide by zero |
6 | Invalid opcode |
13 | General protection |
14 | Page fault |
16 | FPU-Error |
Tritt eine dieser Exceptions ein, wird die auslösende Echtzeitfunktion als fehlerhaft gekennzeichnet und von der weiteren Verarbeitung ausgeschlossen. Der Stack sowie die aktuellen Prozessorregister werden für eine spätere Fehleranalyse gesichert.
Da bei der Ausführung des Echtzeitscheduler alle Interrupts gesperrt werden, kann eine Endlosschleife in einer Echtzeitfunktion das gesamte System unbenutzbar machen. Weiterhin kann die reguläre Bearbeitung einer einzelnen Echtzeitfunktion länger dauern als die Zeit, die zwischen zwei Ticks zur Verfügung steht. Um diesen Problemen zu begegnen schaltet der Scheduler direkt vor der Ausführung einer Echtzeitfunktion seinen eigenen Interrupt wieder frei. Trifft nun während der Abarbeitung einer Echtzeitfunktion ein erneuter Interrupt für den Scheduler ein, kann dieser prüfen ob eine Funktion mit höherer Priorität zur Abarbeitung ansteht und diese gegebenenfalls ausführen. Diese Schachtelung kann im Extremfall bis zu einer Tiefe von 7 unterbrochenen Prozessen reichen.
Jede Echtzeitfunktion erhält in ihrer Taskstruktur einen Parameter, der angibt, wieoft sie unterbrochen werden darf. Wird dieser Wert überschritten, wird von einer Endlosschleife ausgegangen und die Funktion von der weiteren Ausführung suspendiert.
Der Scheduler exportiert über das "/proc"-Filesystem einige Status-und Debug-Informationen. Dazu gehören:
Die notwendigen Anpassungen am Standard Linux Kernel beschränken sich auf den Export einiger Symbole, die normalerweise nicht benötigt werden. Siehe A.1"Änderungen am Standard-Kernel" Der Scheduler arbeitet in seiner derzeitigen Form allerdings nur auf Intel-kompatiblen Prozessoren.
Bei der Entwicklung des Echtzeitschedulers hat sich die Dokumentation aus [4] und [5] als sehr nützlich herausgestellt. In manchen Bereichen ist die Kernelentwicklung zwar schon weiter als die Dokumentation, aber die verbale Beschreibung fördert das Verständniss und erleichtert die Orienetierung in den Kernelquellen ungemein.
Der Kernel selbst bietet ein ausgeprägtes API für die Treiberentwicklung. Über dieses API können für viel Aufgaben eigene Funktionen einfach registriert werden, so daß sich der Treiberentwickler mit den dahinter liegenden Kernelstrukturen nicht weiter befassen muss. Dies vereinfacht zum einen die Entwicklung und reduziert auf der anderen Seite die möglichen Fehlerquellen erheblich.
Hinzufügen folgender Zeilen
#include <linux/irq.h>
#include <asm/hw_irq.h>
EXPORT_SYMBOL(irq_desc);
EXPORT_SYMBOL(idt);
EXPORT_SYMBOL(disable_8259A_irq);
EXPORT_SYMBOL(enable_8259A_irq);
extern unsigned int cached_irq_mask;
EXPORT_SYMBOL(cached_irq_mask);
Änder der Zeile
static unsigned int cached_irq_mask = 0xFFFF;zu
unsigned int cached_irq_mask = 0xFFFF;
This document was generated using the LaTeX2HTML translator Version 2002-2-1 (1.70)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -no_subdir -split 0 -show_section_numbers /tmp/lyx_tmpdir6993IHPb4x/lyx_tmpbuf0/Scheduler-Artikel.tex
The translation was initiated by Joerg Wleklik on 2004-01-20