Galileo Computing < openbook > Galileo Computing - Professionelle Bücher. Auch für Einsteiger.
Professionelle Bücher. Auch für Einsteiger.

Inhaltsverzeichnis
Vorwort
1 Einleitung
2 Die Installation
3 Erste Schritte
4 Linux als Workstation für Einsteiger
5 Der Kernel
6 Die Grundlagen aus Anwendersicht
7 Die Shell
8 Reguläre Ausdrücke
9 Konsolentools
10 Die Editoren
11 Shellskriptprogrammierung mit der bash
12 Die C-Shell
13 Benutzerverwaltung
14 Grundlegende Verwaltungsaufgaben
15 Netzwerkgrundlagen
16 Anwendersoftware für das Netzwerk
17 Netzwerkdienste
18 Mailserver unter Linux
19 LAMP
20 DNS-Server
21 Secure Shell
22 Die grafische Oberfläche
23 Window-Manager und Desktops
24 X11-Programme
25 Multimedia und Spiele
26 Prozesse und IPC
27 Bootstrap und Shutdown
28 Dateisysteme
29 Virtualisierung und Emulatoren
30 Softwareentwicklung
31 Crashkurs in C und Perl
32 Einführung in die Sicherheit
33 Netzwerksicherheit überwachen
A Lösungen zu den einzelnen Aufgaben
B Kommandoreferenz
C X11-InputDevices
D MBR
E Die Buch-DVDs
F Glossar
G Literatur
Stichwort

Download:
- ZIP, ca. 15,7 MB
Buch bestellen
Ihre Meinung?

Spacer
 <<   zurück
Linux von Johannes Plötner, Steffen Wendzel
Das umfassende Handbuch
Buch: Linux

Linux
geb., mit 2 DVDs
1302 S., 39,90 Euro
Galileo Computing
ISBN 978-3-8362-1704-0
Pfeil 5 Der Kernel
  Pfeil 5.1 Grundlagen
    Pfeil 5.1.1 Der Prozessor
    Pfeil 5.1.2 Der Speicher
    Pfeil 5.1.3 Fairness und Schutz
    Pfeil 5.1.4 Die Programmierung
    Pfeil 5.1.5 Die Benutzung
  Pfeil 5.2 Aufgaben eines Betriebssystems
    Pfeil 5.2.1 Abstraktion
    Pfeil 5.2.2 Virtualisierung
    Pfeil 5.2.3 Ressourcenverwaltung
  Pfeil 5.3 Prozesse, Tasks und Threads
    Pfeil 5.3.1 Definitionen
    Pfeil 5.3.2 Lebenszyklen eines Prozesses
    Pfeil 5.3.3 Die Implementierung
  Pfeil 5.4 Das Speichermanagement
    Pfeil 5.4.1 Das Paging
    Pfeil 5.4.2 Die Hardware
    Pfeil 5.4.3 Die Organisation des Adressraums
  Pfeil 5.5 Eingabe und Ausgabe
    Pfeil 5.5.1 Hardware und Treiber
    Pfeil 5.5.2 Interaktion mit Geräten
    Pfeil 5.5.3 Ein-/Ausgabe für Benutzerprogramme
    Pfeil 5.5.4 Das Dateisystem
  Pfeil 5.6 Zusammenfassung
  Pfeil 5.7 Aufgaben


Galileo Computing - Zum Seitenanfang

5.5 Eingabe und Ausgabe  Zur nächsten ÜberschriftZur vorigen Überschrift

Kommen wir nun zur Ein- und Ausgabe, einer der wichtigsten Funktionen eines Betriebssystems. Wir wollen diese dabei einmal prinzipiell und einmal konkret am Beispiel des Dateisystems behandeln.

Abbildung 5.7  Der Anteil des Treibercodes an Kernel 2.6.10

