web-dev-qa-db-ja.com

なぜ「。」 Unixのハードリンク?

UnixベースのOSで空のディレクトリのリンク数が1ではなく2である理由については、多くの説明を見てきました。それらはすべて「。」が原因であると言っています。すべてのディレクトリが自分自身を指し示しているディレクトリ。 「。」の概念がある理由がわかります。相対パスを指定するのに便利ですが、ファイルシステムレベルで実装すると何が得られますか?シェルや、パスを取得するシステムコールだけがその解釈方法を知っているのではないでしょうか。

その「..」は実際のリンクであり、私にとってははるかに理にかなっています。ファイルシステムは、親ディレクトリに移動するために、親ディレクトリへのポインタを格納する必要があります。しかし、「。」の理由はわかりません。実際のリンクであることが必要です。また、実装で醜い特殊なケースにつながるようです-リンク数が1未満のiノードによって使用されているスペースしか解放できないと思うかもしれませんが、それらがディレクトリである場合は、実際に確認する必要がありますリンク数は2未満です。なぜ不整合があるのですか?

51
Joseph Garvin

確かに興味深い質問です。一見すると、次のような利点があります。

まず、「. "は、現在のディレクトリがシェルまたはシステムコールによって実行される可能性があるためです。ただし、ディレクトリにドットエントリがあると、実際にはこの必要性がなくなり、より低いレベルでの一貫性が強制されます。

しかし、これがこの設計決定の背後にある基本的な考えだったとは思いません。

ファイルがディレクトリから作成または削除されている場合、ディレクトリの変更タイムスタンプも更新する必要があります。このタイムスタンプはそのiノードに保存されます。 iノード番号は、対応するディレクトリエントリに格納されます。

[〜#〜] if [〜#〜]ドットエントリが存在しない場合、ルーチンは親ディレクトリのこのディレクトリのエントリでiノード番号を検索する必要があり、これにより再度ディレクトリ検索。

[〜#〜] but [〜#〜]幸い、現在のディレクトリにドットエントリがあります。現在のディレクトリでファイルを追加または削除するルーチンは、最初のエントリ(ドットエントリが通常存在する場所)にジャンプして、現在のディレクトリのiノード番号をすぐに見つけるだけです。

ドットエントリには3つ目の良い点があります。

fsckが腐ったファイルシステムをチェックし、フリーリストにもない非接続ブロックを処理する必要がある場合、データブロック(ディレクトリリストとして解釈される場合)にドットがあるかどうかを簡単に確認できますiノードを指しているエントリは、このデータブロックを指すようになっています。その場合、このデータブロックは、再接続する必要がある失われたディレクトリと見なされる場合があります。

37
ktf

(うーん、次はちょっとした叙事詩です...)

UNIXのファイルシステム上のディレクトリの設計(これは、一般的には通常ですが、必ずしもUNIX OSに接続されているわけではありません)は素晴らしい洞察を表し、実際に必要な特殊なケースの数を減らします。

「ディレクトリ」は、実際にはファイルシステム内の単なるファイルです。ファイルシステム内のファイルの実際の内容はすべてinodesにあります(質問から、あなたはすでにこのことに気づいていることがわかります)。ディスク上のiノードに構造はありません。それらは、ディスク上にピーナッツバターのように広がった、番号の付いたバイトの塊の大きな束です。これは役に立たず、きちんと整頓された人には忌避されます。

only特殊iノードはiノード番号2です(伝統的な理由により、0または1ではありません)。 iノード2はディレクトリファイルですルートディレクトリ。システムがファイルシステムをマウントするとき、システムはそれ自体を開始するためにreaddir inode 2を実行する必要があることを「認識」しています。

ディレクトリファイルは単なるファイルであり、opendir(3)やその仲間が読むことを意図した内部構造を持っています。 (OSに応じて)dir(5)に記載されている内部構造を確認できます。これを見ると、ディレクトリファイルエントリにファイルに関する情報がほとんど含まれていないことがわかります。これはすべてファイルiノード内にあります。このファイルの特別な点の1つは、書き込みを許可するモードでディレクトリファイルを開こうとすると、open(2)関数でエラーが発生することです。他のさまざまなコマンド(例としてhexdumpを1つだけ取り上げます)は、ディレクトリファイルに対して通常の方法で動作することを拒否します。これは、おそらくそれがあなたがやりたいことではないからです(ただし、それはファイルシステムの特別なケースではありません)。 。

ハードリンクは、ディレクトリファイルのマップ内のエントリにすぎません。このようなマップには、同じiノード番号にマップする2つ(またはそれ以上)のエントリを含めることができます。そのため、そのiノードには2つ(またはそれ以上)のハードリンクがあります。 everyファイルに少なくとも1つの「ハードリンク」がある理由もこれで説明されています。 iノードには参照カウントがあり、ファイルシステムのどこかにあるディレクトリファイルでそのiノードが言及された回数が記録されます(これはls -lを実行したときに表示される番号です)。

OK:私たちは今、要点に達しています。

ディレクトリファイルは、文字列(「ファイル名」)から数値(iノード番号)へのマップです。これらのiノード番号は、そのディレクトリにあるファイルのiノードの番号です。そのディレクトリにあるファイルには他のディレクトリファイルが含まれている可能性があるため、そのiノード番号はディレクトリにリストされているものの1つになります。したがって、ファイル/tmp/foo/barがある場合、ディレクトリファイルfooにはbarのエントリが含まれ、その文字列をそのファイルのiノードにマッピングします。ディレクトリ/tmpの「中」にあるディレクトリファイルfooのエントリも、ディレクトリファイル/tmpにあります。

Mkdir(2)でディレクトリを作成すると、その関数は

  1. 正しい内部構造を持つディレクトリファイル(inode番号付き)を作成します。
  2. エントリを親ディレクトリに追加し、新しいディレクトリの名前をこの新しいiノード(リンクの1つを占める)にマッピングします。
  3. 新しいディレクトリにエントリを追加し、文字列「。」をマッピングします同じiノード(これは他のリンクを説明します)
  4. 新しいディレクトリに別のエントリを追加し、文字列「..」をステップ(2)で変更したディレクトリファイルのiノードにマッピングします(これにより、サブディレクトリを含むディレクトリファイルに表示されるハードリンクの数が多くなります。 )。

最終結果は、(ほとんど)唯一の特別な場合です:

  • Open(2)関数は、ディレクトリファイルを書き込み用に開かないようにすることで、足元で自分を撃つことを難しくします。
  • Mkdir(2)関数は、新しいディレクトリファイルにいくつかの追加のエントリ( '。'および '..')を追加することで、物事をうまく簡単にします。これは、純粋にファイルシステム内を移動するのに便利です。ファイルシステムは「。」なしで完全にうまく機能すると思います。と「..」ですが、使用するのは面倒です。
  • ディレクトリファイルは、「特殊」としてフラグが付けられた数種類のファイルの1つです。これは、open(2)などの動作が少し異なることを実際に伝えるものです。 stat(2)のst_modeを参照してください。

(stackoverflowの元の質問、2011-10-20からコピー)

10
Norman Gray