web-dev-qa-db-ja.com

ディレクトリのサイズレポートが他のファイルと異なるのはなぜですか?

空のディレクトリが4096バイトのスペースを占めるのはなぜだろうと思っていたところ、 this の質問が表示されました。スペースはブロック単位で割り当てられるため、新しいディレクトリのサイズは4096バイトです。

ただし、「通常の」ファイルの割り当てもブロック単位で行われることは確かです。少なくともそれは Windows filesystems のようなものであり、少なくともext *でも同様である必要があると思います。

私が理解している限りでは、ファイル、シンボリックリンクなどの他のタイプのファイルのサイズリストは、実際のサイズで行われます。空のファイルを作成すると、サイズとして0が表示されるためです。数文字タイプすると、サイズとして<文字数>バイトが見えます。

だから私の質問は、他のファイルの割り当てもブロックで行われますが、ディレクトリとファイルのサイズを報告するためのポリシーが異なるのはなぜですか?

明確化

質問は十分に明確であると思いましたが、明らかにそうではありませんでした。ここで質問を明確にします。

1)ディレクトリとは:

以下の例で、ディレクトリとは何かを説明しようと思います。読んだ後、誤りがあればお知らせください。

mydirという名前のディレクトリがあるとします。そして、そこにf0f1f2の3つのファイルが含まれているとしましょう。各ファイルの長さが1バイトであると仮定しましょう。

さて、mydirとは何ですか?これは、以下を含むiノードへのポインターです。ストリング「f0」とf0が指すiノード番号。文字列「f1」とf1が指すiノード番号。そして、文字列「f2」とf2が指すiノード番号。 (少なくとも、これは私が考えるディレクトリです。間違っている場合は修正してください。)

現在、ディレクトリのサイズを計算する方法は2つあります。

1)mydirが指すiノードのサイズを計算します。

2)mydirの内容が指すiノードのサイズを合計します。

1の方が直感的ではありませんが、これが使用されている方法であると仮定しましょう。 (この質問では、どの方法が実際に使用されているかは問題ではありません。)次に、mydirのサイズは次のように計算されます。

2 + 2 + 2 + 3 * <space_required_to_store_an_inode_number>

2は、各ファイル名の長さが2バイトであるためです。

2)質問:

ここで質問:ディレクトリが正しいと思うと仮定すると、メソッド1またはメソッド2を使用してそのサイズを計算しても、mydirのレポートサイズは4096をはるかに下回るはずです。

ここで、4096バイトが報告される理由は、割り当てがブロック単位で行われるためであると言います。したがって、報告されたサイズはそれほど大きくありません。

