Wie verfolgt Unix das Arbeitsverzeichnis eines Benutzers beim Navigieren im Dateisystem?

29

Angenommen, ich melde mich in einer Shell auf einem Unix-System an und beginne, Befehle abzuklopfen. Ich beginne zunächst im Home-Verzeichnis meines Benutzers ~. Ich könnte von dort cdrunter ins Telefonbuch Documents.

Der Befehl zum Ändern des Arbeitsverzeichnisses ist hier sehr einfach und intuitiv zu verstehen: Der übergeordnete Knoten verfügt über eine Liste von untergeordneten Knoten, auf die er zugreifen kann, und verwendet vermutlich eine (optimierte) Variante einer Suche, um die Existenz eines untergeordneten Knotens mit der zu lokalisieren Name des Benutzers eingegeben, und das Arbeitsverzeichnis wird dann "geändert", um dies zu entsprechen - korrigieren Sie mich, wenn ich mich dort irre. Es kann sogar einfacher sein, dass die Shell einfach "naiv" versucht, genau nach den Wünschen des Benutzers auf das Verzeichnis zuzugreifen. Wenn das Dateisystem eine Art Fehler zurückgibt, zeigt die Shell eine entsprechende Antwort an.

Was mich jedoch interessiert, ist, wie der gleiche Prozess funktioniert, wenn ich in einem Verzeichnis navigiere, dh zu einem Elternteil oder einem Elternteil.

DocumentsWie bestimmt Unix angesichts meines unbekannten, vermutlich "blinden" Standorts eines von möglicherweise vielen Verzeichnissen im gesamten Dateisystembaum mit diesem Namen, wo ich als nächstes platziert werden soll? Wird darauf Bezug genommen pwdund geprüft? Wenn ja, wie wird pwdder aktuelle Navigationsstatus verfolgt?

ReactingToAngularVues
quelle
1
Siehe auch Rekursion symbolischer Links - was macht es zum "Zurücksetzen"? wo viel davon erklärt wird
Stéphane Chazelas

Antworten:

76

Die anderen Antworten sind Übervereinfachungen, die jeweils nur Teile der Geschichte darstellen und in einigen Punkten falsch sind.

Es gibt zwei Möglichkeiten, um das Arbeitsverzeichnis zu verfolgen:

  • Für jeden Prozess speichert der Kernel in der Kernel-Space-Datenstruktur, die diesen Prozess darstellt, zwei V-Knoten-Verweise auf die V-Knoten des Arbeitsverzeichnisses und des Stammverzeichnisses für diesen Prozess. Die erstere Referenz wird durch die Menge chdir()und die fchdir()Systemaufrufe, wobei letztere durch chroot(). Man kann sie indirekt /procunter Linux-Betriebssystemen oder über den fstatBefehl unter FreeBSD und dergleichen sehen:

    % fstat -p $$ | head -n 5
    BENUTZER CMD PID FD MONTAGE INUM MODUS SZ | DV R / W
    JdeBP zsh 92648 text / 24958 -r-xr-xr-x 702360 r
    JdeBP zsh 92648 ctty / dev 148 crw - w ---- pts / 4 rw
    JdeBP zsh 92648 wd / usr / home / JdeBP 4 drwxr-xr-x 124 r
    JdeBP zsh 92648 root / 4 drwxr-xr-x 35 r
    % 

    Wenn die Pfadnamenauflösung ausgeführt wird, beginnt sie bei dem einen oder anderen dieser referenzierten Knoten, je nachdem, ob der Pfad relativ oder absolut ist. (Es gibt eine …at()Reihe von Systemaufrufen, mit denen die Pfadnamenauflösung an dem Knoten beginnen kann, auf den ein offener (Verzeichnis-) Dateideskriptor als dritte Option verweist.)

    In Microkernel Unices befindet sich die Datenstruktur im Anwendungsbereich, aber das Prinzip, Verweise auf diese Verzeichnisse offen zu halten, bleibt dasselbe.

  • Intern verfolgt die Shell in Shells wie der Z-, Korn-, Bourne Again-, C- und Almquist-Shell außerdem das Arbeitsverzeichnis mithilfe der Zeichenfolgenmanipulation einer internen Zeichenfolgenvariablen. Dies geschieht immer dann, wenn es Grund zum Aufrufen hat chdir().

    Wenn ein relativer Pfadname geändert wird, wird die Zeichenfolge bearbeitet, um diesen Namen anzufügen. Wenn ein absoluter Pfadname geändert wird, wird die Zeichenfolge durch den neuen Namen ersetzt. In beiden Fällen wird die Zeichenfolge angepasst, um Komponenten zu entfernen .und ..symbolische Verknüpfungen aufzuspüren, indem sie durch ihre verknüpften Namen ersetzt werden. ( Hier ist zum Beispiel der Code der Z-Shell .)

    Der Name in der internen Zeichenfolgenvariablen wird von einer Shell-Variablen namens PWD(oder cwdin den C-Shells) verfolgt. Dies wird herkömmlicherweise als Umgebungsvariable (benannt PWD) in Programme exportiert, die von der Shell erzeugt werden.

