正確には file descriptor がどうこう、という話なのだと思うけど、ちゃんと調べる余裕がないのでニーズ起点でメモする。
シナリオ
あるホストのディスク使用量が急増している。あるプロセスが暴走して巨大なゴミデータ(ファイル)を作っていることがわかった。
ディスクを食い潰しているファイルも怪しげなプロセスも特定できているが、果たして本当にそのプロセスが件のファイルを作っている張本人なのだろうか?
プロセスを kill する前に因果関係を特定したい。
解法
あるプロセスがリダイレクトしているファイルは ls -l /proc/$PID/fd
で調べられる。 0 = STDIN, 1 = STDOUT, 2 = STDERR
vagrant@vagrant:~$ sleep 600 >/tmp/1.out 2>/tmp/2.out &
[2] 16476
vagrant@vagrant:~$ ls -l /proc/16476/fd
total 0
lrwx------ 1 vagrant vagrant 64 Jan 7 18:23 0 -> /dev/pts/0
l-wx------ 1 vagrant vagrant 64 Jan 7 18:23 1 -> /tmp/1.out
l-wx------ 1 vagrant vagrant 64 Jan 7 18:23 2 -> /tmp/2.out
STDIN が pipe の場合、パイプラインの上流にあるプロセスを特定するには lsof | grep $PIPE_ID
を使う。
How can I get more info on open pipes show in /proc in Linux? - Server Fault
vagrant@vagrant:~$ yes | sleep 600 &
[6] 16491
vagrant@vagrant:~$ ls -l /proc/16491/fd
total 0
lr-x------ 1 vagrant vagrant 64 Jan 7 18:25 0 -> 'pipe:[45732]'
lrwx------ 1 vagrant vagrant 64 Jan 7 18:25 1 -> /dev/pts/0
lrwx------ 1 vagrant vagrant 64 Jan 7 18:25 2 -> /dev/pts/0
vagrant@vagrant:~$ lsof | grep 45732
yes 16490 vagrant 1w FIFO 0,12 0t0 45732 pipe
sleep 16491 vagrant 0r FIFO 0,12 0t0 45732 pipe
vagrant@vagrant:~$ ls -l /proc/16490/fd
total 0
lrwx------ 1 vagrant vagrant 64 Jan 7 18:25 0 -> /dev/pts/0
l-wx------ 1 vagrant vagrant 64 Jan 7 18:25 1 -> 'pipe:[45732]'
lrwx------ 1 vagrant vagrant 64 Jan 7 18:25 2 -> /dev/pts/0
↑の例だと、 PID 16491 (sleep 600) の STDIN は 'pipe:[45732]'
となっていて、この 45732 という数字で lsof の結果を grep すると、 pipe の両端(入力側と出力側)のプロセスの情報が得られる。
片方の PID (16491) は既知なので、もう片方の PID 16490 (yes) が入力側だとわかる。
ls -l /proc/16490/fd
の結果は STDOUT が 'pipe:[45732]'
となっており、 PID 16491 (sleep 600) の STDIN と一致するので、この二つのプロセスが pipe でつながっていることが確認できる。
真の解法
このトラブルシュートは以下のような流れで行った。
- ディスク容量が急増していることを検知する
- 巨大化しているファイルを特定する(
du /home
など適当にあたりをつけて何度か実行し disk usage の変化をみて絞り込んでいく)
- top, ps などを利用して怪しいプロセスを発見する(例:
cat /dev/random
)
- 怪しいプロセスを kill する
3 と 4 の間に、巨大化しているファイルと怪しいプロセスが関係あるか確認したかった、というのが「解法」のやり方を調べた動機だったが、この手順は良くない。 3 のプロセス特定作業が時間切れになっていたかもしれないからだ。
本来は、以下のような流れでトラブルシュートすべきだった。
- ディスク容量が急増していることを検知する
- 巨大化しているファイルを特定する
- lsof で件のファイルに書き込んでいるプロセスを特定する
- そのプロセスを kill する
これなら最短で問題のプロセスを特定できる。大量のプロセス一覧から怪しいプロセスを探す必要はない。
vagrant@vagrant:~$ perl -E 'while(true){ say 0; sleep 0.1}' >/tmp/0.txt &
[1] 16536
vagrant@vagrant:~$ perl -E 'while(true){ say 0; sleep 0.1}' >/tmp/0.txt &
[2] 16537
vagrant@vagrant:~$ lsof /tmp/0.txt
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
perl 16536 vagrant 1w REG 253,0 49152 3801099 /tmp/0.txt
perl 16537 vagrant 1w REG 253,0 49152 3801099 /tmp/0.txt
vagrant@vagrant:~$ ps 16536 16537
PID TTY STAT TIME COMMAND
16536 pts/0 S 0:01 perl -E while(true){ say 0; sleep 0.1}
16537 pts/0 S 0:01 perl -E while(true){ say 0; sleep 0.1}
vagrant@vagrant:~$ kill 16536 16537
vagrant@vagrant:~$
[1]- Terminated perl -E 'while(true){ say 0; sleep 0.1}' > /tmp/0.txt
[2]+ Terminated perl -E 'while(true){ say 0; sleep 0.1}' > /tmp/0.txt
2 のファイル特定作業も手間取って時間切れになっていたかもしれない。もっと良い方法を知りたい。
2020-01-12 追記
linux ファイル容量降順にソート - Qiita
du -a
オプションをつけるとディレクトリだけでなくファイルも表示される(ディレクトリも含まれる)。
du -a | sort -rn | head
du -ak | sort -rn | head # KB単位
du -am | sort -rn | head # MB単位
du -ag | sort -rn | head # GB単位
du -ah # 単位をいい感じに表示してくれるが sort しづらい
ファイルだけにしたい場合は find を使う。
find - Getting size with du of files only - Unix & Linux Stack Exchange
find . -type f -exec du -a {} + | sort -rn | head