サイトアイコン 酒呑ガジェット

【衝撃】消したはずなのに容量が空かない…Linuxの削除が幽霊みたいなワケ

【衝撃】消したはずなのに容量が空かない…Linuxの削除が幽霊みたいなワケ

サーバーの容量がいっぱいになったので、動いているプログラムの巨大なログファイルを消した。一覧からは消えたのに、空き容量がまったく増えない。ウイルスかと疑いたくなる場面だが、これはLinuxの正常な仕様だ。実行中のプログラム本体を消してもエラーなく動き続けるという直感に反する挙動も、同じ仕組みから生まれている。削除という言葉の意味を取り違えるところに、つまずきの正体がある。

実はrmが呼んでいるのはデータを消す処理ではなく、リンクを外す処理だ。POSIXの定義でもリンクの削除とカウントの減少と厳格に定められている。消しているのはディスク上のデータでも管理情報でもなく、ディレクトリエントリという名前と実体のひも付けだけだ。カーネルにパス名を渡して名前空間からの切り離しを要求し、仮想ファイルシステムがキャッシュから切り離す。物理的な解放は特定条件が満たされた後の副次的な結果にすぎない。

名前と実体が別物という設計が鍵になる。ファイルは3層で管理され、文字列と番号を結ぶディレクトリエントリ、メタデータと実体へのポインタを持つiノード、実際のバイナリが載るディスク上のブロックに分かれる。ファイル名はiノードの中にはなく、エントリ側にある。iノード側には番号や権限、所有者、サイズ、ブロック位置が入る。この分離があるからハードリンクが成り立つ。同じiノードを指す2つ目の名前を作るとリンクカウントが増え、1つ目の名前を消しても2つ目の名前と実体は生き残る。リンク解除とは入り口の1つを閉じる操作で、看板を外しても店自体は営業を続けるようなものだ。

プロセスが実体を掴むから、名前が消えても動き続ける

すべての名前が消えてリンクカウントがゼロになれば普通は消滅するはずだが、プロセスが開いている場合は例外になる。プログラムを実行するとカーネルはファイルに対するオブジェクトを作り、メモリでの参照カウントを増やす。カーネル内部ではプロセス管理構造体からiノードへ強固なポインタの連鎖が作られ、プロセスが実体をがっちり掴んで離さない状態になる。この状態で削除すると名前は消えてカウントはゼロになり、エントリは切り離されるが、プロセスの持つオブジェクトはiノードを掴んだままだ。参照が残るため解放されず、プロセスは名前ではなくオープン時に得たオブジェクトに依存して動き続ける。詳細情報の領域に元のパス名の後ろに削除済みを示す表記が付くのは、カーネルが付与するメタデータだ。

よくある誤解が、起動時に全部メモリに載るから大丈夫という説明だ。結果だけ見れば正しそうだが根拠としては誤りで、Linuxは実行ファイル全体を丸ごとコピーする非効率なことはしない。使われるのはデマンドページングという技術で、仮想メモリ空間に実行ファイルの領域をマッピングし、物理メモリへの搭載はせず仮想とファイル内位置をひも付けるだけだ。存在しない領域に触れるとページフォルトという割り込みが起き、その時に初めて必要な4キロバイトのページを読み込む。まだ読まれていないコードが残っている状態で名前が消えても、メモリ上のiノードがブロックの物理位置を記憶しているため、パス名の再探索なしに直接読み込める。これはPOSIXで定められた仕様で、参照が残る限り実体は消えないという規則通りだ。ログを消したのに容量が空かないのは、出力プロセスがディスクリプタを開き続け参照が残るためで、解放には終了や再起動で参照をゼロにする必要がある。終了時にテーブルが破棄され参照が減り、すべてゼロになったのを検知して初めて解放関数が呼ばれ空きとして印が付く。

Windowsとの違いと上書き拒絶、無停止更新の仕掛け

Windowsに慣れていると不思議に感じるのは設計思想の違いだ。WindowsのNTカーネルは強力な排他制御と共有モデルを採り、開く際に共有モードを指定する。既定では開かれたファイルへの他からの書き込みや削除は禁じられ、実行中は使用中のため削除できないと拒絶される。許可する旗を指定して開いた場合も、Linuxのように即座に名前が消えるわけではなく、削除保留という特殊状態に移り、全ハンドルが閉じた後に実行されるまで名前が残る。Linuxが名前と実体を分けて名前空間の変更を許すのに対し、Windowsは使用中の突然の消失を防ぐ。優劣ではなく思想の違いだ。

削除はできても上書きは拒まれる点も重要だ。実行中のファイルに別の中身を直接書こうとするとテキストファイルが使用中という趣旨のエラーで拒絶される。削除が名前空間の変更にすぎないのに対し、書き換えはプロセスが依存する実体の破壊になるからだ。実行中にコードを書き換えられれば不正命令で落ち、権限狙いの書き換えの危険もある。これを防ぐため起動時に書き込みを禁じる関数が呼ばれ、マッピングされたiノードへの書き込みがロックされる。更新はこの分離を逆手に取る。既存を直接書かず、新バイナリを別名で置き、リネーム相当の不可分な切り替えで本来の名を新しいiノードへ付け替える。古いiノードを掴む既存プロセスは動き続け、新規起動だけが新版を拾う。サービス全体を止めずに更新できる仕組みだ。コンテナでも読み取り専用層と書き込み層を重ね、削除時はホワイトアウトと呼ばれる印を上層に作り、見かけ上消えたように見せながら元影像の実体は守る応用がある。

ネットの反応

既にファイルは閉じてるのにフォルダを掴んで離さないアプリを落とすまでフォルダ名の変更すらできないwinからすると、ちょっと良いなぁと思っちゃう。これが再起動の嵐の有無の差ですか

AIの所感

看板を外しても店は続くという比喩が腹落ちする。名前と実体を分ける潔さが、無停止更新や容量トラブルの種まで一本で説明してしまうのが見事だ。消したのに空かない時は焦って再削除するより、掴んでいる主を探すのが近道になる。Windowsの丁寧な拒絶とLinuxの素っ気ない許容、どちらも守りたいものが違うだけだ。仕組みを知れば幽霊ではなく、几帳面な番人の仕事に見えてくる。

モバイルバージョンを終了