Warum müssen wir auf Unix-Systemen Dateien explizit öffnen () und schließen (), um sie lesen () oder schreiben () zu können?

50

Warum existieren open()und close()existieren sie im Unix-Dateisystemdesign?

Konnte das Betriebssystem nicht einfach das erste Mal erkennen read()oder write()wurde es angerufen und tat, was open()normalerweise tun würde?

user5977637
quelle
22
Es ist erwähnenswert, dass dieses Modell nicht Teil des Dateisystems , sondern der Unix-API ist . Das Dateisystem befasst sich lediglich damit, wo auf der Festplatte die Bytes abgelegt werden und wo der Dateiname usw. abgelegt wird. Es wäre durchaus möglich, das von Ihnen beschriebene alternative Modell auf einem Unix-Dateisystem wie UFS oder ext4 zu haben Kernel, um diese Aufrufe in die richtigen Updates für das Dateisystem zu übersetzen (so wie es jetzt ist).
März
18
Ich denke, es geht eher darum, warum es open()existiert. "Konnte das Betriebssystem nicht einfach das erste Mal lesen () oder schreiben () und das tun, was open () normalerweise tun würde?" Gibt es einen entsprechenden Vorschlag für den Zeitpunkt des Abschlusses ?
Joshua Taylor
7
Wie würden Sie sagen, read()oder auf write()welche Datei Sie zugreifen sollen? Vermutlich auf dem Weg. Was passiert, wenn sich der Pfad der Datei ändert, während Sie darauf zugreifen (zwischen zwei read()oder zwei write()Aufrufen)?
user253751
2
Außerdem machen Sie normalerweise keine Zugriffskontrolle bei read()und write(), nur bei open().
Pavel Šimerda
6
@Johnny: Du vergisst vielleicht, wie begrenzt die Hardware damals war. Der PDP-7, auf dem Unix zum ersten Mal implementiert wurde, verfügte (laut Google) über maximal 64 KB RAM und einen Takt von 0,333 MHz - heutzutage eher weniger als ein einfacher Mikrocontroller. Eine solche Speicherbereinigung oder die Verwendung von Systemcode zur Überwachung des Dateizugriffs hätte das System in die Knie gezwungen.
Jamesqf

Antworten:

60

Dennis Ritchie erwähnt in «Der Entwicklung des Unix Time-Sharing - Systems» , dass openund closezusammen mit read, writeund creatwar in dem System von Anfang an .

Ich denke, ein System ohne openund closewäre nicht undenkbar, aber ich glaube, es würde das Design verkomplizieren. In der Regel möchten Sie mehrere Lese- und Schreibaufrufe durchführen, nicht nur einen. Dies gilt wahrscheinlich insbesondere für alte Computer mit sehr begrenztem Arbeitsspeicher, auf denen UNIX ausgeführt wurde. Ein Handle, das Ihre aktuelle Dateiposition beibehält, vereinfacht dies. Wenn readoderwriteWenn sie das Handle zurückgeben würden, müssten sie ein Paar zurückgeben - ein Handle und ihren eigenen Rückgabestatus. Der Handle-Teil des Paares wäre für alle anderen Anrufe unbrauchbar, was diese Anordnung umständlich machen würde. Indem Sie den Status des Cursors dem Kernel überlassen, können Sie die Effizienz nicht nur durch Puffern verbessern. Die Suche nach Pfaden ist auch mit Kosten verbunden. Wenn Sie ein Handle haben, können Sie es nur einmal bezahlen. Außerdem haben einige Dateien in der UNIX-Weltansicht nicht einmal einen Dateisystempfad (oder nicht - jetzt tun sie das mit Dingen wie /proc/self/fd).