Die Grafik in Abbildung 5.7 verdeutlicht bereits, wie wichtig das Ein-/Ausgabe-Sub- system des Kernels ist. Mittlerweile besteht fast die Hälfte des Kernel-Codes aus Quelldateien für verschiedenste Treiber. Im Folgenden wollen wir zunächst erklären, was Sie sich unter dem Begriff Treiber vorstellen können.


Galileo Computing - Zum Seitenanfang

5.5.1 Hardware und Treiber  Zur nächsten ÜberschriftZur vorigen Überschrift

Damit ein Gerät angesprochen werden kann, muss man »seine Sprache sprechen«. Man muss genau definierte Daten in spezielle Hardwareregister oder auch Speicherstellen laden, um bestimmte Effekte zu erzielen. Daten werden hin und her übertragen, und am Ende druckt ein Drucker eine Textdatei oder man liest Daten von einer CD.

Schnittstelle

Zur Übersetzung einer Schnittstelle zwischen den Benutzerprogrammen, die einen CD-Brenner unterstützen, und den eventuell von Hersteller zu Hersteller unterschiedlichen Hardwareschnittstellen, benötigt man Treiber. Für die Anwenderprogramme werden die Geräte unter Unix als Dateien visualisiert. Als solche können sie natürlich von Programmen geöffnet und benutzt werden: Man sendet und empfängt Daten über Syscalls. Wie die zu sendenden Steuerdaten genau auszusehen haben, ist von Gerät zu Gerät unterschiedlich.

Auch das vom Treiber bereitgestellte Interface kann sich von Gerät zu Gerät unterscheiden. Bei USB-Sticks oder CD-ROM-Laufwerken wird das Interface sicherlich so beschaffen sein, dass man die Geräte leicht in das Dateisystem integrieren und auf die entsprechenden Dateien und Verzeichnisse zugreifen kann. Bei einem Drucker jedoch möchte man dem Gerät die zu druckenden Daten schicken; das Interface wird also eine völlig andere Struktur haben. Auch kann ein Gerät durch verschiedene Treiber durchaus mehrere Schnittstellen anbieten: Eine Festplatte kann man sowohl über eine Art Dateisystem-Interface als auch direkt über das Interface des IDE-Treibers ansprechen.

Module

Die meisten Treiber sind unter Linux als Module realisiert. Solche Module werden zur Laufzeit in den Kernel eingebunden und stellen dort dann eine bestimmte Funktionalität zur Verfügung. Dazu sind aber einige Voraussetzungen zu erfüllen:

  • Interface
    Der Kernel muss ein Interface anbieten, über das Module erst einmal geladen werden können. Einmal geladen, müssen die Module auch irgendwie in den Kernel integriert werden können.
  • Sicherheit
    Lädt man externe Komponenten in den Kernel, so bedeutet dies immer ein Sicherheitsrisiko in doppelter Hinsicht. Einerseits könnten schlecht programmierte Treiber das ganze System zum Absturz bringen und andererseits könnten Hacker durch spezielle Module versuchen, den Kernel zu manipulieren.
  • Gerätemanagement
    Ein Modul beziehungsweise ein Treiber muss beim Laden mitteilen können: Ich bin jetzt für dieses oder jenes Gerät verantwortlich. Vielleicht muss mancher Treiber auch erst erkennen, ob und wie viele vom Treiber unterstützte Geräte angeschlossen sind.

Was aber wäre die Alternative zu Treibern in Modulform? Treiber müssen teilweise privilegierte Befehle zur Kommunikation mit den zu steuernden Geräten nutzen, daher müssen sie zumindest zum großen Teil im Kernel-Mode ablaufen. Und wenn man sie nicht zur Laufzeit in den Kernel laden kann, müssten sie schon von Anfang an in den Kernel-Code integriert sein.

Modulare versus statische Integration

