psコマンドでプロセスの一覧を見よう

# Linuxでプロセスの一覧を見たい
Linux環境で、プロセスの一覧を見たい場合に出てくるのは、`ps`コマンドだと思います。

「今動いてるプロセスを見たい」、「このプロセス、今いるっけ？」といった声が挙がる場で、よく使われるコマンドだと思います。

注）本投稿は、Ubuntu Linux 18.04 LTSで確認しております

```shell
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 18.04.1 LTS
Release:	18.04
Codename:	bionic
```

オプションって、どうしましょうか？自分の観測範囲だと、以下の2つが多い気がします。

```shell
$ ps aux
$ ps -ef
```
`-`がないのは、BSDスタイルオプションというやつですね。

出力例。  

```shell
$ ps aux
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1  0.0  0.0 225536  8684 ?        Ss   08:56   0:05 /sbin/init splash
root          2  0.0  0.0      0     0 ?        S    08:56   0:00 [kthreadd]
root          4  0.0  0.0      0     0 ?        I<   08:56   0:00 [kworker/0:0H]
root          6  0.0  0.0      0     0 ?        I<   08:56   0:00 [mm_percpu_wq]

〜省略〜


$ ps -ef
UID         PID   PPID  C STIME TTY          TIME CMD
root          1      0  0 08:56 ?        00:00:05 /sbin/init splash
root          2      0  0 08:56 ?        00:00:00 [kthreadd]
root          4      2  0 08:56 ?        00:00:00 [kworker/0:0H]
root          6      2  0 08:56 ?        00:00:00 [mm_percpu_wq]

〜省略〜
```

それぞれ、表示内容が異なりますね。

`ps aux`だと、CPU使用率やメモリに関する情報が取得できます。ざっと確認するなら、まずはこちらで。

`ps -ef`だと、親プロセスのID（`PPID`）が表示されています。親プロセスが気になることもあるので、これはこれで便利です。

ちなみに、親子関係を見るだけなら`aux`オプションでも`f`を足すと階層構造表示なら可能です。

```shell
$ ps auxf
USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          2  0.0  0.0      0     0 ?        S    08:56   0:00 [kthreadd]
root          4  0.0  0.0      0     0 ?        I<   08:56   0:00  \_ [kworker/0:0H]
root          6  0.0  0.0      0     0 ?        I<   08:56   0:00  \_ [mm_percpu_wq]
root          7  0.0  0.0      0     0 ?        S    08:56   0:01  \_ [ksoftirqd/0]
root          8  0.0  0.0      0     0 ?        I    08:56   0:08  \_ [rcu_sched]
root          9  0.0  0.0      0     0 ?        I    08:56   0:00  \_ [rcu_bh]
root         10  0.0  0.0      0     0 ?        S    08:56   0:00  \_ [migration/0]
root         11  0.0  0.0      0     0 ?        S    08:56   0:00  \_ [watchdog/0]
root         12  0.0  0.0      0     0 ?        S    08:56   0:00  \_ [cpuhp/0]

〜省略〜
```

各オプションの意味ですが、こちらを参照。