PSkocik
quelle
7
Die Kosten für die Pfadsuche und Berechtigungsprüfung usw. usw. sind sehr hoch. Wenn Sie ein System ohne open/ closeerstellen möchten, müssen Sie Dinge implementieren /dev/stdout, die das Piping zulassen.
Peter Cordes
5
Ich denke, ein weiterer Aspekt dabei ist, dass Sie dieses Handle für dieselbe Datei beibehalten können, wenn Sie mehrere Lesevorgänge verwenden, wenn Sie die Datei geöffnet lassen. Andernfalls kann es vorkommen, dass ein anderer Prozess die Verknüpfung aufhebt und eine Datei mit demselben Namen neu erstellt. Das Lesen einer Datei in Blöcken ist möglicherweise völlig inkohärent. (Einige davon können auch vom Dateisystem abhängen.)
Bruno
2
Ich entwarf eine ohne close (); Sie übergeben die Inode-Nummer und den Offset an read () und write (). Ich kann nicht ohne open () auskommen, weil hier die Namensauflösung lebt.
Joshua
3
@Joshua: Ein solches System hat eine grundlegend andere Semantik, weil Unix-Dateideskriptoren nicht auf Dateien (Inodes) verweisen, sondern auf Dateibeschreibungen , von denen es für eine bestimmte Datei (Inode) viele geben kann.
R ..
@Joshua, die Sie gerade umbenannt open()zu get_inode()und machten das ganze System steife (unmöglich , die gleiche Datei an mehreren Stellen gleichzeitig lesen / schreiben).
Vonbrand
53

Dann müssten alle Aufrufe von readund writediese Informationen bei jeder Operation weitergeben:

  • der Name der Datei
  • die Berechtigungen der Datei
  • ob der Anrufer anhängt oder erstellt
  • ob der Aufrufer mit der Arbeit an der Datei fertig ist (um nicht verwendete Lesepuffer zu verwerfen und sicherzustellen, dass die Schreibpuffer wirklich fertig sind)

Egal , ob Sie die unabhängigen betrachten Anrufe open , read, writeund closeseine einfacher als eine Single-Purpose I / O - Nachricht auf Ihrer Design - Philosophie basiert. Die Unix-Entwickler entschieden sich für einfache Operationen und Programme, die auf viele Arten kombiniert werden können, anstatt für eine einzige Operation (oder ein einzelnes Programm), die alles erledigt.

Thomas Dickey
quelle
Anrufer müssten in den meisten Fällen auch den gewünschten Versatz innerhalb einer Datei angeben. Es gibt Situationen (z. B. ein UDP-Protokoll, das den Zugriff auf Daten ermöglicht), in denen es hilfreich sein kann, dass jede Anforderung eine Datei und einen Offset unabhängig identifiziert, da ein Server nicht mehr den Status aufrechterhalten muss. Im Allgemeinen ist es jedoch bequemer, den Server zu haben Verfolgen Sie die Position der Datei. Wie an anderer Stelle erwähnt, muss Code, der Dateien schreibt, diese häufig vorher und anschließend sperren. Das Kämmen dieser Operationen mit Öffnen / Schließen ist sehr bequem.
Supercat
5
Die "Datei" hat möglicherweise überhaupt keinen Namen oder keine Berechtigungen. readund writesind nicht auf Dateien beschränkt, die sich auf einem Dateisystem befinden, und dies ist eine grundlegende Entwurfsentscheidung in Unix, wie pjc50 erklärt.
reinierpost
1
Auch wo in der Datei zu lesen / schreiben - am Anfang, am Ende oder an einer beliebigen Stelle (in der Regel unmittelbar nach dem Ende des letzten Lese- / Schreibvorgangs) - verfolgt der Kernel dies für Sie (mit einem Modus zu) leite alle Schreibvorgänge an das Ende der Datei, oder auf andere Weise werden Dateien mit der Position am Anfang geöffnet und mit jedem Lese- / Schreibvorgang vorgerückt und können mit verschoben werden lseek)
Random832
51

