Linuxでプロセスを調査する時によく使うコマンドや使い方

# はじめに

Linuxで特定のプロセスの問題調査によく使用するコマンドや方法を紹介します。
また、それぞれのコマンドの詳細な説明は他に譲り、ここではあくまで紹介にとどめます。

## ・`ps` / `pstree`コマンド

非常に有名なコマンドですが、現在動いているプロセスを調べるには`ps`コマンドを使用します。
また、`ps`コマンドには色々なオプションがあり、特に問題点が見つけやすい`aux`オプションをよく使用します。

:::note info
`aux`オプションはそれぞれ下記の意味を持ちます。
* a：端末操作のプロセスを表示
* u：ＣＰＵやメモリの情報を表示
* x：端末操作ではないプロセスを表示(デーモン/サービスなども表示されます)
:::

```bash:ps auxコマンド結果
$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.1  41688  5424 ?        Ss   11月30   0:16 /usr/lib/systemd/systemd
root         2  0.0  0.0      0     0 ?        S    11月30   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        I<   11月30   0:00 [rcu_gp]
・・・
```

また、プロセスの親子関係を見たい場合は`f`オプションを追加したり、`pstree`コマンドを使用するとツリー構造で表示されて便利です。

```bash:ps auxfコマンド結果
$ ps auxf
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         2  0.0  0.0      0     0 ?        S    11月30   0:00 [kthreadd]
root         3  0.0  0.0      0     0 ?        I<   11月30   0:00  \_ [rcu_gp]
・・・
root      2294  0.0  0.1 110840  7320 ?        Ss   11月30   0:00 /usr/sbin/sshd -D
root     29659  0.0  0.2 148512  8672 ?        Ss   11:29    0:00  \_ sshd: user [priv]
user     29676  0.0  0.1 148512  4432 ?        S    11:29    0:00      \_ sshd: user@pts/0
user     29677  0.0  0.1 124872  4132 pts/0    Ss   11:29    0:00          \_ -bash
user     29727  0.0  0.1 162464  4020 pts/0    R+   11:39    0:00              \_ ps auxf
・・・
```

```bash:pstree -aupコマンド結果
$ pstree -aup 2294
sshd,2294 -D
  └─sshd,29659
      └─sshd,29676,user
          └─bash,29677
              └─pstree,29841 -aup 2294
```

`ps`コマンドではコマンドなどの文字列が長い場合に途中で文字列が切れてしまう場合がありますが、そういう場合は`ww`オプションを追加してあげると切れてしまった部分も表示されます。

また、`ps`コマンドはプロセスを探すために`grep`コマンドと組み合わせる場合も多いと思いますが、その際は`grep -e ^U -e [先頭文字]残り文字列`を使用すると、ヘッダーを表示しつつ`ps`コマンドのプロセスを表示せずにキレイに表示できます。
```bash:grepコマンドでヘッダーを出しながらsshdのプロセスを表示
$ ps aux | grep -e ^U -e [s]shd
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root      2294  0.0  0.1 110840  7320 ?        Ss   11月30   0:00 /usr/sbin/sshd -D
root     29659  0.0  0.2 148512  8672 ?        Ss   11:29   0:00 sshd: user [priv]
user     29676  0.0  0.1 148512  4432 ?        S    11:29   0:00 sshd: user@pts/0
$ 
```

`grep`コマンドと同様に、特定の複数のプロセスのメモリ使用量の合計を調べたい時などに`awk`コマンドと組み合わせる場合もあると思いますが、こちらも`[先頭文字]残り文字列`で該当プロセスを絞ってあげると`ps`コマンドのプロセスを含まない正確な合計値が集計できます。
```bash:awkコマンドでsshdのRSSを集計
$ ps aux | awk '/[s]shd/{sum+=$6}END{print sum}'
20424
$ 
```


## ・`top` / `htop`コマンド

`ps`コマンドは他のコマンドと組み合わせる際によく使用するコマンドですが、全体的にザックリ現在プロセスがどんな状況なのか確認するには`top`や`htop`をよく使用します。

```bash:topコマンド結果
$ top
top - 11:38:19 up 1 days,  8:01,  1 user,  load average: 0.00, 0.00, 0.00
Tasks:  41 total,   1 running,  22 sleeping,   0 stopped,   0 zombie
%Cpu(s):  0.0 us,  0.0 sy,  0.0 ni,100.0 id,  0.0 wa,  0.0 hi,  0.0 si,  0.0 st
KiB Mem :  3956464 total,  3259792 free,   109392 used,   587280 buff/cache
KiB Swap:        0 total,        0 free,        0 used.  3621688 avail Mem 

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU %MEM     TIME+ COMMAND                      30000 user      20   0  168840   4412   3884 R   0.3  0.1   0:00.05 top
    1 root      20   0   41688   5424   3816 S   0.0  0.1   0:16.30 systemd
    2 root      20   0       0      0      0 S   0.0  0.0   0:00.17 kthreadd
    3 root       0 -20       0      0      0 I   0.0  0.0   0:00.00 rcu_gp
$ 
```

