Das Dienstesystem wurde entsprechend der Vorüberlegungen aus Kapitel 7.4 in C++ objektorientiert implementiert. Dieses Kapitel soll den Funktionsumfang der implementierten Klassen und die Funktionalität der daraus abgeleiteten Dienste, welche in Kapitel 4 beschrieben worden sind, erläutern.
Die genauen Signaturen und die Semantik der einzelnen Funktionen sind den Quelltexten zu entnehmen. Eine Liste der relevanten Files der Prototypimplementierung zeigt die Tabelle 5.
|
Die Basis-RPC-Funktionalität welche benötigt wird, um zwischen den Diensten zu kommunizieren, wird in der Beispielimplementierung in den Dateien rpc.c und rpc.h zur Verfügung gestellt.
Zur Kommunikation zwischen den Diensten werden strukturierte Daten
serialisiert und über einen Kommunikationskanal versendet. Die Kodierung,
welche für die Serialisierung der Daten eingesetzt wird, wurde in
Anlehnung
an BER[24] entwickelt. Sie bietet im Vergleich zu BER nur eine
eingeschränkten Funktionsumfang und kommt dadurch mit weniger
Metainformationen und somit mit weniger Bandbreite aus. Es wurden nur
wenige Datentypen implementiert
, welche in Tabelle
6 aufgelistet sind. Es wurde auch auf eine Anpassung der
Daten in eine plattformunabhängige Darstellung wie z.B. bei XDR verzichtet,
da wir derzeit davon ausgehen, dass nur x86-Komponenten miteinander
kommunizieren werden. Die Kodierung ist jedoch einfach gegen eine andere
austauschbar.
Die einzelnen Daten werden anhand ihres Datentypes jeweils durch ein Identifikations-Byte eingeleitet. Anschließend folgt das unbearbeitete Datum. Bei der Dekodierung wird jeweils der Typ-Identifikator gelesen und anhand diesem die entsprechende Anzahl an Bytes als Datum angenommen.
Zusätzlich ist es Möglich, eine beliebige Anzahl an Daten zu gruppieren. Dies hat den Vorteil, dass man bei der Auswertung des Datenstromes inhaltlich zusammenhängende Daten als Einheit bearbeiten kann. Somit besteht die Möglichkeit, Strukturen in dem Datenstrom zu definieren. Diese Gruppierung ist auch rekursiv anwendbar.
Das implementierte RPC-Protokoll basiert auf der Serialisierung strukturierter Daten. Dabei ist das Verfahren, welches zum serialisieren genutzt wird, nicht festgelegt sondern austauschbar.
Es werden Anfragen (Request) und Antworten (Response) unterschieden, welche jeweils einen unterschiedlichen Aufbau des Datenstroms haben. Ein Request wird immer durch einen String eingeleitet, welcher die gerufene Funktion bezeichnet. Anschließend folgen die kodierten Parameter. Die Parameter-Struktur ist frei wählbar und wird von jedem entsprechenden Dienst ausgewertet und gegebenenfalls durch einen Fehlercode zurückgewiesen. Antworten auf Requests werden immer durch einen Status-Byte eingeleitet (mögliche Werte sind in Tabelle 7 aufgelistet). Im Anschluss daran folgen die kodierten Ergebniswerte. Die Struktur der Ergebnisse ist ebenfalls frei wählbar.
|
Dieser geringe Funktionsumfang hat sich als ausreichend herausgestellt. Durch das genutzte RPC-Protokoll und die beschriebene Kodierung wird Bandbreite schonend genutzt. Somit ist der Einsatz des RPC-Mechanismuses für die Kommunikation über den RS485-Bus geeignet.
|
)
Dies ist die Basisklasse, welche alle nötigen Funktionen und Variablen
für einen Dienst zur Verfügung stellt. Sie verwaltet alle zur
Verfügung gestellten Funktionen und Variablen. Darüber hinaus startet
sie einen Server-Thread (siehe rpc.h). Diese Klasse
implementiert zusätzlich den Vergleichsoperator, um
Beschreibungslisten von Diensten miteinander zu vergleichen.
|
Ein Objekt der Klasse service stellt standardmäßig Funktionen
(siehe Tabelle 10 auf Seite
) für
den entfernten Aufruf zur Verfügung, mit welchen man Eigenschaften des
Dienstes abfragen und den Dienst überwachen kann. Dadurch ist eine
leichtes Herausfinden der Funktionalität eines Dienstes mit einfachen
Tools möglich.
|
auf dem selben Rechnersystem über den bekannten Port 4000. Über ein
Objekt in diesem Modus können Suchanfragen gestellt werden oder können
Registrierungen erfolgen.
Objekte dieser Klasse, welche im Master- oder Standardnamensdienstmode sind, betreiben zusätzlich noch Dienste-Monitoring (in Funktion service_cache::watcher). In der Prototypimplementation werden die Dienste aller 5 Sekunden (DEFAULT_SCANTIME) auf ihre Funktion geprüft. Sobald ein Dienst 2 mal nicht antwortet (DEFAULT_TTL), wird er bei einer Suchanfrage nicht mehr beachtet. Nach weiteren 3 (DEFAULT_DEATH_TTL) gescheiterten Tests wird der Dienst abgemeldet. Falls der Dienst nur durch eine Netzwerkstörung nicht erreichbar war und somit eigentlich funktionstüchtig ist, so bleibt dieser solange deaktiviert, bis er sich erneut anmeldet. Dies geschieht in der Regel aller 120 Sekunden (DEFAULT_REREGISTER).
|
Neue Dienste können einfach programmiert werden, in dem man die Klasse
service ableitet und neue Funktionalität implementiert
(siehe Beispiel 4 auf Seite
). Auch die Implementierung der Nutzung
eines entfernten Dienstes ist nicht kompliziert (siehe Beispiel
5 auf Seite
). Die genaue Spezifikation der API ist den
Quell-Dateien zu entnehmen.
Wie in Kapitel 6 beschrieben, ist es für die Einführung des Zugangssystemes an der TUC notwendig, den vorhandenen RS485-Bus zur Kommunikation zu nutzen. Es ist auch herausgestellt worden, dass es sinnvoll ist, einen Treiber zu implementieren, welcher transparent IP zur Verfügung stellt.
Recherchen haben ergeben, dass es für Linux noch keine Implementierung für IP-Over-RS485 gibt. Aus diesem Grund wurde ein eigenes Kernel-Modul entwickelt.
In dem Fall der TUC werden nur 2 Adern des verlegten Kabels genutzt. Dies lässt laut RS485-Spezifikation eine asynchrone Kommunikation (Halbduplex) zu. Das hat zur Folge, dass nur ein Bus-Teilnehmer senden kann und alle anderen Teilnehmer empfangen diese Daten. Um dennoch eine geordnete Kommunikation zu ermöglichen, muss ein Teilnehmer die Steuerung des Busses übernehmen. Steuerung bedeutet hierbei, dass er koordiniert, welcher Teilnehmer senden darf und welcher nicht. Die steuernde Komponente soll hier als Master und die restlichen Komponenten als Slave bezeichnet werden. Jeder Teilnehmer wird durch eine busweit eindeutige Nummer identifiziert (Teilnehmernummer oder RS485-nodeid)
Für die Kommunikation über einen RS485-Bus gibt es meinen Recherchen zu Folge keinen Protkollstandard. Aus diesem Grund wurde ein eigenes Protokoll entwickelt.
Der Master sendet jeweils ein Paket auf den Bus, welches genau einen Teilnehmer adressiert. Sobald der entsprechende Teilnehmer dieses Paket empfangen hat, antwortet er mit einem Paket identischen Aufbaus, wobei in dem Adressfeld nun der Absender steht, damit der Master identifizieren kann, ob der richtige Teilnehmer geantwortet hat.
Ein Paket besteht jeweils aus einer Startmarkierung (Start-Kennung FRAME_SS=0x53), einem Adressfeld, der Nutzlast (Payload) und einer Markierung des Paketendes (End-Kennung FRAME_ES=0xca). Die Größe der Nutzlast ist von der eingestellten MTU abhängig. Auf eine Prüfsumme über die Nutzlast wurde vorerst verzichtet, da sich höhere Schichten (TCP) darum kümmern. Die Adresse, welche in dem Adressfeld angegeben werden kann, ist identisch zu der Teilnehmernummer (nodeid).
Bei diesem Verfahren treten jedoch verschiedene Laufzeitprobleme auf. Deshalb wurden einige Zeitabstände definiert, um die Übertragung optimieren zu können. Diese sind auch notwendig, da die eingesetzten Medienwandler Verzögerungen aufweisen. Die Sendeeinheit wird nach einer sendefreien Zeit von einem Baud bei 9600 Baud/s automatisch abgeschaltet. In dieser Zeit kann kein anderer Teilnehmer senden (Timer d1).
|
Der Parameter n2 ist eingeführt worden, da die kleinste
Zeitauflösung (z.B. für Kernel-Timer) in einem Standard-Kernel (also
nicht Realtime-Kernel) ein jiffie
ist. Um dennoch in kleinen
Zeiträumen warten zu können, wurde die Funktion usleep
genutzt. Nach der Wartezeit wird geprüft, ob Daten vorhanden sind,
ansonsten wird wieder unter Zuhilfenahme dieser Funktion gewartet.
Dies wird n2-mal durchgeführt, bevor zum nächsten Slave
übergegangen wird.
Zuerst wurde IP-Over-RS485 über einen Treiber implementiert, welcher im User-Space des Linux-Kernels zwischen der seriellen Schnittstelle (tty) und einem generischen Netzwerkinterface (tun/tap-Kernel-Treiber) vermittelt hat. Jedoch hat sich gezeigt, dass dies nicht optimal war, da die kleinste Zeitauflösung des Linux-Kernels ein jiffie ist, und der Treiber für die serielle Schnittstelle nur in dieser Auflösung mit dem Senden beginnen kann. Da nun Sender und Empfänger nur im Abstand von 10ms agieren konnten und die Frequenzgeber beider Teilnehmer unterschiedlich schnell liefen, konnte man im Worst-Case nur mit einer Latenz von 20ms kommunizieren, welche sich durch Interferenz begründen lässt (siehe Abbildung 4).
Deshalb wurde Arbeit in die Entwicklung eines Kernelmoduls gesteckt, da man im Kernel unmittelbar auf Interrupts reagieren und direkt mit dem seriellen UART kommunizieren kann. [1]
|
Nach erfolgreicher Installation des Kernelmoduls werden im Master-Mode so viele Netzwerkinterfaces (rs00, rs01, ..) bereitgestellt, wie es Teilnehmer gibt. Über diese Interfaces können nun Punkt-Zu-Punkt-Verbindungen konfiguriert werden.
Die Beschreibung der internen Struktur dieses Kernel-Modules würde den Rahmen dieser Studienarbeit sprengen und soll somit hier nicht weiter diskutiert werden [19].
Dieser Effekt dürfte nicht mehr auftreten, wenn ausschließlich moderne Boards benutzt werden, welche auch einen UART-Chip haben, welcher mit 24 MHz betrieben wird.
Neben den Standard-Zählern eines Unix-Netzwerk-Interface stellt der RS485-Treiber eine weitere Schnisttstelle zum Überwachen der Funktionalität des Kernel-Modules im /proc-Verzeichnis zur Verfügung. Daraus sind neben statistischen Zählern (payload Bytes, start sentinel) auch Informationen über Timeouts (irq-to, rx-to) und dem Status einzelner Teilnehmer incl. dem Zeitpunkt ihrer letzten Kommunikation sichtbar.
Für die Kopplung (Bridge) zwischen dem RS485-Bus und dem Campusnetz kommt ein Standard-PC oder eine Steuereinheit mit Ethernet in Reichweite zu einer Datendose zum Einsatz. Diese Brücke betreibt den RS485-Bus als Master und vermittelt Pakete zwischen dem Ethernet-Interface (eth0) und den RS485-Netzwerk-Interfaces (rs00, rs01, ..). Zusätzlich fungiert diese Brücke als Proxy für ARP-Anfragen (Proxy-ARP). Somit ist es möglich, dass die Steuereinheiten campusweit und transparent über IP angesprochen werden können. Es bietet sich an, alle Bridges nur über ein spezielles VLAN an das Campusnetz anzuschließen und Pakete in diesem VLAN nur von dedizierten Servern zuzulassen, um die Sicherheit der Klienten zu erhöhen.
Um viel Flexibilität auf den Client-Controllern zu bekommen, ist es sinnvoll, ein ausgereiftes Betriebssystem darauf laufen zu lassen. Dies wurde auch in Kapitel 7.1.1 schon erläutert.
Bevor im Rahmen dieser Arbeit eine eigene LINUX-Mini-Distribution entwickelt wurde, sind verschiedene existierende Distributionen für embedded Systeme evaluiert worden (z.B. jailbait, muLinux, fdlinux und greenfrog).
Zusammenfassend haben alle diese fertigen Distributionen den Nachteil, dass sie an eine bestimmte glibc-Version gebunden sind, welche meist sehr alt ist. Des Weiteren basieren sie auf alten Kernels und sind nur für Spezialanwendungen (z.B. Router) konzipiert.
Viele dieser Distributionen basieren auf dem Programm busybox [2], welches in einem statisch gelinkten Programm alle notwendigen Posix-Befehle zur Verfügung stellt. Dieses Programm kann auch als init-Ersatz benutzt werden. Somit ist es möglich, eine ausgereifte Distribution ausschließlich mit Hilfe von busybox zu bauen.
Die vollständige Konfiguration und Aufbereitung dieser Distribution soll hier nicht beschrieben werden. Sie ist dem Entwicklungsverzeichnis [18] der Distribution zu entnehmen.
Die derzeitige Installation der LINUX-Distribution erfolgt unter Zuhilfenahme des Paketes etherboot [26]. Beim Boot-Vorgang erhalten die Clienten automatisch über DHCP eine IP und es wird ein lauffähiges Linux über ROOT-NFS gebootet, welches die Flash-Disk der Controller partitioniert und sich selbst darin installiert.
Die komplette Konfiguration aller Controller geschieht zentral in der Datei /etc/setup. Um die Controller automatisch und eindeutig zu identifizieren, wird die MAC-Adresse der der internen Netzwerkkarte ausgelesen. Im Falle des miniPC besteht die Möglichkeit über das jida-Interface einen eindeutigen CPU-Identifikator auszulesen. Jedoch ist mir dies nicht gelungen. Grund hierfür ist, dass nur vorkompilierte Kernel-Module vorhanden sind, welche bei der eingesetzten Kernelversion (2.4.13) zu einem Absturz führen.
|
Anhand des eindeutigen Identifikators (MAC-Adresse) wird aus der Datei /etc/setup eine Controller-spezifische Konfigurationsdatei (/etc/config) erzeugt. Darin sind alle zur Laufzeit notwendigen Parameter enthalten, welche von allen benötigten Skripten ausgewertet werden.
Um bei einem Absturz zu verhindern, dass das Filesystem einen inkonsistenten Zustand erfährt, wurde die Flash-Disk in den Controllern in zwei Partitionen partitioniert (je 8 MByte), welche mit dem EXT3-Filesystem formatiert worden sind. Dieses Filesystem, bietet Data-Journaling-Fähigkeiten, so dass ein Absturz des Systems während eines Schreibvorganges keine Beschädigung des Filesystems nach sich zieht.
Die beiden Partitionen sind für unterschiedliche Aufgaben vorgesehen. Die erste Partition (root-Filesystem /) beherbergt alle Dateien, welche für die Funktionen des Betriebssystems und des Zugangssystem wichtig sind. Diese Partition ist vor Schreibzugriffen geschützt (Read-Only). Die zweite Partition verwaltet alle Daten, welche auf den Steuereinheiten während seiner Laufzeit geändert werden. Das sind vor allem Daten, welche von dem Dienst proxy in einem Cache abgelegt sind.
Um administrative Arbeiten auf den Controllern zu erleichtern wurden einige Tools geschrieben, welche in Tabelle 15 aufgeführt sind.
|
Dieser Kernel benötigt keine VGA-Karte und nutzt als Standardkonsole die erste serielle Schnittstelle. Dadurch ist es möglich, dass man diese Schnittstelle als Wartungskonsole nutzen kann. Über diese ist auch ein direkter Filetransfer über das zmodem-Protokoll möglich, in dem man das Programm rz aufruft und mittels eines Terminalprogramms (z.B. kermit) Dateien überträgt.
|
Als Vorteil des miniPC ist festzuhalten, dass er durch sein kompaktes Gehäuse eine einfache Montage an DIN-Schinen (,,Hut-Schinen'') zulässt und sich einfach austauschen lässt. Jedoch ist die Installation eines Betriebssystem auf dem miniPC nicht einfach, falls kein Ethernetmodul eingebaut ist. Zur Installation müsste man von Diskette booten und über PPP (serieller Anschluß) ein Grundsystem installieren. Alternativ könnte man das CPU-Modul des miniPC ausbauen, was sehr aufwändig ist und mittels eines Evalutaionsboards ein Grundsystem einspielen.
Vorteilhafter für die Wartung und Installation ist ein Board, auf welches man eine ATA-Flash-Disk (Solid State Disk) stecken kann. Diese Flash-Disks haben einen 2,5''-IDE-Anschluss, welchen man mittels eines Adapters einfach an einen normalen PC anschließen und somit ein Grundsystem installieren könnte. Solch eine IDE-Schnittstelle hat der Biscuit PC, welcher mir zur Verfügung stand. Dieser hat zusätzlich auch noch einen PC/104-Bus, über den man einfach beliebige Erweiterungskarten stecken kann. Darüber hinaus ist ein embedded PC auf Einplatinenbasis kostengünstiger als das Konzept des miniPC.
Um diese Informationen im Betriebsystem auslesen zu können, wurde ein Kernelmodul geschrieben, welches diese Daten auswertet und im /proc-Filesystem dem Administrator zugänglich macht.
Dabei wird das Clock-Signal des Magnetkartenleser an Pin 10 der parallelen Schnittstelle angeschlossen, welche bei einer steigenden Flanke einen Interrupt ausgelöst . Im Kernelmodul wird bei dieser Unterbrechung das Daten-Signal (Pin 12) ausgewertet, und anhand der Spezifikation des Magnetkartenlesers decodiert. Die gelesenen Daten werden über ein File im /proc-Filesystem (/proc/card) bereitgestellt und können mittels Filesystemfunktionen ausgelesen werden. Solange keine Daten anliegen, blockiert die Leseoperation. Im Fehlerfall wird das Schlüsselwort ,,ERROR'' zurückgegeben.
Ein wichtiger Punkt, welcher in dieser Arbeit noch nicht angesprochen worden ist, ist die Frage wie realisiert wird, dass sowohl alte Magnetkarten als auch neue Magnetkarten mit einer erweiterten Identifikationsnummer im neuen Zugangssystem funktionstüchtig sind. Dies stellt jedoch kein Problem dar, da in der Zuordnung von Magnetkarten-IDs zu Nutzer (siehe Dienst mcard) keine Beschränkungen existieren. Die alten Magnetkarten liefern eine 10-Stellige ID zurück und werden im Zugangssystem als solche verarbeitet. Von der MoUSe geschriebene neue Magnetkarten-IDs können beliebig lang sein und werden als längere Zahl interpretiert.
Das Tür-Relais wird mit 12 Volt durch die interne Stromversorgung der Steuereinheiten gespeist und durch ein elektronisches Lastrelais gesteuert, welches an einem Ausgang der parallelen Schnittstelle angeschlossen ist.
Zusammenfassen ist festzustellen, dass ein Austausch der Controller-Box bei gleichzeitiger Nutzung vorhandene externer Hardware (Magnetkartenleser, Tür-Relais, RS485-Bus, Tür-Sensor) möglich ist.