コンピューターサイエンス:アルゴリズム / OS / ネットワーク / DB
i-node とディレクトリ
巨大なログを消したのに、空き容量が増えない
ディスクが埋まったので、たまっていた巨大なログを rm した。ls からは消えている。ところが df を見ると、空きが 1 バイトも増えていない。サーバー運用でよく踏む現象です。
答えは「ファイルの実体は、名前を消しただけでは死なない」からです。名前と実体が別々のものだと分かると、この挙動が素直に読めるようになります。
実体の側は、名前を持っていない
ファイルの管理情報は i-node と呼ばれる固定サイズの構造にまとまっています。中身は大きさ、所有者、パーミッション、3 種類の時刻、そして中身がどのブロックに入っているかの一覧です。
ここに ファイル名は入っていません。i-node が持つのは、そのファイルシステム内で一意な番号だけです。名前を持っているのはディレクトリの側で、ディレクトリの中身は「名前と i-node 番号の対」が並んだ表にすぎません。
ターミナル
ls -liプレーンテキスト
67890 -rw-r--r-- 2 alice users 240 Aug 20 11:00 report.log
67890 -rw-r--r-- 2 alice users 240 Aug 20 11:00 backup.log左端が i-node 番号です。名前は 2 つありますが番号は同じで、実体は 1 つしかありません。ln report.log backup.log で作ったこの状態をハードリンクと呼びます。ディスクを 2 倍使っているわけではありません。ln -s で作るシンボリックリンクはこれとは別物で、自分専用の i-node を 1 つ持ち、その中身として相手のパスの文字列を抱えています。
rm が減らしているのは、名前の数
ls -l の 3 列目、上の例の 2 が リンクカウント で、その i-node を指している名前の数です。rm の実際の仕事は、ディレクトリの表から 1 行消して、このカウントを 1 減らすことだけです。
ブロックが空きに戻るのは、次の 2 つが同時に成り立ったときです。
- リンクカウントが 0 になった。つまり、どの名前からも辿り着けない
- そのファイルを開いているプロセスが 1 つも無い
冒頭の現象は 2 つ目に引っかかっています。ログをつかんだままのプロセスが生きているので、名前が消えても実体は解放されません。lsof | grep deleted で犯人が見え、そのプロセスを再起動した瞬間に空きが戻ります。
裏を返せば、動作中のプログラムが書いているファイルを消しても、その場では壊れません。ログローテートはこの性質を使っています。
容量が余っているのに、ファイルが作れない
i-node は多くの実装で、フォーマット時に個数が決め打ちされます。ファイル 1 つにつき 1 つ必要なので、小さなファイルを大量に作ると、容量は余っているのに i-node のほうが先に尽きます。
ターミナル
df -iIUse% が 100% なら、消すべきは大きなファイルではなく、数の多いファイルです。