Was passiert, wenn ich einen Dateideskriptor schließe ()?

16

Ich versuche, das ganze Bild mit Dateideskriptoren zu bekommen. Angenommen, ich habe process1, das anfangs die folgenden Dateideskriptoren hat:

 _process1_
|          |
| 0 stdin  |
| 1 stdout |
| 2 stderr |
|__________|

Dann schließe ich Dateideskriptor 1:

close(1);

Der Dateideskriptor 1 übersetzt (zeigt) in die stdout FILE-Struktur in der Open Files-Tabelle des Kernels .

Mit dem obigen Code wird der Dateideskriptor 1 aus der Prozesstabelle gelöscht.

 _process1_
|          |
| 0 stdin  |
| 2 stderr |
|__________|

Aber was passiert im Kernel? Wird die stdoutDateistruktur freigegeben? Wie ist das möglich, wenn stdout eine spezielle Datei (der Monitor) ist und wahrscheinlich von anderen Prozessen verwendet wird? Was ist mit FILE-Strukturen, die nur normale Dateien sind (z. B. TXT)? Was ist, wenn eine solche Datei von einem anderen Prozess verwendet wird?

Pithikos
quelle

Antworten:

13

Der Dateideskriptor 1 übersetzt in die stdout FILE-Struktur in der Open Files-Tabelle des Kernels.

Das ist ein Missverständnis. Die Dateitabelle des Kernels hat überhaupt nichts mit Dateistrukturen im Benutzerbereich zu tun.

In jedem Fall hat der Kernel zwei Dereferenzierungsebenen. Es gibt die interne Struktur, die die Datei selbst darstellt, die referenziert wird. Es gibt eine „offene Dateibeschreibung“, die Referenz gezählt wird. Und dann gibt es die Datei-Handle, das gezählt nicht Referenz. Die Dateistruktur weist den Weg zum Inode. Die offene Dateibeschreibung enthält Dinge wie die offenen Modus und Dateizeiger.

Wenn Sie close aufrufen, schließen Sie immer das Dateihandle. Wenn ein Dateihandle geschlossen wird, wird der Referenzzähler für die geöffnete Dateibeschreibung verringert. Wenn es auf Null geht, wird auch die Beschreibung der geöffneten Datei freigegeben und der Referenzzähler für die Datei selbst wird dekrementiert. Nur wenn das auf Null geht, wird die Dateistruktur des Kernels freigegeben.

Es gibt keine Möglichkeit für einen Prozess, eine Ressource freizugeben, die von einem anderen Prozess verwendet wird, da gemeinsam genutzte Ressourcen als Referenz gezählt werden.

David Schwartz
quelle
Ich habe leichte Schwierigkeiten, die Terminologie in Ihrer Antwort zu verstehen. Ich vermute, dass Dateizeiger "Datei-Offset" bedeutet. Ist es das, was du meintest? Auch was meintest du mit Dateihandle ?
Geek
Das ist richtig, durch „file offset“, meine ich den Offset , bei dem eine nachfolgende Lese- oder Schreib auftreten würde. Eine „Datei - Handle“ ist eine Verbindung zwischen einem Prozess und einer geöffneten Datei Beschreibung - es ist , was Sie zurück , wenn openerfolgreich ist .
David Schwartz
6

In diesem Fall wird nicht viel passieren. stdin, stdout und stderr alle neigen dazu Klone aus dem gleichen Dateideskriptor zu sein. Der Referenzzähler für den Dateideskriptor wird um eins dekrementiert. Derselbe Dateideskriptor wird normalerweise von der Shell gespeichert, von der aus das Programm ausgeführt wurde. Daher muss der Dateideskriptor beibehalten werden.

Der Kernel führt Referenzzählungen für alle geöffneten Dateien (Inodes). Solange der Referenzzähler größer als Null ist, bleibt die Datei erhalten. Ich würde erwarten, dass für offene Dateihandles ein separater Zähler geführt wird. Sobald dies Null erreicht, kann der Kernel den vom Datei-Handle verwendeten Speicher freigeben.

Wenn alle Verweise auf die Datei (Verzeichniseinträge und Datei-Handles) entfernt wurden, wird das Dateisystem-Code den Inode für die Wiederverwendung markieren. Alle Blöcke, die die Datei enthält, werden für die Zuordnung zur Verfügung gestellt. Viele Dateisysteme löschen die Blockzeiger im Inode, wenn dieser freigegeben wird. Dies erschwert die Wiederherstellung einer gelöschten Datei. Aktualisierungen der Festplatte werden möglicherweise gepuffert und zu einem späteren Zeitpunkt abgeschlossen.

BillThor
quelle
1
Zwei Fragen: (1) Werden die Dateideskriptoren wirklich neu gezählt? Wenn du cat > some.file-da kontrollierst , erhält cat einen EOF für stdin, die Shell jedoch nicht. (2) Warum Referenzzählung? Warum nicht irgendeine Form von Garbage Collection? Ist das nicht GC weit besser im User - Space?
Bruce Ediger
Erweiterung der Antwort von BillThor: In normalen Fällen sind stdin, stdout und stderr nur offene Datei-Handles für ein TTY-Gerät. Wenn Sie also das Datei-Handle schließen, ist das TTY-Gerät noch vorhanden und kann zu einem späteren Zeitpunkt sogar wieder geöffnet werden.
Patrick
1
@BruceEdiger: (1) , wenn die Schale läuft , cat > some.filewas seine eigentlich Forking tun, eröffnen ‚some.file‘ und es Dateideskriptor 1 zuweisen, dann tut es exec("cat"). Wenn ein Prozess exec () 'd ist, erbt er die geöffneten Dateideskriptoren.
Patrick
@BruceEdiger (2) Referenzzählung ist eine ganz feine Form von Garbage Collection , wenn es auf Datenstrukturen verwendet wird, die Zeiger auf (oder Ketten von Zeigern in endend) enthalten keine andere Datenstrukturen des gleichen Typs. Dies geschieht auch im Kernelraum (nicht, dass es sehr wichtig ist).
Gilles 'SO- hör auf böse zu sein'