UNIX/Linuxの部屋 用語集:穴あきファイル

TOP UNIX/Linuxの部屋 UNIX/Linuxコマンド一覧 用語集 新版 由来/読み方辞書 環境変数マニュアル Cシェル変数 システム設定ファイル システムコール・ライブラリ ネットワークプログラミングの基礎知識 クラウドサービス徹底比較・徹底解説




用語集 穴あきファイル 内部に NULL を含むファイル (穴空きファイル・Sparse file・疎なファイル) このエントリーをはてなブックマークに追加

最終更新


UNIX/Linux における「穴あきファイル」とは、内部に NULL データを含むファイルで、なおかつディスク上には一部データの実体が存在しないファイル、である。英語では "Sparse file" (スパース・ファイル) と呼ぶ。



穴あきファイルを作ってみる
穴あきファイルを実際に作ってみよう。以下のようにしてファイルサイズが 1,000,000 バイトな 3つのファイルを作成する。
% perl -e 'open(OUT,">normal.dat");print OUT "a" x 1000000'
⇒ normal.dat は 'a' で埋まったファイル
% perl -e 'open(OUT,">zero.dat");for(1..1000000){printf OUT "%c", 0}'
⇒ zero.dat は NULL (0x00) で埋まったファイル
% perl -e 'open(OUT,">sparse.dat");seek(OUT,999999,0);printf OUT "%c", 0'
⇒ sparse.dat は、1MB 弱シークで移動し、NULL (0x00) を出力したファイル

作成されたファイルを ls で見てみよう。ただし -s オプションを付けて、消費ブロックサイズを表示する。
% ls -ls
  1008 -rw-r--r--   1 user  group   1000000 Jun 25 14:57 normal.dat
  1008 -rw-r--r--   1 user  group   1000000 Jun 25 14:56 zero.dat
    32 -rw-r--r--   1 user  group   1000000 Jun 25 14:56 sparse.dat

normal.dat と zero.dat 使用ブロック数が 1008 なのに対し、sparse.dat は 32 しか使用していない。この環境のブロックサイズは 1024 バイトなので、normal.dat と zero.dat は約 1MB の領域を占めているのに対し、sparse.dat は 32KB しか使用していないことになる。この sparse.dat のようなファイルを「穴あきファイル」と呼ぶ。

穴あきファイルを作るには、ファイルをオープンし、ファイル終端よりも後ろにシークして、何かデータを出力すればよい。そうすれば、中間のデータは NULL (0x00) となり、そのデータはディスク容量を消費しなくなる (間接ブロックの分は必要だが)。

穴あきファイルのメリット
穴あきファイルのメリットは何か。当ページ管理人が思うに、以下ようなものであろう。
  • ディスクを節約したい
  • アクセス速度を高速にしたい
  • 実際のデータ量は少ない
例えば、全国民の氏名・住所などを一元管理するシステムを作るとする。カードを発行して、役所で便利なサービスを提供したい。カードの使用履歴をファイルに保持したい。人数を 1億人として、ひとりあたり 10KB の容量があれば十分な履歴情報を保持できる。データ量は 1TB になるが、1TB のハードディスクを買う予算がない。

悩んでいるとそこに「このシステムは不人気で誰もカードを持とうとしない」という情報が。どうやら国民の1%しかカードを持っていないらしい。ということは実際に必要なデータは 100GB。

ここで穴あきファイルを使って、先頭 10KB は 住民コード 0番の人、次の 10KB は 住民コード 1番の人…、と割り当てれば、
open(OUT, "<+ data.dat");
seek(OUT, 10240 * 住民コード番号, 0);
というアクセス方法で高速に履歴情報を参照・更新でき、なおかつカード発行率は 1% なので実データを 100GB 程度でおさめることができる。

…と苦しい例を出してみたが、ディスク容量が飛躍的に増え、マシン性能が上がった今となっては、穴あきファイルを使うのはやめた方がよいと当ページ管理人は思う。以下に、穴あきファイルの管理の難しさを示す。

穴あきファイルの注意点
穴あきファイルに対して不用意な操作をするとファイルサイズが突然増えることがある。たとえば多くの UNIX では穴あきファイルを cp すると、コピー先ファイルのブロックサイズが増え、1008 ブロック消費するようになる。
% cp sparse.dat sparse2.dat
% ls -l
32 -rw-r--r-- 1 user group 1000000 Jun 25 14:56 sparse.dat
1008 -rw-r--r-- 1 user group 1000000 Jun 25 14:56 sparse2.dat
tar も同様で、tar cf でアーカイブを作成し、tar xf で展開すると、穴あきファイルは普通の NULL 埋めファイルになってしまう。

この例では 1MB 程度のファイルなので問題ないが、数十GB の穴あきファイルをコピーしたりバックアップしたりすると、容易にディスクがあふれてしまうだろう。

これは cp や tar が悪いのではない。穴あき部分を read(2) しても、NULL (0x00) が連続しているデータとして読み込まれるので、各コマンド側から穴あきファイルかどうかを判断することはできないからである。どうしても穴あきファイルをみわけたければ、ブロックサイズから計算した実サイズと、みかけのサイズを比較するか、dump コマンドのようにデバイスを直接参照するしかない。

GNU tar ではアーカイブ作成時に -S オプションをつけると穴あきファイルを「穴あきファイルとして」アーカイブするこができる。ただし、この方法で作成したアーカイブは POSIX 標準 tar フォーマットではないため、GNU tar 以外では展開できないだろう。また、GNU の cp はデフォルトで穴あきファイルを正しくコピーできる。この挙動は --sparce オプションで変更できる。GNU tar と GNU cp は、前述の実サイズとみかけのサイズを比較する方法で穴あきファイルかどうかを判断している。

UNIX において穴あきファイルを正しく扱えるのは pax・dump・dd・cpio・ufsdump などである。

その他
ファイルシステムによっては穴あきファイルをサポートしていないものもある。そのようなファイルシステム上で穴あきファイルを作ろうとしても、実際に NULL (0x00) が詰まったファイルとして生成されるだろう。例えば、Solaris は (デフォルトでは) /tmp がメモリ上に確保されるが、この /tmp 上では穴あきファイルは生成できない。

Linux では、カーネルだか newfs だか mkfs だかのオプションで、ファイルシステムごとに穴あきファイルを生成するかどうかを制御できたような気がしないでもない。

当ページ管理人は、穴あきファイルなどディスクを 1バイトでも節約したかった時代の過去の遺物であり、はやくなくなってしまえと思っていたのだが、Windows でも 2000年頃に穴あきファイルが導入されてしまった (Windows 2000 の NTFS 2000 以降で sparse file が使用可能)。

…と書いたのは 2002年頃だった気がするが、2000年代前半、VMware などの仮想マシンの VM イメージで使われ、2000年代後半はクラウドでの VM イメージなどで使われ、2010年代は Docker イメージでも使われ、結果としては穴あきファイルは必要なものである、ということを歴史が証明したと言えよう。