In allen Programmiersprachen (die ich mindestens verwende) müssen Sie eine Datei öffnen, bevor Sie sie lesen oder schreiben können.
Aber was macht diese offene Operation eigentlich?
Handbuchseiten für typische Funktionen sagen Ihnen eigentlich nichts anderes als "öffnet eine Datei zum Lesen / Schreiben":
http://www.cplusplus.com/reference/cstdio/fopen/
https://docs.python.org/3/library/functions.html#open
Durch die Verwendung der Funktion können Sie natürlich feststellen, dass ein Objekt erstellt wird, das den Zugriff auf eine Datei erleichtert.
Eine andere Möglichkeit wäre, wenn ich eine open
Funktion implementieren würde, was müsste sie unter Linux tun?
C
Linux zu konzentrieren; da unterscheidet sich was Linux und Windows machen. Ansonsten ist es etwas zu breit. Außerdem ruft jede übergeordnete Sprache entweder eine C-API für das System auf oder kompiliert sie zur Ausführung auf C herunter. Wenn Sie also auf der Ebene "C" bleiben, wird sie auf den kleinsten gemeinsamen Nenner gesetzt.Antworten:
In fast jeder Hochsprache ist die Funktion, die eine Datei öffnet, ein Wrapper um den entsprechenden Kernel-Systemaufruf. Es kann auch andere ausgefallene Dinge tun, aber in modernen Betriebssystemen muss das Öffnen einer Datei immer über den Kernel erfolgen.
Aus diesem Grund ähneln die Argumente der
fopen
Bibliotheksfunktion oder Pythonsopen
stark den Argumenten desopen(2)
Systemaufrufs.Zusätzlich zum Öffnen der Datei richten diese Funktionen normalerweise einen Puffer ein, der folglich bei den Lese- / Schreibvorgängen verwendet wird. Mit diesem Puffer soll sichergestellt werden, dass der entsprechende Bibliotheksaufruf beim Lesen von N Bytes N Bytes zurückgibt, unabhängig davon, ob die Aufrufe der zugrunde liegenden Systemaufrufe weniger zurückgeben.
In Unix-ähnlichen Betriebssystemen gibt ein erfolgreicher Aufruf zur
open
Rückgabe eines "Dateideskriptors" zurück, der im Kontext des Benutzerprozesses lediglich eine Ganzzahl ist. Dieser Deskriptor wird folglich an jeden Aufruf übergeben, der mit der geöffneten Datei interagiert, und nach dem Aufrufenclose
wird der Deskriptor ungültig.Es ist wichtig zu beachten, dass der Aufruf an
open
wie ein Validierungspunkt fungiert, an dem verschiedene Überprüfungen durchgeführt werden. Wenn nicht alle Bedingungen erfüllt sind, schlägt der Aufruf fehl, indem-1
anstelle des Deskriptors zurückgegeben wird, und die Art des Fehlers wird in angezeigterrno
. Die wesentlichen Überprüfungen sind:Im Kontext des Kernels muss eine Art Zuordnung zwischen den Dateideskriptoren des Prozesses und den physisch geöffneten Dateien bestehen. Die interne Datenstruktur, die dem Deskriptor zugeordnet ist, kann einen weiteren Puffer enthalten, der sich mit blockbasierten Geräten befasst, oder einen internen Zeiger, der auf die aktuelle Lese- / Schreibposition zeigt.
quelle
man dup2
und die Feinheit zwischen einem Scheck offenen Dateideskriptor (FD , die eine ist , das offen sein geschieht) und eine offene Dateibeschreibung (a OFD).Ich würde vorschlagen, dass Sie sich dieses Handbuch anhand einer vereinfachten Version des
open()
Systemaufrufs ansehen . Es wird das folgende Codefragment verwendet, das repräsentativ dafür ist, was beim Öffnen einer Datei hinter den Kulissen passiert.Kurz gesagt, hier ist, was dieser Code Zeile für Zeile tut:
Die
filp_open
Funktion hat die Implementierungdas macht zwei Dinge:
struct file
mit den wesentlichen Informationen zum Inode und geben Sie diese zurück. Diese Struktur wird zum Eintrag in der Liste der offenen Dateien, die ich zuvor erwähnt habe.Speichern ("installieren") Sie die zurückgegebene Struktur in der Liste der geöffneten Dateien des Prozesses.
read()
,write()
undclose()
. Jeder von diesen übergibt die Steuerung an den Kernel, der den Dateideskriptor verwenden kann, um den entsprechenden Dateizeiger in der Prozessliste nachzuschlagen, und die Informationen in diesem Dateizeiger verwendet, um das Lesen, Schreiben oder Schließen tatsächlich durchzuführen.Wenn Sie sich ehrgeizig fühlen, können Sie dieses vereinfachte Beispiel mit der Implementierung des
open()
Systemaufrufs im Linux-Kernel vergleichen, einer Funktion, die aufgerufen wirddo_sys_open()
. Sie sollten keine Probleme haben, die Ähnlichkeiten zu finden.Dies ist natürlich nur die "oberste Ebene" dessen, was passiert, wenn Sie aufrufen
open()
- oder genauer gesagt, es ist der Kernelcode auf höchster Ebene, der beim Öffnen einer Datei aufgerufen wird. Eine Programmiersprache auf hoher Ebene kann darüber hinaus zusätzliche Ebenen hinzufügen. Auf niedrigeren Ebenen ist viel los. (Danke an Ruslan und pjc50 für die Erklärung.) Grob von oben nach unten:open_namei()
unddentry_open()
invoke Dateisystem - Code, der auch Teil des Kernels ist, der Zugriff auf Metadaten und Inhalte für Dateien und Verzeichnisse. Das Dateisystem liest Rohbytes von der Festplatte und interpretiert diese Bytemuster als Baum von Dateien und Verzeichnissen./dev/sda
und dergleichen auf Rohdaten von der Blockgeräteebene zugreifen .)Dies kann aufgrund von Caching auch etwas falsch sein . :-P Im Ernst, es gibt viele Details, die ich ausgelassen habe - eine Person (nicht ich) könnte mehrere Bücher schreiben, die beschreiben, wie dieser ganze Prozess funktioniert. Aber das sollte dir eine Idee geben.
quelle
Jedes Dateisystem oder Betriebssystem, über das Sie sprechen möchten, ist für mich in Ordnung. Nett!
Bei einem ZX-Spektrum wird durch Initialisieren eines
LOAD
Befehls das System in eine enge Schleife geraten und die Audio-In-Zeile gelesen.Der Beginn der Daten wird durch einen konstanten Ton angezeigt. Danach folgt eine Folge von langen / kurzen Impulsen, wobei ein kurzer Impuls für eine Binärdatei
0
und ein längerer für eine Binärdatei gilt1
( https://en.wikipedia.org/). wiki / ZX_Spectrum_software ). Die enge Ladeschleife sammelt Bits, bis sie ein Byte (8 Bits) füllt, dieses im Speicher speichert, den Speicherzeiger erhöht und dann zurückschleift, um nach weiteren Bits zu suchen.In der Regel liest ein Loader als erstes einen kurzen Header mit festem Format , der mindestens die zu erwartende Anzahl von Bytes und möglicherweise zusätzliche Informationen wie Dateiname, Dateityp und Ladeadresse angibt. Nach dem Lesen dieses kurzen Headers kann das Programm entscheiden, ob der Hauptteil der Daten weiter geladen oder die Laderoutine beendet und eine entsprechende Meldung für den Benutzer angezeigt werden soll.
Ein Dateiende-Status kann erkannt werden, indem so viele Bytes wie erwartet empfangen werden (entweder eine feste Anzahl von Bytes, die in der Software fest verdrahtet sind, oder eine variable Anzahl, wie in einem Header angegeben). Ein Fehler wurde ausgelöst, wenn die Ladeschleife für eine bestimmte Zeit keinen Impuls im erwarteten Frequenzbereich empfangen hat.
Ein kleiner Hintergrund zu dieser Antwort
Das beschriebene Verfahren lädt Daten von einem normalen Audioband - daher muss Audio In gescannt werden (es ist mit einem Standardstecker an Tonbandgeräte angeschlossen). Ein
LOAD
Befehl ist technisch identisch mitopen
einer Datei, ist jedoch physisch an das tatsächliche Laden der Datei gebunden . Dies liegt daran, dass der Kassettenrekorder nicht vom Computer gesteuert wird und Sie eine Datei nicht (erfolgreich) öffnen, aber nicht laden können.Die "enge Schleife" wird erwähnt, weil (1) die CPU, ein Z80-A (wenn Speicher dient), sehr langsam war: 3,5 MHz, und (2) das Spektrum keinen internen Takt hatte! Das bedeutet, dass die T-Zustände (Befehlszeiten) für jeden genau gezählt werden mussten. Single. Anweisung. innerhalb dieser Schleife, nur um das genaue Signalton-Timing aufrechtzuerhalten.
Glücklicherweise hatte diese niedrige CPU-Geschwindigkeit den entscheidenden Vorteil, dass Sie die Anzahl der Zyklen auf einem Blatt Papier und damit die reale Zeit berechnen konnten, die sie benötigen würden.
quelle
Es hängt vom Betriebssystem ab, was genau passiert, wenn Sie eine Datei öffnen. Im Folgenden beschreibe ich, was unter Linux passiert, da es Ihnen eine Vorstellung davon gibt, was passiert, wenn Sie eine Datei öffnen und Sie den Quellcode überprüfen können, wenn Sie an weiteren Details interessiert sind. Ich gehe nicht auf Berechtigungen ein, da dies zu lange dauern würde.
Unter Linux wird jede Datei von einer Struktur namens inode erkannt. Jede Struktur hat eine eindeutige Nummer und jede Datei erhält nur eine Inode-Nummer. Diese Struktur speichert Metadaten für eine Datei, z. B. Dateigröße, Dateiberechtigungen, Zeitstempel und Zeiger auf Plattenblöcke, jedoch nicht den tatsächlichen Dateinamen selbst. Jede Datei (und jedes Verzeichnis) enthält einen Dateinameneintrag und die Inode-Nummer für die Suche. Wenn Sie eine Datei öffnen und davon ausgehen, dass Sie über die entsprechenden Berechtigungen verfügen, wird ein Dateideskriptor unter Verwendung der eindeutigen Inode-Nummer erstellt, die dem Dateinamen zugeordnet ist. Da viele Prozesse / Anwendungen auf dieselbe Datei verweisen können, verfügt inode über ein Verknüpfungsfeld, in dem die Gesamtzahl der Verknüpfungen zur Datei beibehalten wird. Wenn eine Datei in einem Verzeichnis vorhanden ist, beträgt ihre Linkanzahl eins. Wenn sie einen festen Link hat, beträgt ihre Linkanzahl zwei. Wenn eine Datei durch einen Prozess geöffnet wird, wird die Linkanzahl um 1 erhöht.
quelle
Meistens Buchhaltung. Dies beinhaltet verschiedene Überprüfungen wie "Existiert die Datei?" und "Habe ich die Berechtigung, diese Datei zum Schreiben zu öffnen?".
Aber das ist alles Kernel-Zeug - es sei denn, Sie implementieren Ihr eigenes Spielzeug-Betriebssystem, es gibt nicht viel zu vertiefen (wenn Sie es sind, haben Sie Spaß - es ist eine großartige Lernerfahrung). Natürlich sollten Sie immer noch alle möglichen Fehlercodes kennen, die Sie beim Öffnen einer Datei erhalten können, damit Sie richtig damit umgehen können - aber das sind normalerweise nette kleine Abstraktionen.
Der wichtigste Teil auf Codeebene besteht darin, dass Sie ein Handle für die geöffnete Datei erhalten, das Sie für alle anderen Vorgänge verwenden, die Sie mit einer Datei ausführen. Könnten Sie nicht den Dateinamen anstelle dieses beliebigen Handles verwenden? Na klar - aber die Verwendung eines Griffs bietet Ihnen einige Vorteile:
read
von der letzten Position in Ihrer Datei. Wenn Sie ein Handle verwenden, um eine bestimmte "Öffnung" einer Datei zu identifizieren, können Sie mehrere Handles gleichzeitig für dieselbe Datei verwenden, wobei jedes von seinem eigenen Ort aus gelesen wird. In gewisser Weise fungiert das Handle als bewegliches Fenster in die Datei (und als Möglichkeit, asynchrone E / A-Anforderungen auszugeben, die sehr praktisch sind).Es gibt auch einige andere Tricks, die Sie ausführen können (z. B. das Teilen von Handles zwischen Prozessen, um einen Kommunikationskanal ohne Verwendung einer physischen Datei zu haben; auf Unix-Systemen werden Dateien auch für Geräte und verschiedene andere virtuelle Kanäle verwendet, sodass dies nicht unbedingt erforderlich ist ), aber sie sind nicht wirklich an die
open
Operation selbst gebunden , deshalb werde ich mich nicht damit befassen.quelle
Im Mittelpunkt der es beim Öffnen zum Lesen eigentlich nichts Besonderes muss passieren. Sie müssen lediglich überprüfen, ob die Datei vorhanden ist und die Anwendung über ausreichende Berechtigungen zum Lesen verfügt, und ein Handle erstellen, mit dem Sie Lesebefehle für die Datei ausgeben können.
Auf diesen Befehlen wird das eigentliche Lesen ausgelöst.
Das Betriebssystem hat häufig einen Vorsprung beim Lesen, indem es eine Leseoperation startet, um den dem Handle zugeordneten Puffer zu füllen. Wenn Sie dann tatsächlich lesen, kann der Inhalt des Puffers sofort zurückgegeben werden, anstatt auf der Festplatten-E / A warten zu müssen.
Zum Öffnen einer neuen Datei zum Schreiben muss das Betriebssystem einen Eintrag im Verzeichnis für die neue (derzeit leere) Datei hinzufügen. Und wieder wird ein Handle erstellt, auf dem Sie die Schreibbefehle ausgeben können.
quelle
Grundsätzlich muss ein Aufruf zum Öffnen die Datei finden und dann aufzeichnen, was auch immer erforderlich ist, damit spätere E / A-Vorgänge sie wiederfinden können. Das ist ziemlich vage, aber es wird auf allen Betriebssystemen zutreffen, an die ich sofort denken kann. Die Besonderheiten variieren von Plattform zu Plattform. Viele Antworten hier sprechen bereits über moderne Desktop-Betriebssysteme. Ich habe ein wenig auf CP / M programmiert, daher werde ich mein Wissen darüber anbieten, wie es auf CP / M funktioniert (MS-DOS funktioniert wahrscheinlich genauso, aber aus Sicherheitsgründen wird es heute normalerweise nicht so gemacht ).
Auf CP / M haben Sie ein Ding namens FCB (wie Sie C erwähnt haben, können Sie es eine Struktur nennen; es ist wirklich ein zusammenhängender 35-Byte-Bereich im RAM, der verschiedene Felder enthält). Der FCB verfügt über Felder zum Schreiben des Dateinamens und einer (4-Bit-) Ganzzahl, die das Laufwerk identifiziert. Wenn Sie dann die Open File des Kernels aufrufen, übergeben Sie einen Zeiger auf diese Struktur, indem Sie ihn in eines der Register der CPU stellen. Einige Zeit später kehrt das Betriebssystem mit leicht geänderter Struktur zurück. Unabhängig davon, welche E / A Sie mit dieser Datei ausführen, übergeben Sie dem Systemaufruf einen Zeiger auf diese Struktur.
Was macht CP / M mit diesem FCB? Es reserviert bestimmte Felder für den eigenen Gebrauch und verwendet diese, um den Überblick über die Datei zu behalten. Sie sollten sie also niemals aus Ihrem Programm heraus berühren. Die Operation "Datei öffnen" durchsucht die Tabelle am Anfang der Festplatte nach einer Datei mit demselben Namen wie der FCB (das Platzhalterzeichen "?" Entspricht einem beliebigen Zeichen). Wenn eine Datei gefunden wird, werden einige Informationen in den FCB kopiert, einschließlich der physischen Speicherorte der Datei auf der Festplatte, sodass nachfolgende E / A-Aufrufe letztendlich das BIOS aufrufen, das diese Speicherorte möglicherweise an den Festplattentreiber weiterleitet. Auf dieser Ebene variieren die Besonderheiten.
quelle
In einfachen Worten, wenn Sie eine Datei öffnen, fordern Sie das Betriebssystem tatsächlich auf, die gewünschte Datei (Kopieren des Dateiinhalts) vom Sekundärspeicher in den RAM zur Verarbeitung zu laden. Der Grund dafür (Laden einer Datei) ist, dass Sie die Datei aufgrund ihrer im Vergleich zu Ram extrem langsamen Geschwindigkeit nicht direkt von der Festplatte verarbeiten können.
Der Befehl open generiert einen Systemaufruf, der wiederum den Inhalt der Datei vom Sekundärspeicher (Festplatte) in den Primärspeicher (Ram) kopiert.
Und wir 'schließen' eine Datei, weil der geänderte Inhalt der Datei in die Originaldatei auf der Festplatte übernommen werden muss. :) :)
Hoffentlich hilft das.
quelle