tmpfsの/でのこの奇妙なスパースファイル処理を説明できるものは何ですか?


14

私のext4ファイルシステムパーティションで、次のコードを実行できます。

fs="/mnt/ext4"

#create sparse 100M file on ${fs}
dd if=/dev/zero \
   of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2> /dev/null

#show its actual used size before
echo "Before:"
ls ${fs}/sparse100M -s

#setting the sparse file up as loopback and run md5sum on loopback
losetup /dev/loop0 ${fs}/sparse100M 
md5sum /dev/loop0

#show its actual used size afterwards
echo "After:"
ls ${fs}/sparse100M -s

#release loopback and remove file
losetup -d /dev/loop0
rm ${fs}/sparse100M

もたらす

Before:
0 sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
0 sparse100M

tmpfsで次と同じことを行う:

fs="/tmp"

利回り

Before:
0 /tmp/sparse100M
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
After:
102400 /tmp/sparse100M

これは基本的に、単にデータを読み取るだけで、スパースファイルが「バルーンのように爆発する」と思っていたことを意味します。

これは、tmpfsファイルシステムでのスパースファイルのサポートがあまり完璧でないこと、特にFIEMAP ioctlがないためだと思いますが、この動作の原因はわかりませんか?教えて頂けますか?


ハム。共有(コピーオンライト)ゼロページがあり、たとえば、スパースページをmmap()する必要がある場合に使用できます。したがって、スパースtmpfsファイルからの読み取りの種類が実メモリの割り当てを必要とする理由がわかりません。lwn.net/Articles/517465。これが直接ioを使用するループ変換の副作用であるかどうか疑問に思いましたが、tmpfsで新しいタイプのループを使用しようとしても違いはないようです。 spinics.net/lists/linux-fsdevel/msg60337.html
sourcejedi

多分これがSOにあったら答えを得るかもしれない?ただの考え

1
/ tmpの出力には、Before / Afterという異なるファイルがあります。それはタイプミスですか?前:0 / tmp / sparse100(末尾にMなし)後:102400 / tmp / sparse100M(末尾にMが付いています)。
ヨミスモ

@YoMismo、はい、ほんの少しのタイプミスでした
humanityANDpeace

回答:


4

まず、この種の問題について困惑しているのはあなただけはありません

これはtmpfsNFSv4に限定されているだけでなく、懸念事項となってい ます。

アプリケーションがスパースファイルの「穴」を読み取ると、ファイルシステムは空のブロックをゼロで埋められた「実際の」ブロックに変換し、それらをアプリケーションに返します。

場合はmd5sum、ファイルをスキャンしようとしていることが明示的にこれを行うことを選択した 順番のmd5sumが何しようとしているものに基づいて多くの意味になります。

ファイルには基本的に「穴」があるため、このシーケンシャル読み取りによって(状況によっては)書き込み時コピー操作がファイルに書き込まれます。これfallocate()は、ファイルシステムでサポートされているようにサポートされているかどうかについて、より深い問題になりますFALLOC_FL_PUNCH_HOLE

幸いなことに、tmpfsこれをサポートするだけでなく、穴を「掘り返す」メカニズムがあります。

CLIユーティリティfallocateを使用すると、これらのホールを正常に検出して再発見できます。

ごとにman 1 fallocate

-d, --dig-holes
      Detect and dig holes.  This makes the file sparse in-place, without
      using extra disk space.  The minimum size of the hole depends on
      filesystem I/O  block size (usually 4096 bytes).  Also, when using
      this option, --keep-size is implied.  If no range is specified by
      --offset and --length, then the entire file is analyzed for holes.

      You can think of this option as doing a "cp --sparse" and then
      renaming the destination file to the original, without the need for
      extra disk space.

      See --punch-hole for a list of supported filesystems.

fallocateただし、ファイルレベルで動作md5sum し、ブロックデバイスに対して実行している(シーケンシャルリードを要求している)場合、fallocate()syscallの動作方法間の正確なギャップにトリップします。これを実際に見ることができます:

実際の例を使用すると、次のことがわかります。

$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ONTGAS8L06
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ONTGAS8L06/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ sudo md5sum /dev/loop0
2f282b84e7e608d5852449ed940bfc51  /dev/loop0
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 102400 /tmp/tmp.ONTGAS8L06/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ONTGAS8L06/sparse100M

これで、基本的な質問に答えました。私の一般的なモットーは「おかしくなる」ことなので、さらに掘り下げました...

$ fs=$(mktemp -d)
$ echo ${fs}
/tmp/tmp.ZcAxvW32GY
$ dd if=/dev/zero of=${fs}/sparse100M conv=sparse seek=$((100*2*1024-1)) count=1 2>/dev/null
$ echo "Before:" "$(ls ${fs}/sparse100M -s)"
Before: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo losetup /dev/loop0 ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 1036 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 520 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 516 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 512 /tmp/tmp.ZcAxvW32GY/sparse100M
$ fallocate -d ${fs}/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M
$ sudo md5sum ${fs}/sparse100M
2f282b84e7e608d5852449ed940bfc51  /tmp/tmp.ZcAxvW32GY/sparse100M
$ echo "After:" "$(ls ${fs}/sparse100M -s)"
After: 0 /tmp/tmp.ZcAxvW32GY/sparse100M

実行するだけでlosetupスパースファイルのサイズが変わることがわかります。したがって、これは、where tmpfs、HOLE_PUNCHメカニズム、、fallocateおよびブロックデバイスが交差する興味深い組み合わせになります。


2
ご回答有難うございます。tmpfsスパースファイルとpunch_holeがサポートされていることを認識しています。それが非常に混乱tmpfs ている理由です- これをサポートしているので、ループデバイスを読んでいるときに疎な穴を埋めるのはなぜですか?losetupファイルサイズは変更されませんが、ブロックデバイスが作成され、ほとんどのシステムで次のようなコンテンツがスキャンされます:パーティションテーブルはありますか?UUIDのファイルシステムはありますか?次に/ dev / disk / by-uuid /シンボリックリンクを作成する必要がありますか?また、これらの読み取りによって、スパースファイルの一部が既に割り当てられています。これは、何らかの謎の理由により、tmpfsが(一部の)読み取りの穴を埋めるためです。
frostschutz

1
シーケンシャルリーディングが(状況によっては)書き込み時のコピーのような操作を引き起こす」ことを明確にしていただけますか?読み取り操作が書き込み時のコピーアクションをトリガーする方法を理解したいと思っています。ありがとう!
ロアイマ

これは奇妙です。私のシステムでは、スクリプトではなく手動で同じ手順を実行しました。まず、OPと同じように100Mファイルを作成しました。その後、10MBのファイルのみでステップを繰り返しました。最初の結果:ls -s sparse100Mは102400でしたが、10MBファイルのls -sは328ブロックのみでした。?
パトリックテイラー

1
@PatrickTaylor〜328Kは、UUIDスキャナーが来た後に使用されるものについてですが、完全な読み取りのためにループデバイスをcat / md5sumしませんでした。
frostschutz

1
私は、ループカーネルモジュール(中のソースを掘りましたloop.c)とがあることを見た2つの関連の機能はlo_read_simplelo_read_transfer。低レベルのメモリ割り当てを行う方法にはいくつかの小さな違いがあります... lo_read_transfer実際に呼び出しの実行中にslab.hGFP_NOIO)から非ブロッキングioを要求していalloc_page()ます。 lo_read_simple()一方、実行されていませんalloc_page()
ブライアンレッドビアード
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.