Würde man jedoch alle verfügbaren Treiber »ab Werk« direkt in den Kernel kompilieren, wäre der Kernel sehr groß und damit langsam sowie speicherfressend. Daher sind die meisten Distributionen dazu übergegangen, ihre Kernel mit in Modulform kompilierten Treibern auszuliefern. Der Benutzer kann dann alle Module laden, die er braucht – oder das System erledigt diese Aufgabe automatisch für ihn. [Wie man selbst Module lädt und das System so konfiguriert, dass es dies automatisch tut, erfahren Sie in Kapitel 14, »Grundlegende Verwaltungsaufgaben«.]

Zeichenorientierte Treiber

Treiber müssen ins System eingebunden werden, mit anderen Worten: Man benötigt eine einigermaßen uniforme Schnittstelle. Aber kann man zum Beispiel eine USB-Webcam und eine Festplatte in ein einheitliches und trotzdem konsistentes Muster bringen? Nun ja, Unix hat es zumindest versucht. Es unterscheidet zwischen zeichenorientierten und blockorientierten Geräten und klassifiziert damit auch die Treiber entsprechend. Der Unterschied ist dabei relativ simpel und doch signifikant:


Ein zeichenorientiertes Gerät sendet und empfängt Daten direkt von Benutzerprogrammen.


Keine Pufferung

Der Name der zeichenorientierten Geräte leitet sich von der Eigenschaft bestimmter serieller Schnittstellen ab, nur jeweils ein Zeichen während einer Zeiteinheit übertragen zu können. Diese Zeichen konnten nun aber direkt – also ohne Pufferung – gesendet und empfangen werden. Eine weitere wichtige Eigenschaft ist die, dass auf Daten im Allgemeinen nicht wahlfrei zugegriffen werden kann. Man muss eben mit den Zeichen vorlieb nehmen, die gerade an der Schnittstelle anliegen.

Blockorientierte Treiber

Bei blockorientierten Geräten werden im Unterschied dazu meist ganze Datenblöcke auf einmal übertragen. Der klassische Vertreter dieser Gattung ist die Festplatte, bei der auch nur eine blockweise Übertragung der Daten sinnvoll ist. Der Lesevorgang bestimmter Daten gliedert sich nämlich in diese Schritte:

  1. Aus der Blocknummer – einer Art Adresse – wird die physikalische Position der Daten ermittelt.

  2. Der Lesekopf der Platte bewegt sich zur entsprechenden Stelle.

  3. Im Mittel muss nun eine halbe Umdrehung gewartet werden, bis die Daten am Lesekopf anliegen.

  4. Der Lesekopf liest die Daten.

Mehrere Daten auf einmal

Die meiste Zeit braucht nun aber die Positionierung des Lesekopfs, denn wenn die Daten einmal am Lesekopf anliegen, geht das Einlesen sehr schnell. Mit anderen Worten: Es ist für eine Festplatte praktisch, mit einem Zugriff gleich mehrere Daten – zum Beispiel 512 Bytes – zu lesen, da die zeitaufwendige Positionierung dann eben nur einmal statt 512-mal erfolgen muss.


Blockorientierte Geräte haben die gemeinsame Eigenschaft, dass die übertragenen Daten gepuffert werden. Außerdem kann auf die gespeicherten Blöcke wahlfrei, also in beliebiger Reihenfolge, zugegriffen werden. Darüber hinaus können Datenblöcke mehrfach gelesen werden.


Bei einer Festplatte hat diese Tatsache nun gewisse Vorteile wie auch Nachteile: Während des Arbeitens bringen zum Beispiel Schreib- und Lesepuffer eine hohe Performance. Wenn ein Benutzer die ersten Bytes einer Datei lesen möchte, kann man schließlich auch gleich ein Readahead machen und die darauf folgenden Daten schon einmal vorsichtshalber im Hauptspeicher puffern. Dort können sie dann ohne Zeitverzug abgerufen werden, wenn ein Programm – was ziemlich wahrscheinlich ist – in der Datei weiterlesen will. Will es das nicht, gibt man den Puffer nach einiger Zeit wieder frei.

Schreibpuffer

