web-dev-qa-db-ja.com

Unixは、ファイルシステムをナビゲートするときに、ユーザーの作業ディレクトリをどのように追跡しますか?

UNIXシステムのシェルにログインして、コマンドのタップを開始するとします。最初はユーザーのホームディレクトリ~。そこからcdからディレクトリDocumentsに移動するかもしれません。

ここで作業ディレクトリを変更するコマンドは、直感的に理解するのが非常に簡単です。親ノードには、アクセスできる子ノードのリストがあり、おそらく検索の(最適化された)バリアントを使用して、子ノードの存在を特定します。ユーザーが入力した名前を入力すると、作業ディレクトリがこれに合わせて「変更」されます—間違っている場合は修正してください。シェルがユーザーの希望どおりに「単純に」ディレクトリに正確にアクセスしようと試み、ファイルシステムがなんらかのタイプのエラーを返すと、シェルはそれに応じて応答を表示することがさらに簡単になる場合があります。

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

ファイルシステムツリー全体でその名前のディレクトリのおそらく多くのディレクトリの1つであるDocumentsの不明な、おそらく「ブラインド」な場所を考えると、Unixは次に配置する場所をどのように決定しますか?pwdへの参照を作成し、それを調べますか?はいの場合、pwdは現在のナビゲーション状態をどのように追跡しますか?

29

他の答えは単純化しすぎて、それぞれが話の一部だけを示しており、いくつかの点で間違っています。

作業ディレクトリを追跡する方法は 2つあります。

  • すべてのプロセスについて、そのプロセスを表すカーネル空間データ構造で、カーネルは、そのプロセスの作業ディレクトリとルートディレクトリのvnodeへの2つのvnode参照を格納します。前者の参照はchdir()およびfchdir()システムコールによって設定され、後者はchroot()によって設定されます。 Linuxオペレーティングシステムの_/proc_で間接的に、またはFreeBSDのfstatコマンドを介して、それらを間接的に見ることができます。
    %fstat -p $$ | head -n 5
    ユーザーCMD 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 root/4 drwxr-xr-x 35 r 
    

    パス名解決が機能する場合、パスが相対パスか絶対パスかに応じて、参照されているvnodeのいずれかから始まります。 (3番目のオプションとして、オープン(ディレクトリ)ファイル記述子によって参照されるvnodeでパス名の解決を開始できる…at()システムコールのファミリがあります。)

    マイクロカーネルのUnicesでは、データ構造はアプリケーション空間にありますが、これらのディレクトリへのオープン参照を保持する原則は同じです。

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

    相対パス名に変更すると、文字列を操作してその名前を追加します。絶対パス名に変更すると、文字列が新しい名前に置き換えられます。どちらの場合も、文字列を調整して_._および_.._コンポーネントを削除し、シンボリックリンクを追跡して、それらをリンク先の名前に置き換えます。 ( これがそのためのZシェルのコードです など)

    内部文字列変数の名前は、 Shell variable という名前のPWD(またはCシェルではcwd)によって追跡されます。これは通常、シェルによって生成されたプログラムに環境変数(PWDという名前)としてエクスポートされます。

物事を追跡するこれらの2つの方法は、cdおよびpwdシェル組み込みコマンドの_-P_および_-L_オプション、およびシェル間の違いによって明らかになります'組み込みpwdコマンドと、_/bin/pwd_コマンドと(他のもののような)組み込みのpwdコマンドの両方VIMおよびNeoVIM。

%mkdir a; ln -sab%(cd b; pwd;/bin/pwd; printenv 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; printenv PWD) 
/usr/home/JdeBP/a 
/usr/home/JdeBP/a 
/usr/home/JdeBP/a 
%(cd b; PWD =/hello/there/bin/pwd -L)
/usr/home/JdeBP /a
%

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

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

したがって、(POSIX規格にあるように)PWDで指定された名前が_._という名前と同じであることを確認します(システムコールトレースで確認できます)。