Das Konzept der Dateizugriffsnummer ist wichtig, da UNIX als Entwurfsoption festlegt, dass "alles eine Datei ist", auch Dinge, die nicht Teil des Dateisystems sind. B. Bandlaufwerke, Tastatur und Bildschirm (oder Teletyp!), Lochkarten- / Bandleser, serielle Verbindungen, Netzwerkverbindungen und (die wichtigste UNIX-Erfindung) direkte Verbindungen zu anderen Programmen, die als "Pipes" bezeichnet werden.

Wenn Sie sich viele der einfachen Standard-UNIX-Dienstprogramme ansehen grep, insbesondere die Originalversionen, werden Sie feststellen, dass sie keine Aufrufe von open()und, close()sondern nur readund enthalten write. Die Dateihandles werden außerhalb des Programms von der Shell eingerichtet und beim Start übergeben. Das Programm muss sich also nicht darum kümmern, ob es in eine Datei oder in ein anderes Programm schreibt.

Neben opensind die anderen Möglichkeiten Filedeskriptoren zu bekommen socket, listen, pipe, dup, und ein sehr Heath Robinson Mechanismus für Dateideskriptoren über Rohre zu senden: https://stackoverflow.com/questions/28003921/sending-file-descriptor-by-linux -Steckdose

Bearbeiten: Einige Vorlesungsnotizen, in denen die Indirektionsebenen beschrieben werden und wie O_APPEND auf diese Weise sinnvoll funktioniert. Beachten Sie, dass durch das Speichern der Inode-Daten gewährleistet ist, dass das System sie für den nächsten Schreibvorgang nicht erneut abrufen muss.

pjc50
quelle
1
Außerdem erstellt creatund erstellt listenkein FD, aber wenn (und wenn) eine Anforderung beim Abhören accepteingeht, wird ein FD für den neuen (verbundenen) Socket erstellt und zurückgegeben.
Dave_thompson_085
18
Das ist DIE richtige Antwort. Die berühmte (kleine) Menge von Operationen für Dateideskriptoren ist eine einheitliche API für alle Arten von Ressourcen, die Daten erzeugen oder verbrauchen. Dieses Konzept ist sehr erfolgreich. Eine Zeichenfolge könnte möglicherweise eine Syntax haben, die den Ressourcentyp zusammen mit dem tatsächlichen Speicherort (URL anybody?) Definiert. Das Kopieren von Zeichenfolgen, die mehrere Prozent des verfügbaren Arbeitsspeichers belegen (was war das auf dem PDP 7? 16 kB?), Erscheint jedoch zu umfangreich .
Peter - Reinstate Monica
Vielleicht wäre es, wenn die Low-Level-Aufrufe und die Shell gleichzeitig entwickelt würden. Wurde pipeaber einige Jahre nach dem Start der Entwicklung auf Unix eingeführt.
Thomas Dickey
1
@Thomas Dickey: Was nur zeigt, wie gut das ursprüngliche Design war, da es die einfache Erweiterung auf Pipes & C ermöglichte :-)
Jamesqf
Nach dieser Argumentation liefert diese Antwort jedoch nichts Neues.
Thomas Dickey
10

Die Antwort lautet nein, da open () und close () jeweils ein Handle erstellen und zerstören. In bestimmten Situationen möchten Sie möglicherweise sicherstellen, dass Sie der einzige Anrufer mit einer bestimmten Zugriffsebene sind, da beispielsweise ein anderer Anrufer, der in eine Datei schreibt, die Sie gerade analysieren, diese möglicherweise unerwartet verlassen könnte eine Bewerbung in einem unbekannten Zustand oder führen zu einem Livelock oder Deadlock, zB dem Dining Philosophers Lemma.

