


\section{I/O-Hardware und ihre Treiber}
Computer speichern und verarbeiten Daten durch eine Vielzahl an 
unterschiedlichen Peripherie\-geräten. Dazu gehören typischerweise
Massenspeicher, wie Festplatten oder Mag\-net\-band\-laufwerke,
Netzwerkkarten, serielle und \index{Schnittstellen!parallele}parallele 
Schnittstellen, \index{Drucker}Drucker, \index{Terminals!serielle}serielle
Terminals, \index{Modems}Modems und viele weitere.

Die meisten der Unwägbarkeiten im Umgang mit der Hardware werden vor
dem Benutzer durch das Dateisystem oder ähnliche Schnittstellen
versteckt. Das I/O-Subsystem versteckt diese Unterschiede auch vor den
oberen Schichten des \index{Kernel}Kernels und bietet so einen einheitlichen 
Zugriff z.B. auf Dateien oder Laufwerke.

Um der Komplexität grundverschiedener Geräte Herr zu werden, wird für 
jedes Gerät ein Modul,
oder ein Gerätetreiber geschaffen, der alle Informationen enthält, die zum
Zugriff auf die entsprechende Hardware benötigt werden. Der
Gerätetreiber kennt die Details der verwendeten Hardware (also
Adressen der Kontrollregister, Anordnung der Bits in den Registern,
das Format von Kommandos oder Fehlermeldungen etc.).

Auf der anderen Seite stellt er eine einheitliche Schnittstelle zum Rest des
Betriebssystems zur Verfügung. Mit dieser lässt sich jedes
angeschlossene Gerät mit den prinzipiell gleichen Befehlen
ansprechen (öffnen, lesen, schreiben, schließen).


                                % Hardware

\subsection{Hardware}

Geräte kommunizieren mit dem System in der Regel über einen \emph{Port}
(beispielsweise den \index{Port!seriell}seriellen Port). Falls sich mehrere 
Geräte einen Kabelstrang teilen, so nennt man diesen \index{Bus}\emph{Bus}. Formal 
bezeichnet ein Bus nicht nur den Kabelstrang, sondern auch das Protokoll, mit dem die
Geräte angesprochen werden. 

Busse sind in der Computerarchitektur weit verbreitet. Beispiele sind
der \emph{PCI-Bus} oder der \emph{SCSI-Bus (SCSI= Small Computer Systems Interface)}. 

Controller sind elektronische Bauteile, die einen Port, einen Bus oder
ein Gerät steuern können. 

Einfache Controller, wie der für \index{Port!seriell}serielle
oder \index{Port!parallel}parallele Ports bestehen aus einem einzigen Bauteil, 
das die Signale des Ports kontrolliert. Im Gegensatz dazu bestehen
z.B. \index{Controller!SCSI-}SCSI-Controller oft aus eigenem Prozessor, Microcode, und
Speicherbausteinen, um die aufwendigen Nachrichten des SCSI-Protokolls
verarbeiten zu können.  

Wie gibt der Prozessor dem Controller die Anweisungen und wie übergibt er 
die Daten? Dafür hat der Controller ein oder mehrere Register für Daten und
Anweisungen. Der Prozessor kommuniziert mit dem Controller, indem er
Bitmuster in diese Register schreibt und von dort liest. 
Eine Möglichkeit der Kommunikation besteht darin, eine spezielle I/O-Anweisung 
zu benutzen, die den Transfer eines Bytes an eine spezielle 
I/O-Port-Adresse spezifiziert.
Alternativ kann der Controller Memory-Mapping unterstützen. In diesem Fall 
wird der Controller angesprochen, indem man direkt in den von ihm
beanspruchten Speicherbereich schreibt. Mit dieser Methode geht das
Schreiben und Lesen großer Datenmengen natürlich schneller als bei der
ersten Methode, da der Aufwand einige Millonen Bytes zu schreiben
wesentlich geringer ist, als einige Milionen I/O-Anweisungen zu
geben. Allerdings ist hier das Risiko größer, in einen falschen
Speicherbereich zu schreiben, geschützter Speicher hilft natürlich
dagegen. 

