I/O-Namespace

Dieses Kapitel beinhaltet die folgenden Themen:

Einführung

I/O-Namensraum (Namespace)

Die I/O- (Ein-/Ausgabe-) Ressourcen von QNX sind nicht in den Mikrokernel eingebaut. Stattdessen werden die I/O-Dienste durch Prozesse unterstützt, welche dynamisch gestartet werden können, während das System läuft. Da das QNX-Dateisystem optional ist, ist der Raum für Pfadnamen nicht in das Dateisystem, wie in den meisten monolithischen Systemen, eingebaut.

Präfixe und Regionen der Autorität

Mit QNX wird der Raum für Pfadnamen in Regionen mit Autoritäten eingeteilt. Jeder Prozeß, der dateiorientierte I/O-Dienste liefern will, wird ein Präfix mit dem Prozeß Manager definieren, welches den Teil des Namensraumes (Namespace), den er administrieren möchte, benennt (z. B. seine Region der Autorität). Diese Präfixe erstellen einen Präfixbaum, der im Speicher eines jeden QNX Knoten existiert.

Auflösen von Pfadnamen

I/O-Manager-Präfixe

Wenn ein Prozeß eine Datei öffnet, wird der Pfadname der Datei gegen den Präfixbaum geprüft, um die open() Funktion an den entsprechenden I/O-Ressourcen-Manager zu senden. Zum Beispiel registriert der zeichenorientierte Geräte-Manager (Dev) normalerweise das Präfix /dev. Wenn ein Prozeß open() mit einem /dev/xxx aufruft, wird eine Präfixerkennung von /dev eintreten und die Funktion open() wird zu Dev gelenkt (dem Besitzer).

Der Präfixbaum kann sich partiell überschneidende Regionen von Autorität beinhalten. In diesem Fall gewinnt der, welcher am besten passt. Nehmen Sie zum Beispiel an, wir haben drei Präfixe registriert:

/ ein plattenbasierendes Dateisystem (Fsys)
/dev ein zeichenorientiertes geräte-System (Dev)
/dev/hd0 ein rohe Festplatte (Fsys)

Der Dateisystem-Manager hat zwei Präfixe registriert, einen für ein angebundenes QNX-Dateisystem (z. B. /) und einen für eine physikalische Festplatte (z. B. /dev/hd0). Der zeichenorientierte Geräte- Manager hat nur einen Präfix registriert. Die folgende Tabelle illustriert die Regel der längsten passenden Übereinstimmung für Pfadnamenauflösungen.

Der Pfadname:paß auf:und wird bearbeitet von:
/dev/con1 /dev Dev
/dev/hd0 /dev/hd0 Fsys
/usr/dtdodge/test / Fsys

Der Präfixbaum wird wie eine durch Doppelpunkte getrennte Liste von Präfixen verwaltet:

Präfix=pid,unit:Präfix=pid,unit:Präfix=pid,unit

wobei pid die Prozeß ID des I/O-Ressourcen-Managers ist, und die unit Nummer ein einfaches Zeichen ist, welches von dem Manager benutzt wird, um eins von verschiedenen, ihm gehörenden Präfixen zu unterscheiden. Wenn Fsys Prozeß 3 sein sollte und Dev Prozeß 5 wäre, könnte der System-Präfixbaum, in obigem Beispiel, folgendermaßen aussehen:

/dev/hd0=3,a:/dev=5,a:/=3,e
Wenn Sie:Benutzen Sie:
Den Präfixbaum darstellen wollen das prefix Werkzeug
Den Präfixbaum aus einem C-Programm heraus abfragen wollen die qnx_prefix_query() Funktion


Netzwerk-Root (Netzwerkwurzel)