Auch ohne diese Überlegung sind Auswirkungen auf die Leistung zu berücksichtigen. close () ermöglicht es dem Dateisystem, den von Ihnen belegten Puffer zu leeren (wenn es angemessen ist oder wenn Sie es angefordert haben), eine teure Operation. Mehrere aufeinanderfolgende Änderungen an einem In-Memory-Stream sind weitaus effizienter als mehrere im Wesentlichen unabhängige Lese-, Schreib- und Änderungszyklen in einem Dateisystem, das, wie Sie wissen, eine halbe Welt entfernt über ein Datencenter mit einem Massenspeicher mit hoher Latenz liegt. Selbst bei lokalem Speicher ist der Speicher in der Regel um viele Größenordnungen schneller als der Massenspeicher.

msaunier
quelle
7

Open () bietet eine Möglichkeit, Dateien zu sperren, während sie verwendet werden. Wenn Dateien vom Betriebssystem automatisch geöffnet, gelesen / geschrieben und dann wieder geschlossen würden, ließe sich nichts daran ändern, dass andere Anwendungen diese Dateien zwischen den Vorgängen ändern.

Dies kann zwar verwaltbar sein (viele Systeme unterstützen den nicht exklusiven Dateizugriff), die meisten Anwendungen gehen jedoch davon aus, dass sich die geöffneten Dateien nicht ändern.

あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ あ
quelle
5

Da sich der Pfad der Datei möglicherweise verschiebt, obwohl davon ausgegangen wird, dass er gleich bleibt.

Mehrdad
quelle
4

Das Lesen und Schreiben in ein Dateisystem kann eine Vielzahl von Pufferschemata, die Verwaltung des Betriebssystems, die Datenträgerverwaltung auf niedriger Ebene und eine Vielzahl anderer potenzieller Aktionen umfassen. Die Aktionen von open()und close()dienen als Vorbereitung für diese Art von Aktivitäten unter der Haube. Verschiedene Implementierungen eines Dateisystems können nach Bedarf stark angepasst werden und bleiben für das aufrufende Programm dennoch transparent.

Wenn das Betriebssystem nicht geöffnet / geschlossen hätte, dann müssten diese Dateiaktionen mit readoder writejedes Mal Initialisierungen, Pufferlöschung / -verwaltung usw. durchführen. Das ist ein großer Aufwand für sich wiederholende Lese- und Schreibvorgänge.

PeterT
quelle
Nicht zu vergessen, dass open () und close () auch die Position in der Datei behalten (für das nächste Lesen oder Schreiben). Am Ende von read () und write () müsste also eine Struktur für alle Parameter erstellt werden, oder es müssen Argumente für jeden Parameter angegeben werden. Das Erstellen einer Struktur entspricht einer offenen Site (Programmierer-Site). Wenn das Betriebssystem also auch etwas über offene Sites weiß, haben wir nur weitere Vorteile.
Giacomo Catenazzi
1

Das Unix-Mantra ist "Biete eine Möglichkeit, Dinge zu tun", was "Zerlegen" in (wiederverwendbare) Teile bedeutet, die nach Belieben kombiniert werden können. In diesem Fall trennen Sie die Erstellung und Zerstörung von Dateihandles von ihrer Verwendung. Wichtige Vorteile kamen später mit Pipes und Netzwerkverbindungen (sie werden auch über Datei-Handles bearbeitet, aber sie werden auf andere Weise erstellt). Nur so ist es möglich, Dateihandles zu versenden (z. B. an untergeordnete Prozesse als "offene Dateien" weiterzugeben exec(2), die überleben , und sogar an nicht verwandte Prozesse über eine Pipe). Insbesondere, wenn Sie einen kontrollierten Zugriff auf eine geschützte Datei anbieten möchten. So können Sie zB öffnen/etc/passwd Übergeben Sie dies zum Schreiben an einen untergeordneten Prozess, der diese Datei nicht zum Schreiben öffnen darf. (Ja, ich weiß, dies ist ein lächerliches Beispiel. Sie können es gerne mit etwas Realistischerem bearbeiten.)

vonbrand
quelle