Sehr oft hören Anfänger einen Satz "Alles ist eine Datei unter Linux / Unix". Wie lauten dann aber die Verzeichnisse? Wie unterscheiden sie sich von Dateien?
Hinweis: Ursprünglich wurde dies geschrieben, um meine Antwort auf die Fragels
zu unterstützen, warum das aktuelle Verzeichnis im Befehl als mit sich selbst verknüpft gekennzeichnet ist. Ich war jedoch der Meinung, dass dies ein Thema ist, das es verdient, für sich allein zu stehen, und daher auch diese Fragen und Antworten .
Im Wesentlichen ist ein Verzeichnis nur eine spezielle Datei, die eine Liste von Einträgen und deren ID enthält.
Bevor wir mit der Diskussion beginnen, ist es wichtig, einige Begriffe zu unterscheiden und zu verstehen, welche Verzeichnisse und Dateien wirklich repräsentieren. Möglicherweise haben Sie den Ausdruck "Alles ist eine Datei" für Unix / Linux gehört. Nun, was Benutzer oft als Datei verstehen, ist Folgendes: /etc/passwd
- Ein Objekt mit einem Pfad und einem Namen. In Wirklichkeit ist ein Name (sei es ein Verzeichnis oder eine Datei oder was auch immer) nur eine Textzeichenfolge - eine Eigenschaft des tatsächlichen Objekts. Dieses Objekt heißt Inode oder I-Nummer und wird auf der Festplatte in der Inode-Tabelle gespeichert. Offene Programme haben auch Inode-Tabellen, aber das ist vorerst nicht unser Problem.
Unix 'Vorstellung von einem Verzeichnis ist, wie Ken Thompson es in einem Interview von 1989 ausdrückte :
... Und dann waren einige dieser Dateien Verzeichnisse, die nur Namen und I-Nummern enthielten.
Eine interessante Beobachtung kann Dennis Ritchies Vortrag von 1972 entnommen werden
"... Verzeichnis ist eigentlich nicht mehr als eine Datei, aber sein Inhalt wird vom System gesteuert, und der Inhalt sind Namen anderer Dateien. (Ein Verzeichnis wird in anderen Systemen manchmal als Katalog bezeichnet.)"
... aber es wird nirgends in der Diskussion von Inodes gesprochen. Im Handbuch von 1971format of directories
heißt es jedoch:
Die Tatsache, dass eine Datei ein Verzeichnis ist, wird durch ein Bit im Flag-Wort ihres i-Node-Eintrags angezeigt.
Verzeichniseinträge sind 10 Byte lang. Das erste Wort ist der i-Knoten der Datei, die durch den Eintrag dargestellt wird, wenn er nicht Null ist. Bei Null ist der Eintrag leer.
So war es von Anfang an da.
Das Koppeln von Verzeichnissen und Inodes wird auch unter Wie werden Verzeichnisstrukturen im UNIX-Dateisystem gespeichert? Erläutert . . Ein Verzeichnis selbst ist eine Datenstruktur, insbesondere eine Liste von Objekten (Dateien und Inode-Nummern), die auf Listen über diese Objekte (Berechtigungen, Typ, Eigentümer, Größe usw.) verweisen. Jedes Verzeichnis enthält also eine eigene Inode-Nummer und anschließend Dateinamen und deren Inode-Nummern. Am bekanntesten ist die Inode # 2, die /
Verzeichnis ist . (Beachten Sie jedoch , dass /dev
und /run
virtuelle Dateisysteme sind, so da sie Stammordner für ihre Dateisystem sind, haben sie auch Inode 2; dh ein Inode ist auf seinem eigenen Dateisystem eindeutig, aber wenn mehrere Dateisysteme angeschlossen sind, haben Sie nicht eindeutige Inodes. Das Diagramm, das aus der verknüpften Frage entlehnt wurde, erklärt sie wahrscheinlich prägnanter:
Auf alle diese im Inode gespeicherten Informationen kann über stat()
Systemaufrufe gemäß Linux zugegriffen werden man 7 inode
:
Jede Datei hat einen Inode mit Metadaten zur Datei. Eine Anwendung kann diese Metadaten mit stat (2) (oder verwandten Aufrufen) abrufen, die eine stat-Struktur zurückgeben, oder mit statx (2), die eine statx-Struktur zurückgibt.
Ist es möglich, auf eine Datei nur mit Kenntnis der Inode-Nummer ( ref1 , ref2 ) zuzugreifen ? Bei einigen Unix-Implementierungen ist es möglich, aber die Berechtigungs- und Zugriffsprüfungen werden umgangen. Unter Linux ist dies nicht find <DIR> -inum 1234
der Fall und Sie müssen den Dateisystembaum ( z. B. über) durchlaufen , um einen Dateinamen und den entsprechenden Inode zu erhalten.
Auf der Quellcode-Ebene ist es in der Linux-Kernel-Quelle definiert und wird auch von vielen Dateisystemen übernommen, die unter Unix / Linux-Betriebssystemen funktionieren, einschließlich ext3- und ext4-Dateisystemen (Ubuntu-Standard). Interessant: Da Daten nur Informationsblöcke sind, verfügt Linux über die Funktion inode_init_always , mit der festgestellt werden kann, ob es sich bei einem Inode um eine Pipe ( inode->i_pipe
) handelt. Ja, Sockets und Pipes sind technisch gesehen auch Dateien - anonyme Dateien, die möglicherweise keinen Dateinamen auf der Festplatte haben. FIFOs und Unix-Domain-Sockets haben Dateinamen im Dateisystem.
Daten selbst können eindeutig sein, Inode-Nummern sind jedoch nicht eindeutig. Wenn wir eine feste Verbindung zu foo haben, die foobar genannt wird, zeigt das auch auf Inode 123. Dieser Inode selbst enthält Informationen darüber, welche tatsächlichen Plattenspeicherblöcke von diesem Inode belegt sind. Und auf diese Weise können Sie technisch .
mit dem Verzeichnisdateinamen verknüpft werden. Fast schon: Sie können unter Linux selbst keine Hardlinks zu Verzeichnissen erstellen , aber Dateisysteme können Hardlinks zu Verzeichnissen auf sehr disziplinierte Weise zulassen, was die Einschränkung darstellt, dass nur .
und ..
als Hardlinks vorhanden sind.
Dateisysteme implementieren einen Verzeichnisbaum als eine der Baumdatenstrukturen. Im Speziellen,
Der entscheidende Punkt hierbei ist, dass Verzeichnisse selbst Knoten in einem Baum und Unterverzeichnisse untergeordnete Knoten sind, wobei jedes Kind eine Verknüpfung zum übergeordneten Knoten hat. Für eine Verzeichnisverknüpfung beträgt die Inode-Anzahl also mindestens 2 für ein unbeschriebenes Verzeichnis (Verknüpfung mit dem Verzeichnisnamen /home/example/
und Verknüpfung mit self /home/example/.
), und jedes zusätzliche Unterverzeichnis ist eine zusätzliche Verknüpfung / ein zusätzlicher Knoten:
# new directory has link count of 2
$ stat --format=%h .
2
# Adding subdirectories increases link count
$ mkdir subdir1
$ stat --format=%h .
3
$ mkdir subdir2
$ stat --format=%h .
4
# Count of links for root
$ stat --format=%h /
25
# Count of subdirectories, minus .
$ find / -maxdepth 1 -type d | wc -l
24
Das Diagramm auf der Kursseite von Ian D. Allen zeigt ein vereinfachtes, sehr übersichtliches Diagramm:
WRONG - names on things RIGHT - names above things
======================= ==========================
R O O T ---> [etc,bin,home] <-- ROOT directory
/ | \ / | \
etc bin home ---> [passwd] [ls,rm] [abcd0001]
| / \ \ | / \ |
| ls rm abcd0001 ---> | <data> <data> [.bashrc]
| | | |
passwd .bashrc ---> <data> <data>
Das einzige, was im RECHTEN Diagramm falsch ist, ist, dass Dateien technisch gesehen nicht im Verzeichnisbaum selbst liegen: Das Hinzufügen einer Datei hat keine Auswirkungen auf die Anzahl der Verknüpfungen:
$ mkdir subdir2
$ stat --format=%h .
4
# Adding files doesn't make difference
$ cp /etc/passwd passwd.copy
$ stat --format=%h .
4
Um Linus Torvalds zu zitieren :
Der springende Punkt bei "Alles ist eine Datei" ist nicht, dass Sie einen zufälligen Dateinamen haben (in der Tat zeigen Sockets und Pipes, dass "Datei" und "Dateiname" nichts miteinander zu tun haben), sondern die Tatsache, dass Sie gemeinsam verwenden können Werkzeuge, um an verschiedenen Dingen zu arbeiten.
Bedenkt man, dass ein Verzeichnis nur ein Spezialfall einer Datei ist, natürlich muss es APIs, die es uns ermöglichen , öffnen / lesen / schreiben / schließen sie in ähnlicher Weise wie normale Dateien.
Hier kommt die dirent.h
C-Bibliothek ins Spiel , die die dirent
Struktur definiert , die Sie in man 3 readdir finden können :
struct dirent {
ino_t d_ino; /* Inode number */
off_t d_off; /* Not an offset; see below */
unsigned short d_reclen; /* Length of this record */
unsigned char d_type; /* Type of file; not supported
by all filesystem types */
char d_name[256]; /* Null-terminated filename */
};
Daher müssen Sie in Ihrem C-Code definieren struct dirent *entry_p
, und wenn wir ein Verzeichnis mit öffnen opendir()
und mit dem Lesen beginnen readdir()
, speichern wir jedes Element in dieser entry_p
Struktur. Natürlich enthält jeder Artikel die Felder, die in der dirent
oben gezeigten Vorlage definiert sind .
Das praktische Beispiel, wie dies funktioniert, finden Sie in meiner Antwort auf So listen Sie Dateien und ihre Inode-Nummern im aktuellen Arbeitsverzeichnis auf .
Beachten Sie, dass das POSIX-Handbuch zu fdopen angibt, dass "[t] die Verzeichniseinträge für Punkt und Punkt-Punkt optional sind", und dass der manuelle Status von readdir struct dirent
nur erforderlich ist, um d_name
und d_ino
Felder zu haben .
Hinweis zum "Schreiben" in Verzeichnisse: Beim Schreiben in ein Verzeichnis wird die "Liste" der Einträge geändert. Das Erstellen oder Entfernen einer Datei ist daher direkt mit Verzeichnisschreibberechtigungen verbunden , und das Hinzufügen / Entfernen von Dateien ist der Schreibvorgang für dieses Verzeichnis.
open()
undread()
Sockets habenconnect()
undread()
auch. Was genauer wäre, ist, dass "Datei" wirklich "Daten" sind, die entweder auf der Festplatte oder im Speicher gespeichert sind, und einige Dateien anonym sind - sie haben keinen Dateinamen. Normalerweise denken Benutzer an Dateien mit diesem Symbol auf dem Desktop, aber das ist nicht das einzige, was existiert. Siehe auch unix.stackexchange.com/a/116616/85039