[【 ps 】コマンド――実行中のプロセスを一覧表示する](http://www.atmarkit.co.jp/ait/articles/1603/28/news022.html)

* `a` … 端末を持つ全てのプロセスを表示する
* `u` … ユーザー指向のフォーマットで表示する
* `x` … 端末を持たない全てのプロセスを表示する
* `-e` … 全てのプロセスを表示する
* `f` … 階層表示する

# 特定の項目でソートしたい
`ps`コマンドの結果を、特定の項目でソートしたいことがあるかもしれません。

`k`（または`--sort`）オプションを使いましょう。

例えば、以下のコマンドでCPU使用率でソートできます。

```shell
$ ps aux k -pcpu
```

メモリなら。

```shell
$ ps aux k -pmem
$ ps aux k -rss
```

`k`の後に`-フォーマット`で指定するのは、`man`で見るか、以下のページを見たりするとよいでしょう。

[PS / 標準フォーマット指定子](https://linuxjm.osdn.jp/html/procps/man1/ps.1.html)

# スレッドのIDを表示したい
`ps`コマンドに、`-L`オプションを付けると、表示内容にLWP（軽量プロセスまたはスレッドのID）と、NLWP（プロセスにおけるスレッドの数）が追加されます。

```shell
$ ps aux -L | head -n 5
USER        PID    LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root          1      1  0.0    1  0.0 225536  8700 ?        Ss   08:56   0:05 /sbin/init splash
root          2      2  0.0    1  0.0      0     0 ?        S    08:56   0:00 [kthreadd]
root          4      4  0.0    1  0.0      0     0 ?        I<   08:56   0:00 [kworker/0:0H]
root          6      6  0.0    1  0.0      0     0 ?        I<   08:56   0:00 [mm_percpu_wq]
```

これで、プロセスから1段掘り下げて、スレッド単位でCPUの情報を見ることができます。

## Javaのスレッドダンプと紐付けよう
なんとなく、表示されたLWPの情報とJavaのスレッドダンプと紐付けてみます。

Apache Tomcatで試してみましょう。ダウンロードして、起動。

```shell
$ java -version
openjdk version "1.8.0_191"
OpenJDK Runtime Environment (build 1.8.0_191-8u191-b12-0ubuntu0.18.04.1-b12)
OpenJDK 64-Bit Server VM (build 25.191-b12, mixed mode)

$ wget http://ftp.jaist.ac.jp/pub/apache/tomcat/tomcat-9/v9.0.14/bin/apache-tomcat-9.0.14.tar.gz
$ tar xf apache-tomcat-9.0.14.tar.gz
$ cd apache-tomcat-9.0.14
$ bin/startup.sh
```

`jcmd`でスレッドダンプの取得。  

```shell
$ jcmd [PID] Thread.print
```

ここでは、PIDが`21017`のものを使って実例を見てみます。

`nid=`の部分が、スレッドのIDです。

```shell
$ jcmd 21017 Thread.print | grep nid=
"Attach Listener" #44 daemon prio=9 os_prio=0 tid=0x00007f20bc001000 nid=0x5298 waiting on condition [0x0000000000000000]
"ajp-nio-8009-Acceptor-0" #42 daemon prio=5 os_prio=0 tid=0x00007f20fc7fa800 nid=0x5260 runnable [0x00007f206b0ef000]
"ajp-nio-8009-ClientPoller-1" #41 daemon prio=5 os_prio=0 tid=0x00007f20fc7f8800 nid=0x525f runnable [0x00007f206b1f0000]
"ajp-nio-8009-ClientPoller-0" #40 daemon prio=5 os_prio=0 tid=0x00007f20fc7dd800 nid=0x525e runnable [0x00007f206b2f1000]
"ajp-nio-8009-exec-10" #39 daemon prio=5 os_prio=0 tid=0x00007f20fc7c3000 nid=0x525d waiting on condition [0x00007f206b3f2000]
"ajp-nio-8009-exec-9" #38 daemon prio=5 os_prio=0 tid=0x00007f20fc7c1000 nid=0x525c waiting on condition [0x00007f206b4f3000]
"ajp-nio-8009-exec-8" #37 daemon prio=5 os_prio=0 tid=0x00007f20fc7bf000 nid=0x525b waiting on condition [0x00007f206b5f4000]
"ajp-nio-8009-exec-7" #36 daemon prio=5 os_prio=0 tid=0x00007f20fc7bd800 nid=0x525a waiting on condition [0x00007f206b6f5000]
"ajp-nio-8009-exec-6" #35 daemon prio=5 os_prio=0 tid=0x00007f20fc7bb800 nid=0x5259 waiting on condition [0x00007f206b7f6000]
"ajp-nio-8009-exec-5" #34 daemon prio=5 os_prio=0 tid=0x00007f20fc7ba000 nid=0x5258 waiting on condition [0x00007f206b8f7000]
"ajp-nio-8009-exec-4" #33 daemon prio=5 os_prio=0 tid=0x00007f20fc7b8800 nid=0x5257 waiting on condition [0x00007f206b9f8000]
"ajp-nio-8009-exec-3" #32 daemon prio=5 os_prio=0 tid=0x00007f20fc7b6800 nid=0x5256 waiting on condition [0x00007f206baf9000]
"ajp-nio-8009-exec-2" #31 daemon prio=5 os_prio=0 tid=0x00007f20fc770000 nid=0x5255 waiting on condition [0x00007f206bbfa000]
"ajp-nio-8009-exec-1" #30 daemon prio=5 os_prio=0 tid=0x00007f20fc76e000 nid=0x5254 waiting on condition [0x00007f206bcfb000]
"http-nio-8080-Acceptor-0" #29 daemon prio=5 os_prio=0 tid=0x00007f20fc76d000 nid=0x5253 runnable [0x00007f206bdfc000]
"http-nio-8080-ClientPoller-1" #28 daemon prio=5 os_prio=0 tid=0x00007f20fc76a000 nid=0x5252 runnable [0x00007f206befd000]
"http-nio-8080-ClientPoller-0" #27 daemon prio=5 os_prio=0 tid=0x00007f20fc768000 nid=0x5251 runnable [0x00007f206bffe000]
"http-nio-8080-exec-10" #26 daemon prio=5 os_prio=0 tid=0x00007f20fc765800 nid=0x5250 waiting on condition [0x00007f20d013b000]
```

`-q`で、対象のPIDに絞ってプロセスの情報を表示。

```shell
$ ps aux -L -q 21017
USER        PID    LWP %CPU NLWP %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
xxxxx     21017  21017  0.0   48  0.9 6979556 127592 pts/9  Sl   12:41   0:00 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.util.logging.config.file=.....
xxxxx     21017  21018  0.4   48  0.9 6979556 127592 pts/9  Sl   12:41   0:01 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.util.logging.config.file=.....
xxxxx     21017  21019  0.0   48  0.9 6979556 127592 pts/9  Sl   12:41   0:00 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.util.logging.config.file=.....
xxxxx     21017  21020  0.0   48  0.9 6979556 127592 pts/9  Sl   12:41   0:00 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.util.logging.config.file=.....
xxxxx     21017  21021  0.0   48  0.9 6979556 127592 pts/9  Sl   12:41   0:00 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Djava.util.logging.config.file=.....

〜省略〜
```

ここでは、LWPが`21067`のものを対象にしてみましょう。

`jcmd`の結果だと、スレッドのIDは16進数表示だったので、例えば`printf`で表記変更します。

```shell
$ printf '%#x' 21067
0x524b
```

これを`jcmd`の結果と合わせて、絞り込めばよいわけですね。

```shell
$ jcmd 21017 Thread.print | grep `printf '%#x' 21067`
"http-nio-8080-exec-5" #21 daemon prio=5 os_prio=0 tid=0x00007f20fc75c800 nid=0x524b waiting on condition [0x00007f20d0640000]
```

`nid=0x524b`のスレッドがヒットしました。これを利用すると、CPUを消費しているスレッドを特定し、そのスタックトレースを見る、といったことができるようになります。

実際には、スタックトレースも一緒に見ると思うので、`grep`で表示情報も絞ることはしないと思いますが、紐付けの参考として。