Diese beiden Methoden der Verfolgung Dinge enthüllt werden von den -Pund -LOptionen auf die cdund pwdShell integrierte Befehle, und durch die Unterschiede zwischen den Schalen built-in pwdBefehle und sowohl den /bin/pwdBefehl und die eingebaute in pwdBefehle von Dingen wie (unter anderem) VIM und NeoVIM.

% mkdir a; ln -sab 
% (cd b; pwd; / bin / pwd; Druck-PWD)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / a
/ usr / home / JdeBP / b
% (cd b; pwd -P; / bin / pwd -P)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; pwd -L; / bin / pwd -L)
/ usr / home / JdeBP / b
/ usr / home / JdeBP / b
% (cd -P b; pwd; / bin / pwd; Druck-PWD)
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
/ usr / home / JdeBP / a
% (cd b; PWD = / hallo / dort / bin / pwd -L)
/ usr / home / JdeBP / a
% 

Wie Sie sehen können: Um das "logische" Arbeitsverzeichnis zu erhalten, müssen Sie sich die PWDShell-Variable (oder die Umgebungsvariable, falls es sich nicht um das Shell-Programm handelt) ansehen. Um das "physische" Arbeitsverzeichnis zu erhalten, muss die getcwd()Bibliotheksfunktion aufgerufen werden.

Die Bedienung des /bin/pwdProgramms bei Verwendung der -LOption ist etwas subtil. Es kann dem Wert der PWDUmgebungsvariablen, die es geerbt hat, nicht vertrauen . Schließlich muss es nicht von einer Shell aufgerufen worden sein, und möglicherweise haben intervenierende Programme den Mechanismus der Shell, mit dem die PWDUmgebungsvariable immer den Namen des Arbeitsverzeichnisses verfolgt, nicht implementiert . Oder jemand kann das tun, was ich gerade dort getan habe.

Es wird also (wie der POSIX-Standard sagt) überprüft, ob der in angegebene Name PWDdas Gleiche ergibt wie der Name ., wie bei einem Systemaufruf-Trace zu sehen ist:

