Linux とWindows では「ファイルを消す」際の挙動が異なるんだ。気をつけろ
これは、ファイルシステムの実装の違いによるものですが、Linux とWindows では、「ファイルを消す」際の挙動が以下のように異なります。
Linux
- ファイルが使用中であっても、ファイルを削除(rm)することができる
Windows
- ファイルが使用中の場合、ファイルを削除(rm)を削除することができない
尚、削除 に関わらず、ファイルの移動(mv)、名前の変更(rename)に関しても同じ挙動の違いがあります。
「何?それだけ?そんなん知ってるよ」と思われるかもしれませんが、この違いをきちんと理解しないと、例えば次のような問題の原因と対処法が分かりません。
Linuxサーバーでdfコマンドで使用率を確認したところ、使用可容量=0、使用率=100%となっていました。
そこで、不要なログファイルを約1.6GBほど削除しました。削除後にdfコマンドで再確認したところ、使用可容量=0、使用率=100%のままでした。使用容量は1.6GB分減っていました。
計算すると、使用率は約98%ぐらいになっていないとおかしいのに、使用率が変わらないなんて事があるのでしょうか?
原因が同じ問題として、「df とdu で表示される容量が異なる」というのもあります。
Linux はファイルを削除しても、そのファイルを使用しているプロセスがあれば、ファイルの実体は削除されない
先ほどの問題の原因は、UNIX のファイルシステムの特徴が原因です。本当かどうかは、例えば以下のコードを実行すると分かります。
Linuxファイルシステム上にあるように見えるファイルは、実際はinodeへのリンクに過ぎない。inodeには、ファイルのあらゆるプロパティ(アクセス権や所有権など)のほか、ファイルの中味が実際に存在するディスク上のデータブロックのアドレスも記録される。
rmコマンドでファイルを削除すると、ファイルのinodeを指すリンクは削除されるが、inodeそのものは削除されない。削除した時点で、他のプロセス(オーディオ・プレーヤーなど)でファイルがまだ開かれている場合もある。このようなプロセスがすべて終了し、すべてのリンクが削除されるまで、 inodeとそれに関連付けられたデータブロックが書き込みの対象となることはない。
このように実際のファイルが削除されるまでタイムラグがある
Linux ではファイルを消したつもりでも、そのファイルを使用している(ファイルをopen した)プロセスが存在する限り、ファイルの実体は消えません。実際に以下のコードで確認できます。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { FILE *f; char *path = "hoge_hoge.txt"; char buf[1024]; if(!(f = fopen(path, "w+"))) exit(1); fputs("hoge", f); if(unlink(path) < 0) { perror(path); exit(1); } rewind(f); fgets(buf, sizeof buf, f); puts(buf); fclose(f); exit(0); }
Windows の場合は、そもそも他のプロセスがファイルを使用している場合は、そのファイルを消すことができないので、この問題は起こりません。
df とdu で表示される容量が異なるのは、du は「ファイルのディスク使用量を表示する」ので、ls -a で表示されない(inode へのリンクが切れている)ファイルは考慮されないのですが、df は「ファイル・システムのディスク使用状況を表示する」ため、リンクが切れていてもファイルの実態があれば、そのファイルは考慮されるためです。
「df とdu で表示される容量が異なる」問題の解決方法
原因は分かったので、解決方法です。これは、ファイルを使用しているプロセスをkill すれば解決できます。
どのプロセスがファイルを使用しているか確認する
これは、lsof(list open files)コマンドで確認できます。lsof は、現在開いているファイルを一覧するコマンドです。なので、hoge_hoge.txt を開いているプロセスを調べたければ、以下のようにすればおk。
実際に、/proc ディレクトリを見てみると、プロセスが参照しているファイル(へのリンク)が消されていることを確認できます。
$ sudo ls -al /proc/*/fd/*
/home/lukesilvia/tmp/hoge_hoge.txt (deleted)
あとは、このプロセスをkill して完了です。
Column::今回の問題が原因で起こった障害事例
これが原因で起こった問題として、知っている事例に以下のようなものがあります。