Beim Schreibpuffer sieht das Ganze ähnlich aus: Um performanter zu arbeiten, werden Schreibzugriffe in der Regel nicht sofort, sondern erst in Zeiten geringer Systemauslastung ausgeführt. Wenn ein System nun aber nicht ordnungsgemäß heruntergefahren wird, kann es zu Datenverlusten bei eigentlich schon getätigten Schreibzugriffen kommen. Wenn diese nämlich ausgeführt und in den Puffer, aber eben noch nicht auf die Platte geschrieben wurden, sind die Daten weg.

Ein interessantes Beispiel für die Semantik dieser Treiber ist eine USB-Festplatte. Es handelt sich bei diesem Gerät schließlich um eine blockorientierte Festplatte, die über einen seriellen, zeichenorientierten Anschluss mit dem System verbunden ist. Sinnvollerweise wird die Funktionalität der Festplatte über einen blockorientierten Treiber angesprochen, der aber intern wiederum über den USB-Anschluss und damit über einen zeichenorientierten Treiber die einzelnen Daten an die Platte schickt beziehungsweise von ihr liest.

Der wahlfreie Zugriff auf die Datenblöcke der Festplatte wird also über die am seriellen USB-Anschluss übertragenen Daten erledigt. Der Blocktreiber nutzt eine bestimmte Sprache zur Ansteuerung des Geräts und der zeichenorientierte USB-Treiber überträgt dann die »Worte« dieser Sprache und gegebenenfalls zu lesende oder zu schreibende Daten.


Galileo Computing - Zum Seitenanfang

5.5.2 Interaktion mit Geräten  Zur nächsten ÜberschriftZur vorigen Überschrift

Da wir im letzten Abschnitt die unterschiedlichen Treiber allgemein beschrieben haben, wollen wir im Folgenden den Zugriff auf diese Treiber aus dem Userspace heraus betrachten und dabei den internen Aufbau der Treiber analysieren.

Gehen wir also wieder ein paar Schritte zurück und führen wir uns vor Augen, dass die Geräte unter Linux allesamt als Dateien unterhalb des /dev-Verzeichnisses repräsentiert sind. Die Frage ist nun, wie man diese Geräte und Ressourcen nutzen kann und wie der Treiber diese Nutzung unterstützt.

Den passenden Treiber finden

Major- und Minor-Nummern

Früher war die Sache relativ einfach: Jeder speziellen Gerätedatei unterhalb des /dev-Verzeichnisses war eine sogenannte Major- und eine Minor-Nummer zugeordnet. Anhand der Major-Nummer konnte festgestellt werden, welcher Treiber für diese spezielle Gerätedatei zuständig war. Die Minor-Nummer stellte für den Treiber selbst eine Hilfe dar, um festzustellen, welches Gerät nun anzusprechen war – schließlich war es gut möglich, dass in einem System zwei baugleiche Komponenten wie beispielsweise Festplatten verwendet wurden, die zwar vom selben Treiber bedient, aber trotzdem unterschieden werden mussten.

Später dachte man sich, dass man die Geräte doch nicht über statische Nummern identifizieren, sondern stattdessen eine namensbasierte Identifizierung verwenden sollte – das Dateisystem devfs war geboren. Der Treiber musste nun beim Laden nicht mehr angeben, welche Major-Nummer er bediente, sondern er registrierte sozusagen den »Namen« des Geräts. Das geschah im Modulcode recht einfach:

#include <linux/devfs_fs_kernel.h>
...
static int __init treiber_init(void)
{
...
/* Ist dieses Gerät schon registriert? */
if(register_chrdev(4, "Treibername", &fops) == 0)
{
/* Können wir uns registrieren? */
if(devfs_mk_cdev( MKDEV(4,64),
S_IFCHR | S_IRUGO | S_IWUGO, "vc/ttyS%d", 0 ))
// Wenn nein, dann Fehlermeldung ausgeben
printk( KERN_ERR "Integration fehlgeschlagen.\n");
}
...
}

Listing 5.23  So wurde beim devfs ein Gerät registriert

Ein Irrweg