Typischerweise besteht der I/O-Port aus vier Registern: \emph{ Status,
  Kontrolle,  
Daten-ein und Daten-aus}. Das \emph{Statusregister} enthält Bits, die vom Host 
gelesen werden können. Diese Bits geben an, ob ein Byte zum Lesen im
Daten-ein-Register bereitsteht oder ob ein Fehler vorliegt. Ins
\emph{Kontrollregister} kann  
vom Host geschrieben werden, wenn ein Befehl ausgeführt werden soll, oder um 
den Zustand des Gerätes zu ändern.
So kann z.B. ein Bit im Kontrollregister der seriellen Schnittstelle
zwischen Voll-Duplex und Halb-Duplex-Kommunikation unterscheiden. 
Ein weiteres ermöglicht eine Paritätsprüfung, setzt die Wortlänge auf 7 
oder 8 Bit oder bestimmt die Geschwindigkeit der Schnittstelle.    
Das \emph{Daten-ein}-Register wird vom Host gelesen, um Eingaben zu erhalten, 
während auf das \emph{Daten-aus}-Register geschrieben wird, um Ausgaben zu 
erzeugen. 

Das exakte Protokoll für die Kommunikation zwischen Controller und Host wäre
zu aufwendig, aber die Grundzüge des Handshakes sind schnell
erklärt. Man geht davon aus, dass zwei Bits benutzt werden, um das 
Erzeuger-Verbraucher-Verhältnis zwischen Host und Controller zu bestimmen.
Der Controller zeigt seinen Zustand über das busy-Bit im Statusregister an.
Der Controller setzt dieses busy-Bit, wenn er arbeitet und entfernt dieses,
wenn er fertig ist. Der Host signalisiert seine Wünsche, indem er ein Bit ins
Befehlsregister setzt, das anzeigt, dass ein Befehl zur Bearbeitung ansteht.
  
\cite{Silb98}, Kap. 12.2.
                                %Gerätetreiber

\subsection{Gerätetreiber}

\subsubsection{Aufbau}

Gerätetreiber kann man grundsätzlich in drei Teile aufteilen:

                                % trivlist ist die einfachste
                                % Listenumgebung, geht auch mit
                                % automat. Zähler.
\begin{trivlist}
\item 1. Routinen zum Konfigurieren und Initialisieren einer Device.

\item 2. Routinen, die I/O-Anfragen bedienen.

\item 3. Routinen, um Interrupts zu bedienen.
\end{trivlist}

Die \emph{Initialisierungsschicht} dient dazu, herauszufinden ob die Hardware
an\-wesend und funktionsfähig ist, sowie sie zu testen und zu
initialisieren.

Zusätzlich kann eine Routine vorhanden sein, die im Falle eines
unbehebbaren Systemfehlers den \emph{Speicherinhalt auf die Festplatte} schreibt,
um ihn später zu unter\-suchen.
 
                                % cite ist ein Hinweis auf den
                                % Literaturindex 

\cite{McKu96}, Kap 6.1 \emph{Device Drivers}.

\subsubsection{monolithische Kernel}

Viele Unixsysteme benutzen sog. monolithische Kernel. In diesen sind
alle Treiber für Hardware fest integriert und laufen im geschützten
Modus (Kernelspace). Veränderte Treiber werden in der Regel durch
Neukompilieren des Kernels und Rebooten eingebunden. Da keine
Kommunikation zwischen verschiedenen Prozes\-sen notwendig ist, ist
dieser Aufbau momentan der leistungsfähigste.

Mittlerweile haben die meisten Unices ladbare Kernelmodule, die
einzeln angepasst und kompiliert werden können. Dies erlaubt eine hohe
Anpassbarkeit, ohne Geschwindigkeitsvorteile aufzugeben. Ausserdem
blockieren unbenutzte Kernelmodule keinen Arbeitsspeicher, was bei
Systemen mit geringem Speicher\-ausbau sehr vorteilhaft ist Systeme mit
ladbaren Modulen sind \emph{Linux} (ab 2.0.x), die meisten \emph{BSD-Systeme}
(z.B. FreeBSD ab 2.2.x oder SunOS ab 4.x).



\subsubsection{Microkernel}

Seit Anfang der neunziger als der Weg in die Zukunft gepriesen,
verwaltet bei Microkernelsystemen der eigentliche Kernel nur die
grundlegendsten Funktionen von Speicher- und Prozessverwaltung, sowie
Scheduling. Hardwaretreiber sind als eigene Programme ausgeführt, die
wie normale Anwendungen außerhalb des Speicherbereiches des Kernels arbeiten.

Man unterscheidet zwischen zwei Arten von Microkernelsystemen:
solchen, die für jeden Dienst einen dezidierten Server einsetzen, sowie
denen, die ein einzigen Server für alle benötigten Dienste benutzen. 

Systeme des ersten Typs beinhalten \emph{MachUS, BeOS oder QNX}. Der zweite
Typ kommt bei \emph{MkLinux} (ein Mach-Kernel mit Linux-Personality)
sowie \emph{Darwin/\-MacOS X} (ein Mach-Kernel mit 4.4BSD-Personality) zum Einsatz.
