横に

横に

UNIXシステムのシェルにログインし、コマンドの入力を開始するとします。最初はユーザーのホームディレクトリから始めました~。おそらくそこからcdディレクトリに行きますDocuments

ここで作業ディレクトリを変更するコマンドは、理解しやすくシンプルで直感的です。親ノードにはアクセス可能な子ノードのリストがあり、おそらく検索の(最適化された)バリエーションを使用して子ノードの存在を見つけます。ユーザーが入力した名前を入力し、それに合わせて作業ディレクトリを「変更」します。私が間違っている場合は訂正してください。シェルがユーザーが意図したとおりに正確に「素早く」ディレクトリにアクセスしようとし、ファイルシステムが何らかの種類のエラーを返す場合、シェルはそれに応じて応答を表示する方が簡単かもしれません。

しかし、私はディレクトリの上に移動するとき(つまり、親ディレクトリまたは親ディレクトリの親ディレクトリに)、同じプロセスがどのように機能するかに興味があります。

私の未知の、おそらく「隠された」場所(Documentsファイルシステムツリー全体でその名前を持つ複数の可能なディレクトリの1つ)が与えられた場合、Unixは私が次に配置する場所をどのように決定しますか?これを参照pwdして確認しますか?では、pwd現在のナビゲーション状態を追跡する方法は?

答え1

他の回答は過度に単純化され、各回答は物語の一部だけを提示し、いくつかの点では間違っています。

持つ二つ作業ディレクトリを追跡するには:

  • 各プロセスに対して、カーネルは、プロセスを表すカーネル空間データ構造のルートディレクトリと、プロセス作業ディレクトリの vnode への 2 つの vnode 参照を格納します。前者への参照はchdir()システムコールによって確立され、fchdir()後者はLinuxオペレーティングシステムで間接的に表示されるか、FreeBSDなどのオペレーティングシステムからのコマンドで表示chroot()できます。/procfstat

    % fstat -p $$|head -n 5
    ユーザーコマンドPID FDインストールINUMモード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 ルート/4 drwxr-xr-x 35r
    %

    パス名解決が実行されると、パスが相対パスであるか絶対パスであるかによって、参照されている仮想ノードのいずれかで始まります。…at()(3番目のオプションで開かれた(ディレクトリ)ファイル記述子が参照するvnodeでパス名の解決を開始できるようにする一連のシステムコールがあります。)

    マイクロカーネルユニセスでは、データ構造はアプリケーション空間にありますが、これらのディレクトリへの公開参照を維持する原則は変わりません。

  • 内部的には、Z、Korn、Bourne Again、C、Almquistシェルなどのシェルからまた内部文字列変数の文字列操作を使用して作業ディレクトリを追跡します。呼び出す理由があるたびにこれを行いますchdir()

    相対パス名に変更した場合は、文字列に名前を追加します。絶対パス名に変更すると、文字列は新しい名前に置き換えられます。どちらの場合も、削除する文字列...コンポーネントを調整し、シンボリックリンクをたどり、リンクされた名前に置き換えます。 (これはZシェルのコードです。、例えば。 )

    内部文字列変数の名前は次のとおりです。シェル変数名前PWD(またはcwdCシェル)。通常、PWDシェル生成プログラムに環境変数(名前指定)としてエクスポートされます。

物事を追跡するこの2つの方法は、シェルに組み込まれているコマンドのオプションと、シェルの組み込みコマンドとコマンドと、VIMやNeoVIMなどの組み込み機能との違いによって明らかに-Pなります-Lcdpwdpwd/bin/pwdpwd

% mkdir a;リソソーム
%(cd b; パスワード; /bin/pwd; printenv PWD)
/usr/ホーム/JdeBP/b
/usr/ホーム/JdeBP/a
/usr/ホーム/JdeBP/b
%(cd b; パスワード -P; /bin/pwd -P)
/usr/ホーム/JdeBP/a
/usr/ホーム/JdeBP/a
%(cd b; pwd -L; /bin/pwd -L)
/usr/ホーム/JdeBP/b
/usr/ホーム/JdeBP/b
%(cd -P b; パスワード; /bin/pwd; printenv PWD)
/usr/ホーム/JdeBP/a
/usr/ホーム/JdeBP/a
/usr/ホーム/JdeBP/a
% (cd b; PWD=/hello/そこ /bin/pwd -L)
/usr/ホーム/JdeBP/a
%

ご覧のとおり、「論理」作業ディレクトリを取得するのは、単にシェルPWD変数(またはシェルプログラムではない場合は環境変数)を見ることです。 「物理的」作業ディレクトリを取得することは、ライブラリ関数を呼び出すことですgetcwd()

/bin/pwdこのオプションを使用すると、プログラムの動作はやや微妙です-L。それ信じられないPWD継承する環境変数の値です。結局、これはシェルによって呼び出される必要はなく、介入プログラムはPWD作業ディレクトリの名前を追跡する環境変数を保持するシェルメカニズムを実装しない可能性があります。あるいは、私がやったことを誰かがするかもしれません。

したがって、これは(POSIX標準によると)システムコールトレースに示されているように、提供された名前がPWDnameと同じであることを確認することです。.