%ln -sac%(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( "。"、 {mode = drwxr-xr-x、inode = 120932、size = 2、blksize = 131072})= 0(0x0) 
/usr/home/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( "。"、{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 'No such file or directory'  __ 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 

ご覧のとおり、不一致を検出した場合にのみgetcwd()を呼び出します。 PWDを実際に同じディレクトリに名前を付ける文字列に設定することでだまされますが、ルートは異なります。

getcwd()ライブラリ関数は、それ自体がサブジェクトです。しかし、précis:

  • もともとは純粋にライブラリ関数でしたが、_.._ディレクトリで作業ディレクトリを繰り返し検索して、作業ディレクトリからルートまでのパス名を作成していました。 _.._がその作業ディレクトリと同じであるループに達したとき、または次の_.._を開こうとしてエラーが発生したときに停止しました。これは、内部では多くのシステムコールになります。
  • 最近の状況は少し複雑です。たとえば、FreeBSDでは(これは他のオペレーティングシステムでも同様です)、 is は真のシステムコールです。これは、前述のシステムコールトレースで確認できます。作業ディレクトリvnodeからルートまでのすべてのトラバーサルは単一のシステムコールで行われます。これは、カーネルモードコードがディレクトリエントリキャッシュに直接アクセスするなどの利点を活用して、パス名コンポーネントの検索をより効率的に行います。

    ただし、FreeBSDやその他のオペレーティングシステムでも、カーネルは /が作業ディレクトリを文字列で追跡しないことに注意してください。

_.._に移動することも、それ自体が主題です。別の原則:従来のディレクトリ(ただし、既に言及したように、これは not が必要)ですが、ディスク上のディレクトリデータ構造に実際の_.._が含まれていますが、カーネルは各ディレクトリvnode自体の親ディレクトリ。したがって、任意の作業ディレクトリの_.._ vnodeに移動できます。これは、この回答の範囲を超えているマウントポイントと変更されたルートメカニズムによってやや複雑です。

さておき

実際、Windows NTでも同様のことが行われます。プロセスごとに1つの作業ディレクトリがあり、SetCurrentDirectory() AP​​I呼び出しによって設定され、そのディレクトリへの(内部)オープンファイルハンドルを介してカーネルによってプロセスごとに追跡されます。また、Win32プログラム(コマンドインタープリターだけでなく、 all Win32プログラム)が複数の作業ディレクトリ(ドライブごとに1つ)の名前を追跡するために使用する一連の環境変数が追加されています。彼らがディレクトリを変更するときはいつでも、それらを上書きしたり上書きしたりします。

従来、UnixおよびLinuxオペレーティングシステムの場合とは異なり、Win32プログラムはこれらの環境変数をユーザーに表示しません。ただし、Windows NTで実行されているUnixライクなサブシステムや、特定の方法でコマンドインタープリターのSETコマンドを使用することで、これらを見ることができます。

参考文献

76
JdeBP

カーネルはディレクトリまたはファイル名を追跡しません。ファイルまたはディレクトリは、カーネルではiノード/デバイスのペアで表されます。 chdir()open()などのシステムコールは、絶対パス(例:_/etc/passwd_)または現在のディレクトリからの相対パス(例:Documents)をパラメーターとして使用します。 、_.._)。プロセスがchdir("Documents")を実行すると、現在の作業ディレクトリでDocumentsが検索され、このディレクトリを参照するようにプロセスの作業ディレクトリが更新されます。カーネルの観点からは、「..」という名前に特別なものはありません。これは、_.._が親ディレクトリを参照するというファイルシステムの規則にすぎません。

getcwd()関数はシステムコールではありませんが、ルートディレクトリまで上手く機能し、途中でパスコンポーネントの名前を記録する必要があるライブラリ関数です。

1
Johan Myréen

興味深いことに、従来の_cd .._はpwdよりもはるかに単純です。 _.._という名前のディレクトリは、明示的にファイルシステムに配置されます。システムは現在のディレクトリのデバイス/ iノードを追跡しているため、_cd .._またはより正確には、システムコールchdir("..")は、現在のディレクトリに属する​​ファイルで ".."という名前を検索するだけです。ディレクトリのiノードと現在のディレクトリのデバイス/ iノードをそこにある値に変更します。

pwd(より正確には_/bin/pwd_)は_.._リンクを順にたどり、それぞれのディレクトリを読み取り、元のiノードを見つけて、ルートに到達するまでそれらの名前のリストを逆に組み立てますディレクトリ(特に_.._エントリを含まない)。

これが元の低レベルの基本動作です。実際のシェルコマンドpwdは代わりに、現在のパス名をキャッシュするさまざまな手法に依存しています。しかし、コアでは、実際に知られているのはそのiノードだけです。これは、ディレクトリのナビゲートにシンボリックリンクが使用されると、現在のシェルとシステムの現在の作業ディレクトリ名の表記法_/bin/pwd_が異なる可能性があることを意味します。

0
user267857