In diesem Beispiel wurde das zeichenorientierte Gerät ttyS0 über die Funktion devfs_mk_cdev [Für blockorientierte Geräte gibt es einen entsprechenden anderen Befehl, der auch mit richtigen Parametern – S_IFCHR steht im Beispiel für zeichenorientierte Geräte – aufgerufen werden muss.] im Verzeichnis vc angelegt. Das devfs hat jedoch nicht zu unterschätzende Nachteile, daher wird es vom 2.6er Linux zwar noch unterstützt, ist aber als deprecated und damit als nicht mehr unterstützt gekennzeichnet. Die Nachteile sind dabei unter anderem:

  • Die Implementierung des devfs ist sehr umfangreich und damit schlecht skalierbar, außerdem wird der Code als nicht besonders gut angesehen.
  • Die Gerätedateien haben im devfs neue Namen, die nicht mehr standardkonform sind.
  • Nicht alle Treiber funktionieren für das devfs.
  • Die Methode, die Zugriffsrechte für eine von einem Treiber erstellte Datei zu setzen, ist sehr umständlich.

Man musste also wieder eine neue Lösung finden und kehrte schließlich zu einer Identifizierung über Nummern zurück. Jedoch warf man eine der Altlasten von Unix – die Beschränkung auf jeweils 8 Bit für die Major- und Minor-Nummer – über Bord und führte mit Kernel 2.6 die 32-Bit-lange Gerätenummer ein. Natürlich kann man, wie im Beispiel gesehen, von den bekannten Major- und Minor-Nummern mittels des MKDEV(major,minor)-Makros diese Nummern auf den 32-Bit-Wert der Gerätenummer abbilden.

Also musste wieder etwas Neues her. Im Zuge der Weiterentwicklung des Powermanagements kam den Entwicklern eine andere Art der Geräteverwaltung in den Sinn: die Verwaltung in Form eines Baumes, der die Zusammenhänge des Prozessors mit den Controller-Bausteinen verschiedener Bussysteme und schließlich mit der über diese Bussysteme angebundenen Peripherie abbildet. Das heißt nichts anderes, als dass ein Treiber wissen muss, ob sein Gerät zum Beispiel am PCI- oder USB-Bus hängt. Für das Powermanagement ist das insofern wichtig, als zum Beispiel der PCI-Bus erst nach der Deaktivierung des letzten PCI-Geräts heruntergefahren werden sollte.

Das sysfs und udev

Visualisieren kann man sich diese Struktur über das sysfs, ein virtuelles, also nicht irgendwo auf einem Medium abgelegtes, sondern vielmehr dynamisch generiertes Dateisystem. Dieses spezielle Dateisystem muss erst gemountet werden, bevor man die zahlreichen Daten auslesen kann:

# mount -t sysfs sysfs /sys
# ls /sys/*
/sys/block:
fd0  hdb  hdd   ram1   ram11  ram13  ram15  ram3  ...
hda  hdc  ram0  ram10  ram12  ram14  ram2   ram4  ...
/sys/bus:
ide  pci  platform  pnp  usb
/sys/class:
graphics  input  mem  misc  net  nvidia  pci_bus  ...
/sys/devices:
pci0000:00  platform  pnp0  pnp1  system
/sys/firmware:
acpi
/sys/module:
8139too      commoncap  ide_disk     nvidia       ...
af_packet    dm_mod     ide_floppy   ppp_generic  ...
agpgart      ext3       ide_generic  pppoe        ...
...          ...        ...          ...          ...
/sys/power:
state

Listing 5.24  Das Dateisystem sysfs mounten und anzeigen

An diesem Beispiel kann man schon erkennen, dass das sysfs alle wichtigen Informationen über geladene Module, Geräteklassen und Bussysteme enthält. Ein Gerät kann im sysfs also durchaus mehrfach auftauchen, eine Netzwerkkarte würde zum Beispiel unterhalb des /sys/pci-Verzeichnisses und unterhalb der Geräteklasse /sys/net erscheinen.