QNX unterstützt das Konzept einer Super- oder Netzwerk-Root, der durch einen mit zwei Schrägstrichen beginnenden Pfadnamen, gefolgt von einer Knotennummer, spezifiziert wird. Dies ermöglicht Ihnen, einen Pfadnamen zusammen mit dem Präfixbaum eines spezifischen Knotens zu benutzen, was Sie dann einfach auf Dateien und Geräte zugreifen läßt, die gar nicht in dem normalen Pfadnamensraum ihres eigenen Knotens vorhanden sind. In einem typischen QNX-Netzwerk würden die nachstehenden Pfade zum Beispiel folgendermaßen zugeordnet werden:

/dev/ser1 lokaler serieller Port
//10/dev/ser1 serieller Port auf Knoten 10
//0/dev/ser1 lokaler serieller Port
//20/usr/dtdodge/test Datei auf Knoten 20

Beachten Sie, daß //0 stets den lokalen Knoten referenziert.


Standard-Netzwerk-Root

Wenn ein Programm entfernt ausgeführt wird, möchten Sie typischerweise alle vergebenen Pfadnamen auf den Kontext des Pfadnamensraumes Ihres eigenen Computers aufgelöst haben. Zum Beispiel dieses Kommando:

//5 ls /

welches ein ls auf Knoten 5 ausführt, soll das gleiche zurückgeben wie:

ls /

was ein ls auf Ihrem Knoten ausführt. In beiden Fällen, sollte / über den Präfixbaum Ihres Knoten's aufgelöst werden, nicht über den auf Knoten 5. Stellen Sie sich andernfalls das Chaos vor, welches sich ergeben würde, wenn die Root (/) auf Knoten 5 dessen lokale Festplatte wäre und die Root Ihres Knotens Ihre lokale Festplatte wäre - die entfernte Ausführung würde Dateien aus einem komplett anderen Dateisystem erhalten!

Um Pfadnamen richtig aufzulösen, hat jeder Prozeß eine Standard-Netzwerk-Root, welche definiert, von welchem Knoten der Präfixbaum genutzt wird, um Pfadnamen, die mit einem einzelnen Schrägstrich beginnen, aufzulösen. Ist ein Pfadname, beginnend mit einem einzelnen / aufgelöst, wird die Standard-Netzwerk-Root vorangestellt. Hat beispielsweise ein Prozeß eine Standard-Netzwerk-Root von //9, dann würde:

/usr/home/luc

aufgelöst werden zu:

//9/usr/home/luc

was als ``Auflösung des Pfadnamens/usr/home/luc'' über den Präfixbaum von Knoten 9 interpretiert werden kann.

Die Standard-Netzwerk-Root wird durch neue Prozesse vererbt, wenn diese erzeugt werden, und wird in Ihrem lokalen Knoten initialisiert, wenn das System hochfährt. Lassen Sie uns zum Beispiel annehmen, Sie arbeiten auf Knoten 9 in einer Shell, welche ihre Standard-Netzwerk-Root auf Knoten 9 gesetzt hat (ein sehr typischer Fall). Wenn Sie das Kommando:

ls /

aufrufen, wird das Kommando die Standard-Netzwerk-Root von //9 erben und Sie würden den entsprechenden Wert von:

ls //9/

zurückbekommen. Würden Sie dieses Kommando eingeben:

//5 ls /

würden Sie das ls Kommando auf Knoten 5 starten, aber es würde dennoch die Standard-Netzwerk-Root von //9 erben, so daß Sie erneut den Wert von ls //9/ zurückerhalten. In beiden Fällen wird der Pfadname in Abhängigkeit desselben Pfadnamensraumes aufgelöst.

Wenn Sie:Benutzen Sie:
Ihre Standard-Netzwerk-Root erhalten wollen die qnx_prefix_getroot() Funktion
Ihre Standard-Netzwerk-Root setzen wollen die qnx_prefix_setroot() Funktion
Ein Programm mit einer neuen Standard-Netzwerk-Root laufen lassen wollen das on Werkzeug


Pfadnamen zwischen Prozessen austauschen