% ln -sac 
% (cd b; Fachwerk / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / b", { mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0)
/ usr / home / JdeBP / b
% (cd b; PWD = / usr / local / etc Fachwerk / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / local / etc" , {mode = drwxr-xr-x, inode = 14835, size = 158, blksize = 10240}) = 0 (0x0) 
stat (".", {mode = drwxr-xr-x, inode = 120932, size = 2 , blksize = 131072}) = 0 (0x0)
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / hello / there truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ hello / there", 0x7fffffffe730) ERR # 2 'Keine solche Datei oder kein solches Verzeichnis' 
__getcwd ("/ usr / home / JdeBP / a", 1024) = 0 (0x0)
/ usr / home / JdeBP / a
% (cd b; PWD = / usr / home / JdeBP / c truss / bin / pwd -L 3> & 1 1> & 2 2> & 3 | grep -E '^ stat | __getcwd') 
stat ("/ usr / home / JdeBP / c ", {mode = drwxr-xr-x, inode = 120932, size = 2, blksize = 131072}) = 0 (0x0) 
stat (". ", {Mode = drwxr-xr-x, inode = 120932 , size = 2, blksize = 131072}) = 0 (0x0)
/ usr / home / JdeBP / c
%

Wie Sie sehen können: Es ruft nur auf, getcwd()wenn es eine Nichtübereinstimmung feststellt. und es kann getäuscht werden, indem PWDeine Zeichenfolge festgelegt wird, die zwar dasselbe Verzeichnis benennt, jedoch auf einem anderen Weg.

Die getcwd()Bibliotheksfunktion ist ein eigenständiges Fach. Aber vorab:

  • Ursprünglich war es eine reine Bibliotheksfunktion, die einen Pfadnamen vom Arbeitsverzeichnis bis zum Stammverzeichnis aufbaute, indem sie wiederholt versuchte, das Arbeitsverzeichnis im Verzeichnis ..nachzuschlagen. Es wurde angehalten, als es eine Schleife erreichte, in der ..es sich um das Arbeitsverzeichnis handelte, oder wenn ein Fehler beim Öffnen des nächsten Verzeichnisses auftrat ... Dies wäre eine Menge Systemaufrufe unter der Decke.
  • Heutzutage ist die Situation etwas komplexer. Unter FreeBSD beispielsweise (dies gilt auch für andere Betriebssysteme) handelt es sich um einen echten Systemaufruf, wie Sie in der oben angegebenen Ablaufverfolgung für Systemaufrufe sehen können. Der gesamte Durchlauf vom Arbeitsverzeichnisknoten bis zum Stammverzeichnis erfolgt in einem einzigen Systemaufruf. Dabei wird beispielsweise der direkte Zugriff des Kernelmoduscodes auf den Verzeichniseintrags-Cache genutzt, um die Suche nach Pfadnamen-Komponenten wesentlich effizienter zu gestalten.

    Beachten Sie jedoch, dass auch auf FreeBSD und die anderen Betriebssysteme der Kernel nicht funktioniert mit einem String - Spur des Arbeitsverzeichnisses halten.

Das Navigieren zu ..ist wieder ein eigenständiges Thema. Eine weitere Voraussetzung: Obwohl Verzeichnisse herkömmlicherweise (obwohl, wie bereits erwähnt, dies nicht erforderlich ist) eine tatsächliche ..Verzeichnisdatenstruktur auf der ..Festplatte enthalten , verfolgt der Kernel das übergeordnete Verzeichnis jedes Verzeichnisknotens selbst und kann so zum Knoten eines beliebigen Verzeichnisknotens navigieren Arbeitsverzeichnis. Dies wird durch den Einhängepunkt und geänderte Root-Mechanismen etwas kompliziert, die den Rahmen dieser Antwort sprengen.

Beiseite

Windows NT macht in der Tat eine ähnliche Sache. Es gibt ein einziges Arbeitsverzeichnis pro Prozess, das vom SetCurrentDirectory()API-Aufruf festgelegt und vom Kernel über ein (internes) offenes Dateihandle zu diesem Verzeichnis pro Prozess verfolgt wird. Außerdem gibt es eine Reihe von Umgebungsvariablen, mit denen Win32-Programme (nicht nur die Befehlsinterpreter, sondern alle Win32-Programme) die Namen mehrerer Arbeitsverzeichnisse (eines pro Laufwerk) verfolgen und diese bei jedem Verzeichniswechsel anhängen oder überschreiben.

Im Gegensatz zu Unix- und Linux-Betriebssystemen zeigen Win32-Programme Benutzern diese Umgebungsvariablen normalerweise nicht an. Man kann sie jedoch manchmal in Unix-ähnlichen Subsystemen sehen, die unter Windows NT ausgeführt werden, oder indem man die SETBefehle der Befehlsinterpreter auf eine bestimmte Weise verwendet.

Weitere Lektüre

JdeBP
quelle
1
Das ist weit mehr als ich jemals erwartet habe. Vielen Dank und extra vielen Dank für die weitere Lektüre!
ReactingToAngularVues
doc.cat-v.org/plan_9/4th_edition/papers/lexnames spricht über einige der Probleme ..im Zusammenhang mit Plan9,
icarus
@JdeBP: Vielleicht fehlt mir etwas. Sie sagen: „Intern, innerhalb von…, bash,… und… verfolgt die Shell zusätzlich das Arbeitsverzeichnis mithilfe der Zeichenfolgenmanipulation einer internen Zeichenfolgenvariablen. … Passt die Zeichenfolge an, um Komponenten zu entfernen .und symbolische Verknüpfungen aufzuspüren, indem sie durch ihre verknüpften Namen ersetzt werden. … Der Name in der internen Zeichenfolgenvariablen wird von einer Shell-Variablen mit dem Namen … “(Hervorhebung hinzugefügt) verfolgt. … (Fortsetzung)..PWD
G-Man sagt, dass Monica am
(Fortsetzung)… Aber Ihr Beispiel zeigt PWD= …/bnach einem cd bBefehl, obwohl bes sich um eine symbolische Verknüpfung handelt a- damit die Shell die a -> bVerknüpfung nicht „jagt“ . Haben Sie falsch angegeben oder habe ich falsch gelesen?
G-Man sagt, dass Monica am
Ich habe einfach einen Seitenpunkt beschönigt und Sie auf den Code verwiesen, um Einzelheiten zu erfahren. In den Handbüchern der verschiedenen Shells finden Sie Informationen darüber, wann und wie sie nach symbolischen Links suchen oder nicht. Die Z-Shell ruft einfach ihre Shell-Option auf, die ein Teil der Entscheidungsformel ist CHASE_LINKS.
JdeBP
1

Der Kernel verfolgt weder Verzeichnis- noch Dateinamen. Eine Datei oder ein Verzeichnis wird im Kernel durch ein Inode / Device-Paar dargestellt. Systemaufrufe wie chdir(), open()etc. nehmen einen Pfad als Parameter, die absoluten sein kann ( zum Beispiel /etc/passwd) oder relativ zum aktuellen Verzeichnis (Beispiele: Documents, ..). Wenn ein Prozess ausgeführt wird chdir("Documents"), wird Documentsim aktuellen Arbeitsverzeichnis nachgeschlagen, und das Arbeitsverzeichnis des Prozesses wird aktualisiert, um auf dieses Verzeichnis zu verweisen. Aus der Sicht des Kernels enthält der Name ".." nichts Besonderes. Es handelt sich lediglich um eine Konvention im Dateisystem, die ..auf das übergeordnete Verzeichnis verweist.

Die getcwd()Funktion ist kein Systemaufruf, sondern eine Bibliotheksfunktion, die sich bis zum Stammverzeichnis durcharbeiten muss und dabei die Namen der Pfadkomponenten aufzeichnet.

Johan Myréen
quelle
0

Interessanterweise ist traditionell cd ..viel viel einfacher als pwd. Die genannten Verzeichnisse ..werden explizit im Dateisystem abgelegt. Das System verfolgt das Gerät / den Inode des aktuellen Verzeichnisses, sodass cd ..der Systemaufruf chdir("..")genau genommen nur das Nachschlagen des Namens ".." in der Datei zum Inode des aktuellen Verzeichnisses und das Ändern des Geräts / des Inodes des aktuellen Verzeichnisses in "." Wert dort gefunden.

pwd(genauer /bin/pwd) folgt den ..Links nacheinander und liest die entsprechenden Verzeichnisse, bis der Inode gefunden ist, von dem sie stammen. Die Liste dieser Namen wird umgekehrt zusammengestellt, bis das Stammverzeichnis erreicht ist (insbesondere kein ..Eintrag enthalten ist).

Dies ist das ursprüngliche Grundverhalten auf niedriger Ebene. Tatsächliche Shell-Befehle pwdstützen sich stattdessen auf eine Vielzahl von Techniken, mit denen der aktuelle Pfadname zwischengespeichert wird. Im Kern ist jedoch nur die Inode bekannt. Dies bedeutet, dass, sobald Symlinks zum Navigieren in Verzeichnissen verwendet werden, die aktuellen Bezeichnungen der Arbeitsverzeichnisnamen der aktuellen Shell und des Systems /bin/pwdmöglicherweise abweichen.


quelle