udev

Mittlerweile werden die Einträge im /dev-Verzeichnis mit dem Programm udev dynamisch erzeugt, das auf Hotplug-Ereignisse reagiert. udev arbeitet mit sysfs zusammen und ist als Nachfolger des devfs zu betrachten. Im Gegensatz zum devfs-Dateisystem ist udev nicht im Kernel implementiert, sondern läuft im Userspace. Zudem kann udev vom Administrator über UDEV-Regeln konfiguriert werden.

Intelligenterweise muss man sich als Treiberprogrammierer in den seltensten Fällen mit dem Eintrag seines Geräts ins sysfs beschäftigen, Ausnahmefälle wären aber zum Beispiel eine neue Geräteklasse oder besondere Powermanagement-Funktionen.


Einen besonders einfachen Fall wollen wir hier noch einmal kurz zeigen: Ein zeichenorientiertes Gerät mit der Major-Nummer 62 soll ins System integriert werden.


#include <linux/fs.h>
static struct file_operations fops;
int init_driver(void) {
if(register_chrdev(62, "NeuerTreiber", &fops) == 0)
// Treiber erfolgreich angemeldet
return 0;
 // Ansonsten: Anmeldung fehlgeschlagen
return –1;
}

Listing 5.25  Ein Gerät registrieren

Hier geben wir wieder nur eine Major-Nummer an, denn aus dieser kann der Kernel eine gültige Gerätenummer generieren. Ist diese Major-Nummer schon vergeben, wird das Registrieren des Geräts unweigerlich fehlschlagen. Jedoch kann man sich auch über die spezielle Major-Nummer »0« einfach eine beliebige freie Nummer zuweisen lassen. Mit der Zeichenkette »NeuerTreiber« identifiziert sich der Treiber im System, taucht unter dieser Bezeichnung im sysfs auf und kann sich mit dieser Kennung natürlich auch wieder abmelden.

Auf das Gerät zugreifen

I/O-Syscalls

Geräte sind also Dateien, auf die man im Wesentlichen mit den üblichen Syscalls [Bei der Kommunikation mit Gerätedateien werden die C-Funktionen fopen(), fprintf() usw. in der Regel nicht verwendet. Zwar greifen diese Funktionen intern auch auf die Syscalls zurück, allerdings wird standardmäßig die gepufferte Ein-/Ausgabe benutzt, was im Regelfall für die Kommunikation mit Geräten nicht ideal ist.] zur Dateibearbeitung zugreifen wird:

  • open() – öffnet eine Datei (dies ist notwendig, um in diese zu schreiben und aus ihr zu lesen)
  • write() – schreibt in eine geöffnete Datei
  • read() – liest aus einer geöffneten Datei
  • close() – schließt eine geöffnete Datei
  • lseek() – ändert den Schreib-/Lesezeiger einer geöffneten Datei, also die Stelle in einer Datei, an der ein Programm arbeitet
  • ioctl() – bietet ausführliche Funktionen zur Gerätesteuerung

Callbacks

Diese Schnittstellen müssen nun natürlich vom Treiber als Callbacks bereitgestellt werden. Callbacks sind Funktionen, die genau dann aufgerufen werden, wenn ein entsprechender Event – in diesem Fall die Benutzung des entsprechenden Syscalls auf eine Gerätedatei – auftritt.

Wenn eine Applikation also mittels open() eine Gerätedatei öffnet, stellt der Kernel den zugehörigen Treiber anhand der bereits besprochenen Major/Minor- beziehungsweise der Gerätenummer fest. Danach erstellt der Kernel im Prozesskontext eine Datenstruktur vom Typ struct file, in der sämtliche Optionen des Dateizugriffs wie die Einstellung für blockierende oder nichtblockierende Ein-/Ausgabe oder natürlich auch die Informationen zur geöffneten Datei gespeichert werden.

Callbacks

