カレントディレクトリとは何か (Unix系OSの場合)
結論
カレントディレクトリ (以下cwd) は、以下の2つの組み合わせで管理される、プロセス状態の構成要素です。
- 物理的なcwd: ディレクトリそのものを指すポインタ。カーネル側で管理される。
-
論理的なcwd: ディレクトリパス。
PWDという名前の環境変数を経由して、ユーザーランドで管理される。
ポイント
一般論として、ファイルを特定する方法には、ファイルへのポインタを保持する方法とパスを保持する方法の2通りがあります。この2つの方法は一長一短で、様々な場面で振る舞いが異なります。
- ファイルポインタを保持する方法は、同じファイルシステム内でのファイルの 移動やリネーム を追跡します。一方、パスを保持する場合は移動やリネームを追跡せず、かわりに元の場所に新しくファイルが作られればそちらを参照するようになります。
- ファイルポインタを保持する方法は、ファイルが削除されてもファイルの実体を参照し続けます。一方、パスを保持する場合は削除されたファイルにはアクセスできなくなります。
- ファイルポインタを保持する方法は、 経路情報 を保持しません。つまり、symlinkなどで同じファイルを複数の方法で参照できるとき、どの方法でアクセスしたかはファイルポインタには記録されていません。いっぽう、パスはこうした情報を保持します。
したがって、cwdにも同様の問題が発生します。
Unix系OSでは、両方の方法を組み合わせることで両者の利点を得ることを目指しています。
どのように組み合わせているのか
シェル以外の場合
open(2) などの各種ファイルシステムAPIは、相対パスを物理的なcwdに基づいて解決します。また、 chdir(2) や getcwd(3) は物理的なcwdに対する操作です。多くのプログラミング言語はこうしたAPIに対する通常のバインディングを提供するため、 Rubyなどのシェル以外のプログラミング言語 を使って書いたプログラムはたいてい、 物理的なcwd に対して動作します。
シェルの場合
いっぽう、 シェルは異なる動作をします 。POSIX.1-2024のcd (1) の説明を要約すると、以下のようになります。 (CDPATH に関する動作等は省略しています)
-
cdには 物理モード であるcd -Pと 論理モード であるcd -Lがあり、デフォルトでは論理モードで動作する。 - 物理モードでは以下のように振る舞う。
- 物理的なcwdを、物理的なcwdからの相対で更新する。
- 論理的なcwdを、物理的なcwdから逆引きして更新する。
- 論理モードでは以下のように振る舞う。
- 論理的なcwdを、論理的なcwdからの相対で更新する。
- 物理的なcwdを、論理的なcwdを解決することで更新する。 (ただし、解決に失敗したら論理的なcwdの変更もリバートする)
また、 pwd にも pwd -P と pwd -L があり、 pwd -L がデフォルトです。
つまり、シェルは 論理的なcwd に基づいて pwd/cd を行います。ただし、その他の操作は依然として 物理的なcwd に基づくため、その挙動はハイブリッド的になります。
シェル起動時の挙動
加えて、 PWD 変数の項 ではざっくり以下のような規定があります。
- シェルの起動時、物理的なcwdと論理的なcwdが一致しなければ、 物理的なcwd からパスを逆引きして論理的なcwdを上書きする。
この規定は、cd以外の場面ではシェルが 物理的なcwdを優先して論理的なcwdを同期する ことを示唆しています (実際にbashやzshで実験してみるとシェルの起動時以外にもこうした挙動になることが確認できます)。
まとめ
- Unix系OSにおいて、カレントディレクトリは物理/論理の二重の状態として管理されている。
- 論理的なカレントディレクトリはシェルで利用される。
- 物理的なカレントディレクトリと論理的なカレントディレクトリは特定の場面で同期される。
Discussion
具体例だとこういう感じですよね
cwdまたは親ディレクトリがリネームされた場合、削除された場合、アクセス権限がない場合なども重要ですね。