rm で削除したのに、df -h で使用量を確認しても数字がまったく変わらない。このケースで途方に暮れたことのあるエンジニアは少なくないはずです。Linuxでは
rm でファイルを削除しても、そのファイルをプロセスが「開いたまま」の状態であれば、ディスクの実体(データブロック)は一切解放されません。これはLinuxのファイルシステムが持つ「inode参照カウント」という設計によるもので、動作ログを書き続けるWebサーバーやDBサーバーでは日常的に起きうる問題です。この記事では、削除したのにディスクが空かない問題を
lsof コマンドで特定し、プロセスを止めずに truncate と /proc/PID/fd で即座に解放する手順を解説します。RHEL 9 / Rocky Linux 9 / Ubuntu 24.04 LTSで動作確認済みです。
この記事のポイント
・rmでディスクが空かないのは、プロセスがそのファイルをまだ開いているから(inode参照カウント)
・dfとduの差が大きい時は lsof | grep deleted で削除済み・開放待ちファイルを特定する
・プロセスを止めずに解放するには truncate -s 0 /proc/PID/fd/FD を使う
・logrotateのcopytruncateオプションはこの仕組みを応用した設計
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ「削除したのにディスクが空かない」問題が起きるのか
Linuxのファイルシステムでは、ファイルの実体はinode(インデックスノード)という構造体で管理されています。ディレクトリはあくまでも「ファイル名→inode番号」の対応表にすぎません。rm が行うのは、このディレクトリエントリ(名前とinodeの紐付け)を削除することだけです。inodeが管理するデータブロックが実際に解放されるのは、そのinodeへの参照カウントが0になったときです。プロセスがファイルを
open() するとinodeの参照カウントが増加します。rm でディレクトリエントリを消しても、プロセスがそのファイルディスクリプタを閉じるまで参照カウントは0になりません。NginxやApache、MySQLのようにログファイルを書き続けるプロセスは、サーバーが動いている限りログファイルを開き続けます。この状態で rm しても、ディスクブロックはプロセスが終了またはファイルを閉じるまで保持されます。この仕組みを知ってさえいれば、「削除したのに容量が減らない」問題は数分以内に解決できます。
dfとduの「差」で問題を見抜く
1. dfでファイルシステムの実際の使用量を確認する
df -h はカーネルがファイルシステムに記録している使用量を返します。削除済みだが開かれたままのファイルも「使用中」としてカウントされます。# df -h /var/log Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 47G 3.0G 94% /
2. duでディレクトリの実体のみを測定する
du はディレクトリツリーを実際に走査してサイズを集計します。ディレクトリエントリから消えたファイル(rmされたファイル)は走査対象に含まれません。# du -sh /var/log 3.8G /var/log
3. 差が大きければ「削除済み・開放待ち」ファイルが存在する
上記の例ではdf で47GB使用、du で/var/log配下が3.8GB——この40GB以上の差が「削除済みだが開いたまま」のファイルによる占有です。この差異が出た時点で、次のステップで原因ファイルを特定します。
lsofコマンドで削除済みだが開かれたままのファイルを特定する
lsof(LiSt Open Files)はプロセスが現在開いているすべてのファイルを一覧表示するコマンドです。ポート確認や接続調査でも活用される強力なツールで、Linux ポート確認の全コマンドでも詳しく紹介しています。1. 全プロセスを対象に削除済みファイルを検索する
# lsof | grep deleted nginx 1423 nginx 9w REG 8,1 15032385536 1310722 /var/log/nginx/access.log (deleted) nginx 1423 nginx 10w REG 8,1 45678901 1310723 /var/log/nginx/error.log (deleted) mysqld 2187 mysql 5w REG 8,1 32212254720 1441794 /var/log/mysql/mysql.log (deleted)
(deleted) と表示されているのが「ディレクトリエントリは削除済みだが、プロセスがまだ開いている」ファイルです。2. 特定のディレクトリ配下に絞り込む
lsof | grep deleted ではシステム全体が対象になるため、出力が多い場合は +D オプションで対象ディレクトリを絞り込めます。# lsof +D /var/log | grep deleted nginx 1423 nginx 9w REG 8,1 15032385536 1310722 /var/log/nginx/access.log (deleted)
3. 出力結果の読み方
lsof の出力で注目すべき列は以下の4つです。・COMMAND列: ファイルを開いているプロセス名
・PID列: プロセスID(後でファイルディスクリプタを操作する際に使用)
・FD列: ファイルディスクリプタ番号(例:
9w はFD番号9で書き込みモード)・SIZE/OFF列: ファイルの現在のサイズ(バイト)
上記の例では nginx(PID 1423)がFD 9番で15GBのログファイルを保持していることがわかります。
ディスク領域を解放する方法
解放方法は「プロセスを再起動できるか」によって選択します。1. プロセスを再起動する(最も確実な方法)
サービス停止が許容できる場合は再起動が最もシンプルで確実です。プロセスが終了するとすべてのファイルディスクリプタが閉じられ、参照カウントが0になってディスクブロックが即座に解放されます。# systemctl restart nginx # 再起動後にdfで確認 # df -h /var/log Filesystem Size Used Avail Use% Mounted on /dev/sda1 50G 32G 18G 64% /
2. /proc/PID/fdを使ってプロセスを止めずに解放する
本番サービスを停止できない場合は、/proc/<PID>/fd/<FD> 経由で直接ファイルの内容を空にします。/proc/<PID>/fd/ ディレクトリには、そのプロセスが開いている全ファイルへのシンボリックリンクが並んでいます。# lsof | grep deleted | grep nginx nginx 1423 nginx 9w REG 8,1 15032385536 1310722 /var/log/nginx/access.log (deleted) # PIDは1423、FDは9(9wの数字部分) # /proc/1423/fd/9 を確認する # ls -la /proc/1423/fd/9 lrwx------ 1 root root 64 Oct 13 10:15 /proc/1423/fd/9 -> /var/log/nginx/access.log (deleted) # truncateでFD経由のファイルを0バイトに空にする # truncate -s 0 /proc/1423/fd/9 # 解放確認(サイズが0になっている) # lsof | grep deleted | grep nginx nginx 1423 nginx 9w REG 8,1 0 1310722 /var/log/nginx/access.log (deleted)
truncate -s 0 はファイルのサイズを0バイトに切り詰めます。プロセスは同じファイルディスクリプタのまま書き込みを継続できます。プロセスは停止せず、nginxはアクセスログの記録を中断しません。なお、
truncate -s 0 の代わりに以下のリダイレクトでも同じ結果が得られます。# > /proc/1423/fd/9
3. ファイルがまだディレクトリに残っている場合の予防的truncate
ログファイルが巨大化しているがまだrm していない場合は、直接 truncate を実行します。この場合はプロセスが書き込みを継続しているパスに直接適用できます。# ls -lh /var/log/nginx/access.log -rw-r--r-- 1 nginx nginx 14G Oct 13 10:00 /var/log/nginx/access.log # truncateで中身だけ空にする(プロセスを止めない) # truncate -s 0 /var/log/nginx/access.log # 確認 # ls -lh /var/log/nginx/access.log -rw-r--r-- 1 nginx nginx 0 Oct 13 10:20 /var/log/nginx/access.log
rm とは異なり、truncate はファイルのinodeを残したままデータブロックのみを解放します。プロセスは同じファイルへの書き込みを継続できます。
「truncateしたのにディスクが解放されない」時のチェックポイント
truncate -s 0 /proc/PID/fd/FD を実行したのに容量が変わらない、あるいは操作できないケースへの対処方法を説明します。「No such file or directory」エラーが出る場合
FD番号を誤っている可能性があります。lsof の出力でFD列が 9w なら数字は 9 ですが、見誤りやすい場合は /proc/PID/fd/ を直接確認します。# ls -la /proc/1423/fd/ | grep deleted lrwx------ 1 nginx nginx 64 Oct 13 10:15 9 -> /var/log/nginx/access.log (deleted) lrwx------ 1 nginx nginx 64 Oct 13 10:15 10 -> /var/log/nginx/error.log (deleted)
「Permission denied」エラーが出る場合
他ユーザーが所有するプロセスのFDはroot でなければ操作できません。sudo truncate -s 0 /proc/PID/fd/FD を使います。truncate後もdfの数値が変わらない場合
同じファイルを複数プロセスが開いているケースがあります。lsof | grep ファイル名 で全PIDを確認し、全プロセスのFDに対して truncate -s 0 を実行してください。# lsof | grep 'access.log' nginx 1423 nginx 9w REG 8,1 15032385536 1310722 /var/log/nginx/access.log (deleted) nginx 1424 nginx 9w REG 8,1 15032385536 1310722 /var/log/nginx/access.log (deleted) # ワーカープロセス1424にも同じFDがある場合は両方を空にする # truncate -s 0 /proc/1423/fd/9 # truncate -s 0 /proc/1424/fd/9
logrotateのcopytruncateオプションとの関係
logrotate には copytruncate というオプションがあります。これは今まで解説した truncate の仕組みをローテーション処理に応用したものです。通常の
logrotate の動作(copytruncate なし):・アクセスログをリネーム(
access.log → access.log.1)・新しい空の
access.log を作成・プロセスにシグナルを送り、新しいファイルを開き直させる(
postrotate)copytruncate を使った場合の動作:・現在の
access.log の内容を access.log.1 にコピー・
access.log を truncate -s 0 で空にする・プロセスはそのまま同じFDで書き込み継続(シグナル不要)
copytruncate の設定例:# /etc/logrotate.d/myapp /var/log/myapp/*.log { daily rotate 7 missingok notifempty copytruncate compress delaycompress }
access.log.1 に入らないことがある)。定期的なログ管理の自動化を設計する際には、Linuxサーバー運用の「型」を体系的に学ぶ場として活用してみてください。
本記事のまとめ
「削除したのにディスクが空かない」問題の調査から解決までの流れをまとめます。| 状況 | コマンド・確認方法 | 対処 |
|---|---|---|
| ディスクが満杯に見える | df -h / |
次のステップへ進む |
| dfとduの差が大きい | du -sh /var/log と df -h を比較 |
削除済み・開放待ちファイルが存在 |
| 原因プロセスの特定 | lsof | grep deleted |
COMMAND・PID・FD列を確認 |
| プロセスを再起動できる | systemctl restart サービス名 |
最も確実・FDが全て閉じられる |
| プロセスを止めたくない | truncate -s 0 /proc/PID/fd/FD |
サービス継続しながら即座に解放 |
| ファイルがまだ存在する | truncate -s 0 /path/to/file |
追記を継続しながらブロックを解放 |
df と du の差を確認する習慣をつけておくだけで、トラブルシュートの初動がかなり速くなります。原因が特定できれば truncate -s 0 /proc/PID/fd/FD 一発でプロセスを止めずに解放できるので、深夜障害でも落ち着いて対処できます。
20年以上・3,100名以上の指導実績を持つ現役サーバー管理者が、RHEL10対応の少人数ハンズオンセミナーを開催しています。
>> Linux Master Pro Seminarの詳細を見る
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら
- 前のページへ:LinuxにカスタムCA証明書を追加する方法|update-ca-trustとupdate-ca-certificatesの実践手順
- この記事の属するカテゴリ:Linuxトラブルシューティングへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。
登録無料・いつでも解除できます