Als Nächstes wird beim Treiber der in der file_operations-Struktur vermerkte Callback für den open()-Syscall ausgerufen, dem unter anderem eine Referenz dieser file-Struktur übergeben wird. Anhand dieser Referenz wird auch bei allen anderen Callbacks die Treiberinstanz referenziert.


Eine Treiberinstanz ist notwendig, da ein Treiber die Möglichkeit haben muss, sitzungsspezifische Daten zu speichern.


Solche Daten könnten zum Beispiel einen Zeiger umfassen, der die aktuelle Position in einem Datenstrom anzeigt. Dieser Zeiger muss natürlich pro geöffneter Datei eindeutig sein, selbst wenn ein Prozess ein Gerät mehrfach geöffnet hat.


Galileo Computing - Zum Seitenanfang

5.5.3 Ein-/Ausgabe für Benutzerprogramme  Zur nächsten ÜberschriftZur vorigen Überschrift

Für Benutzerprogramme spiegelt sich dieser Kontext im Deskriptor wider, der nach einem erfolgreichen open() als Rückgabewert an das aufrufende Programm übergeben wird:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main()
{
// Ein Deskriptor ist nur eine Identifikationsnummer
int fd;
char text[256];
 // Die Datei "test.c" lesend öffnen und den zurück-
// gegebenen Deskriptor der Variable fd zuweisen
fd = open( "test.c", O_RDONLY );
 // Aus der Datei unter Angabe des Deskriptors lesen
read( fd, text, 256 );
 // "text" verarbeiten
// Danach die Datei schließen
close( fd );
 return 0;
}

Listing 5.26  Einen Deskriptor benutzen

ioctl()

Ein wichtiger Syscall im Zusammenhang mit der Ein-/Ausgabe auf Gerätedateien ist ioctl() (I/O-Control). Über diesen Syscall können alle Funktionalitäten abgebildet werden, die nicht in das standardisierte Interface eingebaut werden können.


Galileo Computing - Zum Seitenanfang

5.5.4 Das Dateisystem  topZur vorigen Überschrift

Ein besonderer Fall der Ein-/Ausgabe ist das Dateisystem, das wir im Folgenden näher behandeln wollen. Eigentlich müssen wir zwischen »Dateisystem« und »Dateisystem« unterscheiden, da Unix mehrere Schichten für die Interaktion mit Dateien benutzt.

Der VFS-Layer

Die oberste Schicht des Dateisystems ist der sogenannte VFS-Layer (engl. virtual filesystem). Das virtuelle Dateisystem ist eine Schnittstelle, die von den physikalischen Dateisystemen die grundlegenden Funktionen beim Umgang mit Dateien abstrahiert:

Treiberarbeit

  • open() und close()
    Wie Sie schon beim Umgang mit Treibern und Geräten gesehen haben, ist die Möglichkeit zum Öffnen und Schließen von Dateien essenziell. Mit dieser Architektur setzt das VFS jedoch eine zustandsbasierte Funktionsweise des Dateisystems voraus. Beim Netzwerkdateisystem NFS ist dies aber nicht gegeben: Dort gibt es keine open()- oder close()-Aufrufe, stattdessen müssen bei jedem lesenden oder schreibenden Zugriff der Dateiname sowie die Position innerhalb der Datei angegeben werden. Damit ein NFS-Dateisystem von einem entfernten Server nun in das VFS integriert werden kann, muss der lokale Treiber sich den jeweiligen Zustand einer geöffneten Datei merken und bei jedem Zugriff in die Anfragen für den NFS-Server übersetzen.
  • read() und write()
    Hat man eine Datei einmal geöffnet, kann man über einen Deskriptor Daten von der aktuellen Position in der Datei lesen oder schreiben. Nachdem das VFS bereits beim open() festgestellt hat, zu welchem physikalischen Dateisystem ein Zugriff gehört, wird jeder read()- oder write()-Aufruf wieder direkt zum Treiber für das entsprechende Dateisystem weitergeleitet.
  • create() und unlink()
    Das VFS abstrahiert natürlich auch die Erstellung und das Löschen von Dateien. Die Erstellung wird dabei allerdings über den open()-Syscall abgewickelt.
  • readdir()
    Genauso muss auch ein Verzeichnis gelesen werden können. Schließlich ist die Art und Weise, wie ein Dateisystem auf einem Medium abgelegt ist, ebenfalls treiberspezifisch.