嚢胞内%
% (cd b; truss /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(".",{ モード=drwxr-xr-x ,inode=120932,size=2,blksize=131072 }) = 0 (0x0)
/usr/ホーム/JdeBP/b
% (cd b; PWD=/usr/local/etc truss /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(".",{ モード=drwxr-xr-x ,inode=120932,size=2,blksize=131072 }) = 0 (0x0)
__getcwd("/usr/home/JdeBP/a",1024) = 0 (0x0)
/usr/ホーム/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 '該当するファイルやディレクトリはありません'
__getcwd("/usr/home/JdeBP/a",1024) = 0 (0x0)
/usr/ホーム/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(".",{ モード=drwxr-xr-x ,inode=120932,size=2,blksize=131072 }) = 0 (0x0)
/usr/ホーム/JdeBP/c
%

ご覧のとおり、不一致getcwd()が検出された場合にのみ呼び出され、PWD同じディレクトリ名を別のパスに指定する文字列を設定することでだますことができます。

ライブラリgetcwd()機能はそれ自体が1つの規律です。しかし、簡単に言えば:

  • もともとこれは、ディレクトリ内の作業ディレクトリを繰り返し見つけようとし、作業ディレクトリからルートディレクトリにパス名を構築する純粋にライブラリ関数でした..。作業ディレクトリと同じループに到達するか、..次のディレクトリを開こうとするとエラーが発生すると停止します..。これにより、後ろから多くのシステムコールが発生します。
  • 今、状況はもう少し複雑になりました。たとえば、FreeBSDでは(他のオペレーティングシステムでも同様です)はい前述のシステムコールのトレースに示すように、実際のシステムコールです。作業ディレクトリ vnode からルートディレクトリへのすべてのナビゲーションは、単一のシステムコールで実行されます。これは、より効率的なパス名コンポーネントの検索のためにディレクトリエントリキャッシュに直接アクセスするカーネルモードコードなどの機能を利用します。

    しかし、FreeBSDや他のオペレーティングシステムでもカーネルは確かに文字列を使用して作業ディレクトリを追跡します。

ナビゲーションは..それ自体が別のトピックです。別の概要:伝統的に目次ですが(すでに述べたように、これはいいえ必須)..には、ディスク上の物理ディレクトリデータ構造が含まれており、カーネルは各ディレクトリvnode自体の親ディレクトリを追跡して、..すべての作業ディレクトリのvnodeに移動できます。これはマウントポイントと変更されたルートメカニズムのために少し複雑であり、これはこの回答の範囲外です。

横に

Windows NTは実際に同様のことを行います。各プロセスSetCurrentDirectory()には、API呼び出しによって設定され、そのディレクトリへの(内部)開かれたファイルハンドルと、Win32プログラミング用の一連の環境変数を介してカーネルによって追跡される作業ディレクトリがあります。みんなWin32プログラム)複数の作業ディレクトリ(各ドライブに1つずつ)の名前を追跡し、ディレクトリが変更されるたびにそのディレクトリを追加または上書きするために使用されます。

通常、Unix および Linux オペレーティング システムとは異なり、Win32 プログラムはこれらの環境変数をユーザーに表示しません。ただし、Windows NTで実行されているUnixファミリーサブシステム、またはSET特定の方法でコマンドソルバーを使用するコマンドを使用することがあります。

追加読書

答え2

カーネルはディレクトリやファイル名を追跡しません。ファイルまたはディレクトリは、inode/デバイスのペアとしてカーネルに表示されます。chdir()、その他のシステムコールは、絶対open()パス(たとえば/etc/passwd)、または現在のディレクトリへの相対パス(たとえば、)などのパラメータDocumentsとして..パスを使用します。プロセスが実行されると、chdir("Documents")現在の作業ディレクトリで照会が行われ、Documentsプロセスの作業ディレクトリがそのディレクトリを参照するように更新されます。カーネルの観点から見ると、「..」という名前には特別なものはなく、親ディレクトリを..参照するためのファイルシステムの慣例にすぎません。

これgetcwd()はシステムコールではなく、ルートディレクトリまで実行され、パスコンポーネントの名前を記録する必要があるライブラリ関数です。

答え3

興味深いことに。cd ..pwd指定されたディレクトリは、伝統的に..ファイルシステムに明示的に配置されます。システムは現在のディレクトリのデバイス/インデックスノードを追跡するので、cd ..より正確には、システムコールはchdir("..")現在のディレクトリのinodeに属するファイルで「..」という名前を探してデバイス/インデックスノードを変更するだけです。現在のディレクトリの値を取得します。

pwd(より正確には/bin/pwd..引き続きリンクをたどり、そのディレクトリのソースであるinodeが見つかるまでそのディレクトリを読み、ルートディレクトリ(特にエントリを含まない..)に達するまで、その名前のリストを逆順に組み立てます。

これで、これは元の低レベルのデフォルト動作です。代わりに、実際のシェルコマンドはpwd現在のパス名をキャッシュするためのさまざまな技術を使用します。しかし、本質的に実際に知られているのはインデックスノードだけです。これは、シンボリックリンクを使用してディレクトリを参照すると、現在の作業ディレクトリ名の現在のシェルとシステムの概念が異なる可能性/bin/pwdがあることを意味します。

関連情報