しかしそれから私は言うでしょう:割り当ては通常のファイルのブロックでも行われます。 (参考として thrig's answer を参照してください)ただし、それらのサイズは実際のサイズで報告されます。 (1文字の場合は1バイト、2文字の場合は2バイトなど)

だから私の質問は、ディレクトリのサイズを報告するためのポリシーが通常のファイルのサイズを報告することとはなぜ違うのですか?

さらに明確化:

空ではないファイルと空のディレクトリに割り当てられたブロックの初期数はどちらも8ブロックであることはわかっています。 ( thrig's answer を参照)通常のファイルとディレクトリの両方で同じ数のブロックで割り当てが行われているにもかかわらず、報告されたディレクトリのサイズがはるかに大きいのはなぜですか?

8
Utku

あなたが混乱している理由は、ディレクトリが何であるかわからないためだと思います。これを行うには、少し前に戻り、Unixファイルシステムがどのように機能するかを調べます。

Unixファイルシステムには、ディスク上のデータをアドレス指定するためのいくつかの個別の概念があります。

  • データブロックは、ファイルのcontentsを持つディスク上のブロックのグループです。
  • inodesはファイルシステム上の特別なブロックであり、そのファイルシステム内で一意の数値アドレスがあり、次のようなファイルに関するメタデータが含まれています。
    • 権限
    • アクセス/変更時間
    • サイズ
    • データブロックへのポインタ(ブロック、エクステントなどのリストである可能性があります)
  • filenamesは、iノードにマップされるファイルシステムルート上の階層的な場所です。

言い換えると、「ファイル」は実際には3つの異なるもので構成されています。

  1. ファイルシステムのPATH
  2. メタデータを持つiノード
  3. iノードが指すデータブロック

ほとんどの場合、ユーザーはファイルが「ファイル名に関連付けられたエンティティ」と同義であると想像します。これは、低レベルのエンティティまたはファイル/ソケットAPIを処理する場合にのみ、iノードまたはデータブロックと考えます。ディレクトリはそれらの低レベルのエンティティの1つです。

ディレクトリは、他のファイルの束を含むファイルであると考えるかもしれません。それは半分だけ正しいです。ディレクトリは、ファイル名をiノード番号にマップするファイルです。ファイルは含まれていませんが、ファイル名へのポインタです。次のようなエントリを含むテキストファイルのように考えてください。

  • 。 -iノード1234
  • ..-iノード200
  • ドキュメント-iノード2008
  • README.txt-inode 2009

上記のエントリはディレクトリエントリと呼ばれます。それらは基本的にファイル名からiノード番号へのマッピングです。ディレクトリは、ディレクトリエントリを含む特別なファイルです。

これはもちろん単純化ですが、基本的な考え方やその他のディレクトリの奇妙さを説明しています。

  • ディレクトリが自分のサイズを知らないのはなぜですか?
    • それらには他のものへのポインタしか含まれていないので、サイズを見つけるために内容を反復する必要があります
  • ディレクトリが空にならないのはなぜですか?
    • 少なくとも。および..エントリ。したがって、適切なディレクトリは、これらのエントリを含むことができる最小ファイルサイズと少なくとも同じくらい小さくなります。ほとんどのファイルシステムでは、4096バイトが最小です。
  • ファイルの名前を変更するときに親ディレクトリへの書き込み権限が必要なのはなぜですか?
    • ファイルを変更するだけでなく、ファイルを指すディレクトリエントリも変更します。
  • Lsがディレクトリへの奇妙な数の「リンク」を表示するのはなぜですか?
    • ディレクトリは、それ自体、その親、その子によって参照(リンク)できます。
  • ハードリンクとは何ですか。また、シンボリックリンクとどのように異なりますか?
    • ハードリンクは、同じiノード番号を指すディレクトリエントリを追加します。 iノード番号を指すため、同じファイルシステム内のファイルのみを指すことができます(ノードはファイルシステムに対してローカルです)。
    • シンボリックリンクは、別のファイル名を指す新しいiノードを追加します。ファイル名を参照するため、ツリー内の任意のファイルを指すことができます。

ちょっと待って!奇妙なことが起こっています!

ls -ld somedirectoryは常にファイルサイズが4096であることを示しますが、ls -l somefileは、ファイルの実際のサイズを示します。どうして?

混乱のポイント1:「サイズ」と言うとき、2つのことを指すことがあります。

  • iノードに保存される数値であるファイルサイズ。そして
  • 割り当てられたサイズ。これは、iノードに関連付けられているブロックの数に各ブロックのサイズを掛けたものです。

一般に、これらは同じ数ではありません。 regularファイルでstatを実行してみてください。この違いがわかります。

ファイルシステムが空ではないファイルを作成するとき、それは通常グループでデータブロックを熱心に割り当てます。これは、ファイルが任意に速く拡大および縮小する傾向があるためです。ファイルシステムがファイルを表すのに必要な数のデータブロックのみを割り当てた場合、拡大/縮小は遅くなり、断片化は深刻な問題になります。したがって、実際には、ファイルシステムは小さな変更のためにスペースを再割り当てし続ける必要はありません。これは、ファイルによって「要求」されたが、完全に未使用のディスクに多くのスペースがある可能性があることを意味します。

ファイルシステムは、このすべての未使用領域をどのように処理しますか?何もない。それが必要であると感じるまで。ファイルシステムオプティマイザーツール(おそらくバックグラウンドで実行されているオンラインオプティマイザー、おそらくfsckの一部、ファイルシステム自体に組み込まれている)のように思える場合、ファイルのデータブロックを再割り当てする可能性があります-使用済みブロックを移動し、未使用を解放しますブロックなど.

ここで、通常のファイルとディレクトリの違いについて説明します。ディレクトリはファイルシステムの「バックボーン」を形成しているため、頻繁にアクセスまたは変更する必要があり、最適化する必要があることが予想されます。そして、あなたはそれらをまったく断片化したくありません。ディレクトリが作成されると、ディレクトリエントリが非常に多い場合でも、すべてのデータブロックのサイズが常にmax outになります。ディレクトリはファイルとは異なり、通常、ディレクトリのサイズと成長率が制限されているため、これは問題ありません。

4096と報告されたディレクトリーのサイズは、ディレクトリー内のエントリーの数ではなく、ディレクトリーiノードに保管されている「ファイルサイズ」の数です。これは固定数ではなく、ディレクトリに割り当てられたブロック数に収まる最大バイト数です。通常、これは512バイト/ブロック×任意の内容のファイルに割り当てられた8ブロックです-ちなみに、ディレクトリの場合、ファイルサイズと割り当てられたサイズは同じです。単一のグループとして割り当てられるため、ファイルシステムオプティマイザーはブロックを移動しません。

ディレクトリが大きくなると、より多くのデータブロックがそのディレクトリに割り当てられ、それに応じてファイルサイズを調整することで、それらのブロックもmax outします。

したがって、lsおよびstatは、ディレクトリのiノードのファイルサイズフィールドを表示します。これは、割り当てられたデータブロックのサイズに設定されています。

11
madumlao

最初の空のディレクトリサイズはファイルシステムに依存すると思います。アクセス可能なext3およびext4ファイルシステムでは、4096バイトの空のディレクトリも取得します。 NFSマウントされたNAS=ある種の場合、80バイトの空のディレクトリを取得します。ReiserFSファイルシステムにアクセスできません。新しく作成された空のディレクトリサイズがあります。面白い。

従来、ディレクトリは、そのiノード(ファイルを記述するディスク上の構造)にビットが設定されたファイルであり、ディレクトリであることを示していました。そのファイルは可変長レコードで埋められました。 _/usr/include/linux/dirent.h_の内容は次のとおりです。

_struct dirent64 {
    __u64       d_ino;
    __s64       d_off;
    unsigned short  d_reclen;
    unsigned char   d_type;
    char        d_name[256];
};
_

_d_off_値を使用すると、ディレクトリファイルエントリをスキップできます。エントリが削除された場合(unlink()システムコール、rmコマンドで使用)、前のエントリの_d_off_値が不足しているレコードを考慮して増加しました。レコードの「圧縮」は行われませんでした。おそらく、ファイルに割り当てられたディスクブロックのバイト数の観点から割り当てを表示するのが最も簡単でした。ディレクトリファイル内のすべてのエントリのバイト数や、最後のエントリー。

最近では、ディレクトリの内部形式はBツリーや ハッシュツリー のようになっています。ブロック単位でディレクトリを実行することでパフォーマンスが大幅に向上するか、古い学校のディレクトリと同様にその中に「空白」があるため、特にディレクトリの「実際のサイズ」が何であるかを判断するのは難しいと思いますしばらく使用されていて、ファイルが削除されたり、追加されたりしたもの。ブロック数にブロックごとのバイト数を掛けた値を表示するだけの方が簡単です。

3
Bruce Ediger

ファイルにはブロックが割り当てられていない場合があります。 lsの_-s_フラグはこの違いを示しますが、ディレクトリにはいくつかの最小ブロックが割り当てられているため、デフォルトのサイズになります。 (あなたがこれらの概念をウィンドウの外に投げ出すいくつかの派手な最新のファイルシステムを使用しているのでない限り。)例えば:

_% mkdir testfoo
% cd testfoo/
% mkdir foodir
% touch foofile
% ln -s foofile foosln
% ls -ld foo*
drwxrwxr-x  2 jmates  jmates  512 Oct  5 19:48 foodir
-rw-rw-r--  1 jmates  jmates    0 Oct  5 19:48 foofile
lrwxrwxr-x  1 jmates  jmates    7 Oct  5 19:48 foosln -> foofile
% ls -lds foo*
8 drwxrwxr-x  2 jmates  jmates  512 Oct  5 19:48 foodir
0 -rw-rw-r--  1 jmates  jmates    0 Oct  5 19:48 foofile
0 lrwxrwxr-x  1 jmates  jmates    7 Oct  5 19:48 foosln -> foofile
% 
_

readlink(2)に必要な詳細に7バイトを割り当てているにもかかわらず、ここではシンボリックリンクはブロックを取りません。とにかく、1バイトか2バイトでfoofileを埋めましょう:

_% echo >> foofile a
% ls -lds foo*
8 drwxrwxr-x  2 jmates  jmates  512 Oct  5 19:48 foodir
8 -rw-rw-r--  1 jmates  jmates    2 Oct  5 19:49 foofile
0 lrwxrwxr-x  1 jmates  jmates    7 Oct  5 19:48 foosln -> foofile
%
_

そして、2バイトしかない(foofileと改行aが追加されている)にもかかわらず、echoに割り当てられたブロックが_8_にジャンプしたことがわかります。

ファイルはスパースである場合もあります。これは、ファイルと対話するツールがスパース性を処理する方法に応じて、報告されたファイルサイズと実際のコンテンツが異なる場合があることを示します。

また、ディレクトリのサイズを増やし、非常に長い名前で多くのファイルを作成し、_ls -lds ._を使用して新しい長いファイル名を作成した後、ディレクトリ(および割り当てられたブロック)のサイズがどうなるかを確認できます。

2
thrig