Der Benutzer beziehungsweise seine Programme greifen nun über solche uniformen Schnittstellen des VFS auf die Funktionen und Daten des physikalischen Dateisystems zu. Der Treiber des Dateisystems muss also entsprechende Schnittstellen anbieten, damit er in das VFS integriert werden kann.

Mehr Interna zu Dateisystemen finden Sie in Kapitel 28.

Mounting

Das Einbinden eines Dateisystems in das VFS nennt man Mounting. Eingebunden werden die Dateisysteme schließlich unterhalb von bestimmten Verzeichnissen, den sogenannten Mountpoints. Definiert wird das Ganze in einer Datei im Userspace, der Datei /etc/fstab:

# Partitionen
/dev/hda1  /           ext3     errors=remount-ro 0 1
/dev/hda3  /home       reiserfs defaults          0 0
/dev/hda4  none        swap     sw                0 0
# Wechselspeicher
/dev/fd0   /mnt/floppy auto     user,noauto       0 0
/dev/hdc   /mnt/dvd    iso9660  ro,user,noauto    0 0
/dev/hdd   /mnt/cdburn auto     ro,user,noauto    0 0
# virtuelle Dateisysteme
proc       /proc       proc     defaults          0 0

Listing 5.27  Eine /etc/fstab-Datei

Interessant sind für uns im Moment dabei vor allem die ersten beiden Spalten dieser Tabelle: Dort werden das Device sowie der Mountpoint angegeben, wo das auf dem Gerät befindliche Dateisystem eingehängt werden wird.

Option beim Booten

Besonders interessant ist an dieser Stelle das Root-Dateisystem /. Wie gesagt, befindet sich die /etc/fstab irgendwo auf dem Dateisystem, auf das man nur zugreifen kann, wenn man zumindest das Root-Dateisystem schon gemountet hat. Man hat also das klassische Henne-Ei-Problem, das nur gelöst werden kann, wenn der Kernel das Root-Dateisystem als Option beim Booten übergeben bekommt.

So kennen die Bootmanager lilo und grub eine Option root, mit der man dem zu bootenden Kernel sein Root-Dateisystem übergibt. Von diesem kann er dann die fstab lesen und alle weiteren Dateisysteme einbinden.



Ihr Kommentar

Wie hat Ihnen das <openbook> gefallen? Wir freuen uns immer über Ihre freundlichen und kritischen Rückmeldungen.






 <<   zurück
  Zum Katalog
Zum Katalog: Linux, Ausgabe 2011






Linux, Ausgabe 2011
Jetzt bestellen


 Ihre Meinung?
Wie hat Ihnen das <openbook> gefallen?
Ihre Meinung

 Buchempfehlungen
Zum Katalog: Linux-Server






 Linux-Server


Zum Katalog: Linux Hochverfügbarkeit






 Linux Hoch-
 verfügbarkeit


Zum Katalog: LPIC-1






 LPIC-1


Zum Katalog: Debian GNU/Linux






 Debian GNU/Linux


Zum Katalog: openSUSE 11.2






 openSUSE 11.2


Zum Katalog: Shell-Programmierung






 Shell-Programmierung


Zum Katalog: Ubuntu GNU/Linux






 Ubuntu GNU/Linux


 Shopping
Versandkostenfrei bestellen in Deutschland und Österreich
InfoInfo




Copyright © Galileo Press 2011
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.


[Galileo Computing]

Galileo Press, Rheinwerkallee 4, 53227 Bonn, Tel.: 0228.42150.0, Fax 0228.42150.77, info@galileo-press.de