正確には 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 追記
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