fallocate() と ftruncate() の違いについて
※2013年2月13日、ちょっと書き直し。
以前、MongoDBをext3で使ったら死んだというページとはてブを見て、fallocateってまだまだ知られてないんだなぁと思ったので、周知のために色々書いてみることにした。
fallocateって何?
ファイルにデータを書き込む前に、指定したサイズ分の領域を事前に確保(予約)する時に使う、Linuxのシステムコール(C言語用の関数)です。ext4やXFSなど、対応したファイルシステムでしか使えません。ちなみに、同名かつ(ほぼ)同機能のコマンドが存在します。
posix_fallocateってのもあったんだけど、fallocateと何が違う?
fallocateにちょっと機能を追加したモノ(というかラッパー)です。fallocateに対応していないファイルシステムでも使えますが、その場合はファイルに対してゼロフィルを行うため、注意が必要です。MongoDBのpreallocateの中身はたぶんコレ。
ftruncateは?
ファイルサイズを変更するためのC言語用の関数です。ファイルを切り詰めたり、Sparseファイル(穴あきファイル)を作るのに使います。こちらも、ほぼ同名かつ同機能のコマンドが存在します。
fallocateとftruncateって具体的にどう違うわけ?
実際に使ってみると違いが分かります。
まず、次のようにそれぞれのコマンドを実行して1GBのファイルを作成します。
fallocate -l 1000M test_fallocate truncate -s 1000M test_truncate
そのファイルの情報を、filefragコマンドで確認します。
$ filefrag -v test_fallocate Filesystem type is: 58465342 File size of test_fallocate is 1048576000 (256000 blocks, blocksize 4096) ext logical physical expected length flags 0 0 54729056 256000 unwritten,eof test_fallocate: 1 extent found
$ filefrag -v test_truncate Filesystem type is: 58465342 File size of test_truncate is 1048576000 (256000 blocks, blocksize 4096) test_truncate: 0 extents found
このように、truncateでは実ブロックが確保されていません。
fallocateって、実際にどう使うわけ?
creat()システムコールを実行して新規ファイルを作成 → そのファイルディスクリプタを引数にしてfallocate()システムコールを呼び出す、といった感じで使います。
fallocateを使う利点って何?
fallocateの戻り値やerrnoをチェックすることで、ドライブに空きがあるか分かります。
また、fallocateは可能な限り連続した領域を確保するため、断片化を抑えることができます。
ただし、ファイルシステムがext4の場合、バグがあるらしく、128MBを超えるサイズに対しては断片化を抑える効果がありません(Ubuntu 12.04での話です。いずれ改善するかも?)
ファイルシステムがXFSの場合、空き領域が充分あれば断片化しません。1GBを超えるサイズも余裕です。
ext4でfallocateを使って大きなサイズを確保した場合、どれぐらい断片化するの?
1GB確保してみたら、こうなりました。
$ fallocate -l 1000M test_fallocate $ filefrag -v test_fallocate Filesystem type is: ef53 File size of test_fallocate is 1048576000 (256000 blocks, blocksize 4096) ext logical physical expected length flags 0 0 784384 2048 unwritten 1 2048 815104 786432 4096 unwritten 2 6144 835584 819200 26624 unwritten 中略 28 223232 2174976 2164736 8192 unwritten 29 231424 2217984 2183168 22528 unwritten 30 253952 2250752 2240512 2048 unwritten,eof test_fallocate: 31 extents found
断片化しまくりです。