Wenn mehrere Prozesse laufen, müssen sie nicht alle die gleiche Standard-Netzwerk-Root haben - selbst, wenn sie auf dem gleichen Knoten laufen. Ein Prozeß kann zum Beispiel eine Standard- Netzwerk-Root von seinem Vaterprozeß irgendwo im Netzwerk geerbt haben oder seine Standard-Netzwerk-Root kann explizit durch seinen Vaterprozeß überschrieben worden sein.

Wenn Pfadnamen zwischen Prozessen, deren Standard-Netzwerk-Root unterschiedlich ist, verschickt werden (z. B. wenn Sie eine Datei an einen Druckerspooler übermitteln), sollten Sie die Standard-Netzwerk-Root an den Pfadnamen anpassen, bevor Sie den Pfadnamen an den Empfängerprozeß versenden. Wenn Sie sicher sind, daß der sendende und der empfangende Prozeß die gleiche Standard-Netzwerk-Root haben (oder wenn der Pfadname ein führendes //Knoten/ hat), können Sie diesen Schritt im sendenden Prozeß auslassen.

Alias-Präfixe

Wir haben Präfixe besprochen, welche an einen I/O-Ressource- Manager angebunden sind. Eine zweite Form von Präfixen, bekannt als Alias- Präfixe, ist eine einfache Zeichensubstitution für ein passendes Präfix. Ein Alias-Präfix hat die Form:

Präfix=Ersetzungs-Zeichen

Nehmen wir zum Beispiel an, Sie arbeiten auf einer Maschine, welche kein lokales Dateisystem besitzt (d. h. daß es keinen Prozeß gibt, der ``/'' administriert). Wie auch immer, auf einem anderen Knoten (sagen wir 10) gibt es ein Dateisystem, auf welches Sie durch ``/'' zugreifen möchten. Dies erreichen Sie mit dem folgenden Alias-Präfix:

/=//10/

Dadurch wird der führende Schrägstrich (/) in das //10/ Präfix eingebunden. So wird zum Beispiel /usr/dtdodge/test durch das Folgende ersetzt:

//10/usr/dtdodge/test

Dieser Pfadname wird erneut gegen den Präfixbaum geprüft. Diesesmal wird aber wegen dem führenden //10 Pfadnamen der Präfixbaum auf Knoten 10 benutzt. Dieser wird durch den Dateisystem-Manager auf Knoten 10 aufgelöst, wohin die open() Anfrage dann gerichtet ist. Mit nur ein paar Zeichen erlaubt uns dieser Alias, auf ein entferntes Dateisystem zuzugreifen, als ob es lokal wäre.

Es ist nicht notwendig, einen lokalen Dateisystem-Prozeß laufen zu lassen, um die Umleitung durchzusetzen. Der Präfixbaum eines plattenlosen Arbeitsplatzes könnte folgendermaßen aussehen:

/dev=5,a:/=//10/

Durch diesen Präfixbaum werden Pfadnamen unter /dev an den lokalen zeichenorientierten Geräte-Manager gelenkt, während Anfragen zu anderen Pfadnamen an das entfernte Dateisystem gelenkt werden.

Erzeugung spezieller Gerätenamen

Sie können Alias auch nutzen, um spezielle Gerätenamen zu erzeugen. Wenn beispielsweise auf Knoten 20 ein Druckerspooler läuft, könnten Sie auf einen lokalen Druckerpfadnamen einen Alias wie folgt setzen:

/dev/printer=//20/dev/spool

Alle Anfragen, den /dev/printer zu öffnen, werden über das Netzwerk an den echten Spooler gerichtet. Gleichermaßen könnte ein Alias auf ein entferntes Diskettenlaufwerk auf Knoten 20 folgendermaßen erstellt werden, wenn lokal keines vorhanden ist:

/dev/fd0=//20/dev/fd0

In beiden obigen Fällen könnte die Alias-Umleitung fortfallen und die entfernte Ressource könnte direkt wie folgt benannt werden:

//20/dev/spool  ODER  //20/dev/fd0

Relative Pfadnamen

Pfadnamen müssen nicht mit einem oder zwei Schrägstrichen beginnen. In solchen Fällen wird der Pfad als relativ zu dem aktuellen Arbeitsverzeichnis bezeichnet. QNX behält das aktuelle Arbeitsverzeichnis als Zeichenkette bei. Relative Pfadnamen werden immer zu vollen Netzwerk-Pfadnamen konvertiert, indem der String des aktuellen Arbeitsverzeichnisses vor den relativen Pfadnamen gehangen wird.

Beachten Sie, daß verschieden Verhaltensmuster auftreten, wenn Ihr aktuelles Arbeitsverzeichnis mit einem einzelnen Schrägstrich beginnt, anstatt mit einer Netzwerk Root.

Aktuelles Arbeitsverzeichnis

Wenn das aktuelle Arbeitsverzeichnis einen doppelten führenden Schrägstrich hat (Netzwerk-Root), wird es als spezifisch bezeichnet und an den Pfadnamensraum des spezifizierten Knoten angeschlossen. Hat es stattdessen einen einzelnen führenden Schrägstrich, wird die Standard-Netzwerk- Root verwendet.

Dieses Kommando zum Beispiel:

cd //18/

ist ein Beispiel für die erste (spezifische) Form, und würde zukünftige relative Pfadnamenauswertungen durchführen, als wären Sie auf Knoten 18, gleichgültig wie die augenblickliche Einstellung Ihrer Standard-Netzwerk-Root ist. Die spätere Eingabe von cd dev würde Sie zu //18/dev bringen.

Andererseits wäre dieses Kommando:

cd /

von der zweiten Form, in der die Standard-Netzwerk-Root die Auflösung des relativen Pfadnamens beeinträchtigt. Wäre zum Beispiel Ihre Standard-Netzwerk-Root //9, würde Sie die Eingabe von cd dev zu //9/dev bringen. Da das aktuelle Arbeitsverzeichnis nicht mit einem Knotenpräfix beginnt, wird bei der Auflösung des Pfades der Standardwert des augenblicklichen Knotens automatisch vorangestellt.

Das ist wirklich nicht so kompliziert, wie es erscheint. Typischerweise werden Netzwerk-Roots (//Knoten/) nicht spezifiziert, und alles was Sie tun wird einfach innerhalb Ihres Namensraum (Namespace) laufen (definiert durch Ihre Standard-Netzwerk-Root). Die meisten Benutzer werden sich anmelden, die normale Standard-Netzwerk-Root akzeptieren (z. B. den Namensraum (Namespace) ihres eigenen Knotens), und in dieser Umgebung arbeiten.

Eine Anmerkung zu cd

In einigen traditionellen UNIX-Systemen modifiziert das cd (Verzeichnis wechseln) Kommando den ihm gegebenen Pfadnamen, wenn dieser symbolische Verweise enthält. Als Ergebnis wird sich der Pfadname des neuen Arbeitsverzeichnisses - welches Sie sich mit pwd anzeigen lassen können - von dem, welcher cd übergeben wurde, unterscheiden.

Wie auch immer, in QNX modifiziert cd den Pfadnamen nicht - abgesehen von selbst reduzierenden .. Referenzen. Zum Beispiel:

cd /usr/home/luc/test/../doc

würde zu einem aktuellen Arbeitsverzeichnis von /usr/home/luc/doc führen, selbst wenn einige der Elemente im Pfadnamen symbolische Verweise waren.

Um mehr Informationen zu symbolischen Verweisen und .. Referenzen zu erhalten, lesen Sie bitte Kapitel 5, Der Dateisystem-Manager.


Note: Um einen vollständig aufgelösten Netzwerk-Pfadnamen anzuzeigen, können Sie das fullpath Werkzeug benutzen.

Dateideskriptor-Namensraum (Namespace)

Ist eine I/O-Ressource erst einmal geöffnet, kommt ein anderer Namensraum (Namespace) ins Spiel. Das open() gibt einen Integer zurück, welcher als ein Dateideskriptor (FD, file descriptor) referenziert wird. Er wird benutzt, um alle vorangegangenen I/O- Anfragen an den Manager zuzuordnen. (Beachten Sie, daß die Sendfd() Kernelrufe in Bibliotheksroutinen benutzt werden, um die Anfrage umzuleiten.)

Der Dateideskriptor-Namensraum (Namespace), im Gegensatz zu dem Pfadnamensraum, ist für jeden Prozeß lokal. Der Manager benutzt eine Kombination aus PID und FD, um die mit dem vorangegangenen open() Aufruf zusammenhängende Kontrollstruktur zu identifizieren. Diese Struktur wird als ein offener Kontrollblock (open control block) (OCB) referenziert und ist im I/O-Manager enthalten.

Das folgende Diagramm zeigt einen I/O-Manager, der einige PID, FD-Paare verwaltet und sie zu OCBs verbindet.


fig: images/pidfdocb_de.gif


Die PID und der FD werden zu einem OCB eines I/O-Managers verbunden.

 

Kontrollblöcke für geöffnete Geräte

Die Kontrollblöcke, im weiteren OCB (open control block) genannt, beinhaltet aktive Informationen über die geöffnete Ressource. Das Dateisystem hält hier beispielsweise den aktuellen Suchpunkt innerhalb der Datei fest. Jedes open() erzeugt ein neues OCB. Wenn ein Prozeß die gleiche Datei zweimal öffnet, wird deshalb jeder Aufruf von lseek() mit einem FD den Suchpunkt des anderen FD nicht beeinträchtigen.

Das gleiche gilt, wenn verschiedene Prozesse die gleiche Datei öffnen.

Das folgende Diagramm zeigt zwei Prozesse. Der eine öffnet die gleiche Datei zweimal, der andere einmal. Es gibt keine gemeinsamen Dateideskriptoren.


fig: images/tmpfile_de.gif


Prozeß A öffnet die Datei /tmp/file zweimal. Prozeß B öffnet die gleiche Datei einmal.

 

Viele Dateideskriptoren in einem oder mehreren Prozessen können sich auf den gleichen OCB beziehen. Dies kann eintreten durch zwei Ereignisse:

  • Ein Prozeß kann die dup(), dup2(), oder fcntl() Funktionen benutzen, um einen Dateideskriptor zu duplizieren, welcher den gleichen OCB referenziert.
  • Wird ein neuer Prozeß über fork(), spawn() oder exec() erzeugt, werden standardmäßig alle Dateideskriptoren an den neuen Prozeß vererbt; diese vererbten Deskriptoren referenzieren die gleichen OCBs wie die korrespondierenden Dateideskriptoren im Vaterprozeß.

Wenn verschiedene Deskriptoren den gleichen OCB referenzieren, wird jede Änderung des Zustandes des OCB unverzüglich von allen Prozessen gesehen, welche Dateideskriptoren auf den gleichen OCB haben.

Benutzt zum Beispiel ein Prozeß die lseek() Funktion, um die Position des Suchpunktes zu ändern, wird ein Schreiben oder Lesen von der neuen Position aus durchgeführt, gleichgültig welcher Deskriptor benutzt wird.

Das folgende Diagramm zeigt zwei Prozesse. Der eine öffnet eine Datei zweimal und setzt ein dup() ab, um einen dritten Deskriptor zu erhalten. Dann erzeugt der Prozeß einen Sohnprozeß, welcher alle geöffneten Dateien erbt.


fig: images/dupfd_de.gif


Ein Prozeß öffnet eine Datei zweimal und benutzt dup() um einen dritten FD zu erhalten. Sein Sohnprozeß erbt diese drei Dateideskriptoren.

 

Sie können die Vererbung eines Dateideskriptors verhindern, indem Sie vor dem Aufruf von spawn() oder exec() für den entsprechenden Deskriptor das FD_CLOEXEC Flag mit Hilfe der fcntl() Funktion setzen.