@kyanny's blog

事実上すべての広告に見られる一貫したテーマとは、消費者の劣等性である - W・B・キイ「メディア・レイプ」

Linux: プロセスがリダイレクトしているファイルを調べる

正確には 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 でつながっていることが確認できる。

真の解法

このトラブルシュートは以下のような流れで行った。

  1. ディスク容量が急増していることを検知する
  2. 巨大化しているファイルを特定する(du /home など適当にあたりをつけて何度か実行し disk usage の変化をみて絞り込んでいく)
  3. top, ps などを利用して怪しいプロセスを発見する(例: cat /dev/random
  4. 怪しいプロセスを kill する

3 と 4 の間に、巨大化しているファイルと怪しいプロセスが関係あるか確認したかった、というのが「解法」のやり方を調べた動機だったが、この手順は良くない。 3 のプロセス特定作業が時間切れになっていたかもしれないからだ。

本来は、以下のような流れでトラブルシュートすべきだった。

  1. ディスク容量が急増していることを検知する
  2. 巨大化しているファイルを特定する
  3. lsof で件のファイルに書き込んでいるプロセスを特定する
  4. そのプロセスを 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