unlinkとファイルの寿命、名前を消しても開いた参照が残る理由

白い楕円の台の薄緑の角張った塊に青い紐がつながり、左には桃色の紐の外れた二枚の無地の札が置かれている。

大きなログファイルの名前を削除したのに、書き込みが続いている。ファイル名で探しても見つからないのに、開いていた処理からは読める。この動作を理解する鍵は、ファイルを指す名前と、プロセスがすでに持っている開いた参照を分けることです。

unlinkは、ファイルシステムから名前を取り除く操作です。「名前を消した」「ファイルへの参照がなくなった」「保存領域を再利用できるようになった」を、一つの出来事として扱わないようにします。Linux man-pages 6.19の説明を基に、二つの名前と一つの開いた参照で順序を追います。[1]

簡単にまとめると?

  • unlinkで一つの名前を取り除いても、同じファイルへの別のハードリンクがあれば、その名前から引き続きアクセスできます。
  • 最後の名前がなくなっても、プロセスが開いたままならファイルは残ります。既存の記述子が、同じ名前で後から作った新ファイルへ移ることもありません。
  • 削除成功を、空き容量の増加や安全な消去の証明にしません。残っている名前と開いた参照、保存先の条件を別々に確認します。

ハードリンクは、コピーではなく同じ対象への別名

通常ファイルをreportという名前で作り、そのファイルにaliasというハードリンクを追加したとします。二つの名前は、同じファイルを指します。一方が本体で他方がその複製、という関係ではありません。どちらの名前から開いても同じ内容を参照し、一方から内容を変えると同じファイルへの変更になります。[2]

この例のリンク数は2です。リンク数が数えるのは、ファイルを指すハードリンクの数であり、開いている記述子の数ではありません。reportをunlinkするとリンク数は1になりますが、aliasが残っているので、その名前から開けます。

シンボリックリンクは別の仕組みです。こちらは別のパス名への案内を保持します。シンボリックリンクそのものをunlinkすれば、その案内が取り除かれます。案内先の通常ファイルを同時に消す操作ではありません。[1] 本稿の数値例はハードリンクを使い、この二種類を混ぜません。

名前を解決して開いた後は、参照の寿命が分かれる

reportが存在するうちに、プロセスが読み取り用に開いて記述子fdを得たとします。fdは、開いたファイルへアクセスするための番号です。その後のreadは、毎回reportという文字列を最初から探しているわけではありません。Linuxのopenの説明でも、開いた参照は、元の名前が後で除去されたり別のファイルを指したりしても影響を受けないとされています。[3]

ここでreportをunlinkし、続いてaliasもunlinkすると、元のファイルのリンク数は0になります。二つの名前から新しく開くことはできなくなりますが、fdはまだ元のファイルを参照しています。名前からの探索と、すでに開いた参照を通る読み取りは、別の経路なのです。

Linuxのunlinkの説明では、最後のリンクを取り除いた時点で開いているプロセスがなければ、ファイルが削除され、その領域を再利用できるようになります。開いたままなら、最後の参照する記述子が閉じられるまでファイルは残ります。[1] 一つのプロセスが閉じても、別のプロセスや複製された記述子が残る場合を見落とさないことが必要です。

二つの名前を消す、小さなローカル実験

この区別を確かめるため、専用の一時ディレクトリで四段階の読み取りを行いました。通常ファイルreportへ文字列OLDと改行を書き、aliasというハードリンクを作って、reportを読み取り用に開きます。その後は同じfdを保持し、各段階で位置を先頭へ戻して読みます。

実行環境はmacOS 26.6.2、Python 3.9.6です。os.link、os.unlink、os.open、os.fstat、os.readを使いました。これは手元のUnix系環境での観測であり、Linuxカーネル上で実行した試験ではありません。Linuxの仕様説明と、実行環境を明示した小さな実験を分けて示します。

一時ファイルの実験。reportとaliasがあるとリンク数2。reportを消すと1。aliasも消すと0。それでも同じ開いたfdからは全段階でOLDが読めた。同じreport名へNEWを作っても、既存fdはOLDを参照した。
同じ開いた記述子を保持し、先頭へ戻して読んだ観測。対象はこの実験専用の一時ファイル。空き容量、クラッシュ後の状態、安全な消去を測ったものではない。原寸画像を開けます。

最初は二つの名前があり、fdで調べたリンク数は2、読み取った内容はOLDでした。reportを取り除くとreportは存在しなくなり、リンク数は1へ減りましたが、fdからはOLDが読めました。さらにaliasを取り除くと、両方の名前がなくなり、リンク数は0になりました。それでもfdから読めた内容はOLDでした。

この三段階から確認できるのは、名前がなくなったことと、その時点で既存の参照が使えなくなることが同じではない、という点です。読み取りができた理由を「削除に失敗した」と解釈すると、リンク数0という観測と矛盾します。削除対象だった名前はなくなり、開いた参照が残った状態です。

同じ名前を作り直しても、古い参照は切り替わらない

最後に、両方の名前がなくなった後で、reportという名前へNEWと改行を持つファイルを新しく作りました。reportを名前から読めばNEWですが、保持していたfdからはOLDです。fdに対応するファイルのリンク数は0のままでした。同時点のファイル識別情報も比較し、名前から得た新しい対象と、fdが指す古い対象が異なることを確認しました。

同じ文字列を使い直しても、過去に開いた参照の行き先が書き換わるわけではありません。常駐するログ出力処理が古いファイルを開いたままなら、同じパスへ新しいファイルを置いただけで出力先が自動的に移る、と考えることはできません。出力処理が対応する方法で開き直しや切替を行う必要があります。

この例は逐次実行で、途中に別の書き手はいません。古い参照から常に同じ内容が読めるスナップショットを作ったわけではなく、別の処理が古いファイルを書き換えれば内容も変わり得ます。また、ファイルの識別番号は寿命を越えて永久に一意な番号ではないので、後日の番号だけで同じファイルだと断定しません。

容量調査では「名前がない」の先を調べる

削除後の容量が期待どおり変わらないときは、まず別のハードリンクがないか、次に開いた参照を持つ処理がないかを調べます。名前を一覧に出す方法だけでは、名前を失ったまま開かれているファイルを捉えきれません。プロセス側から開いた参照を調べる観測と、ファイルシステム側の容量観測を組み合わせます。

ただし、本稿の実験では空き容量を測っていません。最後の参照を閉じた後の容量表示や、スナップショットなどが保持するデータの扱いまで、この四段階の読み取りから証明することはできません。ネットワークファイルシステムにも固有の挙動があります。Linuxのunlinkの資料にはNFSでの注意があり、ローカルの通常ファイルの観測をそのまま全保存先へ広げないことが必要です。[1]

unlinkは、データを上書きして復元不能にする処理の証明でもありません。名前の除去、参照の終了、領域の再利用、媒体からの安全な消去は、確認すべき対象が異なります。目的が容量を空けることなのか、ログの出力先を切り替えることなのか、情報を消去することなのかを先に分けると、成功と判断するための観測も明確になります。

出典

  1. Linux man-pages 6.19、unlink(2)、DESCRIPTION、ERRORS、BUGS。本文日付2026年2月8日、2026年10月22日保存。
  2. Linux man-pages 6.19、link(2)、DESCRIPTION。ハードリンクの別名と同一対象の関係。
  3. Linux man-pages 6.19、open(2)、DESCRIPTION、Open file descriptions。開いた参照とパス名の関係。図と四段階の値は、本文に示したmacOS環境での独自実験です。