`top`コマンド実行中は入力を受け付けており、その中でも`L`(文字列検索)、`P`(CPU使用順でソート)、`M`(メモリ使用順にソート)をよく使用します。`h`を入力するとヘルプが出てくるので、興味のある方はそちらを参照してください。

また、`top`コマンドを拡張した`htop`というコマンドがあります。見た目もグラフィカルで見やすく、マウスや十字キーで画面スクロールやプロセスの選択(！)ができ、`top`コマンドでできることは大抵できる優れモノです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/699056/54ff948b-2cac-e61b-b277-f9f5847339b5.png)
ただ、`top`コマンドのようにデフォルトで入っていない事が多いため追加インストールが必要、メモリやCPUリソースを多く取るなどの欠点もあります。


## ・`lsof`コマンド

`ps`コマンドや`top`コマンドなどで調査対象のプロセスが分かった後に、該当プロセスが掴んでいるファイルやポートを調べる場合に`lsof`コマンドをよく使用します。

```bash:lsofコマンドでsshdプロセスを調査
$ sudo lsof -p 29659
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    29659 root  cwd    DIR              259,1      302       96 /
sshd    29659 root  rtd    DIR              259,1      302       96 /
sshd    29659 root  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
sshd    29659 root  mem    REG              259,1    15408 12864569 /usr/xxx/xxx/xxx.so
・・・
sshd    29659 root    3u  IPv4             658172      0t0      TCP ip-xxx-xxx-xxx-xxx:ssh->ip-xxx-xxx-xxx-xxx:48183 (ESTABLISHED)
sshd    29659 root    4u  unix 0xffffa0d25215d800      0t0   658603 socket
sshd    29659 root    6w  FIFO               0,23      0t0    72119 /run/systemd/sessions/2604.ref
sshd    29659 root    7u  unix 0xffffa0d25207dc00      0t0   658237 socket
```

また、ロックファイルなどのファイルを現在どのプロセスが掴んでいるか調べる事もできるので、比較的使用頻度の高いコマンドになります。

```bash:sshdファイルをつかんでいるプロセスを調査
$ sudo lsof /usr/sbin/sshd
COMMAND   PID      USER  FD   TYPE DEVICE SIZE/OFF     NODE NAME
sshd     2294      root txt    REG  259,1   832328 13021527 /usr/sbin/sshd
sshd    29659      root txt    REG  259,1   832328 13021527 /usr/sbin/sshd
sshd    29676      user txt    REG  259,1   832328 13021527 /usr/sbin/sshd
sshd    30036      root txt    REG  259,1   832328 13021527 /usr/sbin/sshd
sshd    30053      user txt    REG  259,1   832328 13021527 /usr/sbin/sshd
$ 
```

:::note info
`ps`コマンドなどでプロセスを特定した後にタイムラグなく`lsof`コマンドで確認したい場合は、プロセスIDを特定する事に特化した`pidof`コマンドや`pgrep`コマンドなどを使用するとスッキリ記述する事ができます。

```bash:rpcbindを含んだコマンドのプロセスがつかんでいるファイルやポートを表示(1プロセス)
$ sudo lsof -p $(pidof rpcbind)
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
rpcbind 1809  rpc  cwd    DIR              259,1      302       96 /
rpcbind 1809  rpc  rtd    DIR              259,1      302       96 /
rpcbind 1809  rpc  txt    REG              259,1    61432 12997494 /usr/sbin/rpcbind
・・・
```

```bash:sshdを含んだコマンドのプロセスがつかんでいるファイルやポートを表示(複数プロセス)
$ pgrep sshd | sed 's/^/echo "---";sudo lsof -p/e'
---
COMMAND  PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    2294 root  cwd    DIR              259,1      302       96 /
sshd    2294 root  rtd    DIR              259,1      302       96 /
sshd    2294 root  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
・・・
---
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    29659 root  cwd    DIR              259,1      302       96 /
sshd    29659 root  rtd    DIR              259,1      302       96 /
sshd    29659 root  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
・・・
---
COMMAND   PID      USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    29676      user  cwd    DIR              259,1      302       96 /
sshd    29676      user  rtd    DIR              259,1      302       96 /
sshd    29676      user  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
・・・
---
COMMAND   PID USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    30036 root  cwd    DIR              259,1      302       96 /
sshd    30036 root  rtd    DIR              259,1      302       96 /
sshd    30036 root  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
・・・
---
COMMAND   PID      USER   FD   TYPE             DEVICE SIZE/OFF     NODE NAME
sshd    30053      user  cwd    DIR              259,1      302       96 /
sshd    30053      user  rtd    DIR              259,1      302       96 /
sshd    30053      user  txt    REG              259,1   832328 13021527 /usr/sbin/sshd
・・・
$ 
```

:::

## ・`/proc/(プロセスID)/`ディレクトリ

`lsof`コマンドでファイルやポートを調べる事もできますが、ザックリこのプロセス何やっているのか調べる場合は`/proc/(プロセスID)/`ディレクトリを確認する方が手っ取り早い場合があります。

`/proc/(プロセスID)/`ディレクトリにはプロセスに関する多くの情報が格納されていますが、その中でも使用頻度が高い確認方法を紹介します。

```bash:どんなコマンドが実行されているのか確認
$ sudo ls -l /proc/2294/exe
lrwxrwxrwx 1 root root 0 11月 30 03:27 /proc/2294/exe -> /usr/sbin/sshd
$ sudo cat /proc/2294/cmdline
/usr/sbin/sshd-D$ 
$ sudo cat /proc/2294/environ
LANG=en_US.UTF-8PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/binNOTIFY_SOCKET=/run/systemd/notifySSH_USE_STRONG_RNG=0$ 
```

```bash:プロセスの状態やメモリ使用状況を確認
$ sudo cat /proc/2294/status
Name:	sshd
Umask:	0022
State:	S (sleeping)
Tgid:	2294
Ngid:	0
Pid:	2294
PPid:	1
TracerPid:	0
Uid:	0	0	0	0
Gid:	0	0	0	0
FDSize:	64
Groups:	 
NStgid:	2294
NSpid:	2294
NSpgid:	2294
NSsid:	2294
VmPeak:	  110868 kB
VmSize:	  110840 kB
VmLck:	       0 kB
・・・
```

```bash:プロセスが使用しているファイルやポートを確認
$ sudo ls -l /proc/2294/fd
合計 0
lr-x------ 1 root root 64 11月 30 03:27 0 -> /dev/null
lrwx------ 1 root root 64 11月 30 03:27 1 -> socket:[17744]
lrwx------ 1 root root 64 11月 30 03:27 2 -> socket:[17744]
lrwx------ 1 root root 64 11月 30 03:27 3 -> socket:[19174]
lrwx------ 1 root root 64 11月 30 03:27 4 -> socket:[19183]
```

```bash:プロセスが使用しているIO統計情報を確認
$ sudo cat /proc/2294/io
rchar: 1438914
wchar: 748992
syscr: 20279
syscw: 19141
read_bytes: 1880064
write_bytes: 24576
cancelled_write_bytes: 0
$ 
```

他にも様々な情報が格納されているので、興味のある方は`man proc`コマンドなどでprocを調べてみてください。


## ・`strace`コマンド

さらに踏み込んだ調査方法として、現在プロセスが内部的にどのような処理を行なっているのか`strace`コマンドで見る事ができます。

また、`strace`コマンドはプロセスが処理しているシステムコールを表示する事ができるので非常に詳細な情報を確認する事ができますが、このプロセス止まっているように見えるけど何やっているんだろう...というような場合に確認するには細かすぎてよくわからなくなってしまうので、出力する内容を限定する`-e read,write,open,execve`オプションをよく使用します。

```bash:echo testコマンドの処理内容を確認
$ strace -e read,write,open,execve echo test
execve("/bin/echo", ["echo", "test"], [/* 36 vars */]) = 0
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340\30\2\0\0\0\0\0"..., 832) = 832
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
write(1, "test\n", 5test
)                   = 5
+++ exited with 0 +++
$ 
```

`strace`コマンドはプロセスが現在実行している詳細な内容をリアルタイムに確認する事ができる非常に強力なコマンドなので、是非活用して頂ければと思います。


## ・`gcore` / `gdb`コマンド

さらにさらに踏み込んだ調査方法として、プロセスが使用しているメモリや処理に必要な情報を断面的にまるごと出力できる`gcore`というコマンドがあります。

`strace`コマンドではプロセスが実行しているシステムコールをリアルタイムで確認する事ができましたが、`gcore`コマンドでは現時点でプロセスが使用しているメモリの内容や処理内容(コアダンプ)を出力し、`gdb`コマンドというデバッガでメモリや処理内容を確認していく事ができます。

:::note warn
`gcore`コマンドは一貫性のあるメモリ情報を出力するために一時的にプロセスを中断します。コアダンプ取得後に処理は再開されますが、止める事ができないプロセスなどには使用しないでください。
:::

プロセスが異常終了した場合に異常終了直前のコアダンプが出力され(coreが吐かれ)、`gdb`コマンドなどのデバッガを使用して原因調査を行う流れが一般的かと思いますが、`gcore`コマンドを使用する事で異常状態にならずともコアダンプが出力できる、`gdb`コマンドで本命プロセスにアタッチせずに解析できる、などのメリットがあります。