psでプロセス一覧を確認する
「動いているはず」を、目で確かめる
アプリが応答しない、ポートが既に使われていると言われる、メモリが急に減っている。どれも最初の一手は同じで、今このマシンで何が走っているかを一覧することです。それを出すのが ps です。
ps は特別なことを何もしていません。カーネルが /proc の下に置いている情報を読んで、表の形に整えているだけです。だから特別な権限も要らず、いつでも打てます。
何も付けないと、自分の端末の分しか出ない
ターミナル
$ ps
PID TTY TIME CMD
1234 pts/0 00:00:00 bash
1300 pts/0 00:00:00 ps出てきたのは、今使っているシェルと、たった今打った ps の 2 つだけでした。既定では、自分の端末に紐づくものしか見せません。他の人が動かしているものやサービスまで含めて全部見るには -e を付けます。あわせて -f を付けると、親子関係や起動時刻まで含む詳しい形になります。
ターミナル
$ ps -ef | head -4
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 09:00 ? 00:00:01 /sbin/init
www-data 789 1 0 09:01 ? 00:00:00 nginx master
www-data 790 789 0 09:01 ? 00:00:00 nginx worker当面は ps -ef の形だけ覚えておけば足ります。PPID の列を見ると、790 番の親が 789 番だと読み取れます。
絞ろうとすると grep 自身が邪魔をする
ターミナル
$ ps -ef | grep nginx | grep -v grep
www-data 789 1 0 09:01 ? 00:00:00 nginx master
www-data 790 789 0 09:01 ? 00:00:00 nginx workergrep nginx が動いている間は、その grep 自体もプロセスとして存在していて、しかもコマンド行に nginx という文字を含んでいます。だから何もしないと、自分自身が結果に混ざります。後ろに grep -v grep をつないで取り除くのが定番の書き方です。
重い順に並べて、犯人を上に持ってくる
ターミナル
$ ps -eo pid,user,%mem,%cpu,cmd --sort=-%mem | head -3
PID USER %MEM %CPU CMD
2100 deploy 8.2 1.5 node /app/server.js
789 www-data 1.0 0.1 nginx master-o で欲しい列だけを選び、--sort で並べ替えられます。頭に - を付けると降順になります。-%mem ならメモリを食っている順、-%cpu なら CPU を食っている順に並ぶので、サーバーが重いときの最初の 1 枚として使えます。
%MEMはマシン全体の物理メモリに対する割合です。コンテナの中で打つと、そのコンテナに割り当てた上限ではなくホスト全体が分母になります。数字が実際より小さく見えるので、上限に張り付いているのに気づけないことがあります。
対象を1つ選び、結果を保存する
-p は PID を指定して対象を選ぶオプションです。-o comm= はコマンド名だけを、見出しなしで表示します。たとえば確認済みの PID が2100なら、次のように見られます。
ターミナル
ps -p 2100 -o comm=名前だけを取得するので、絞り込みに使う grep のコマンド行は混ざりません。演習でも、ほかの人の sleep と混ざらないように、用意した PID の1件だけを調べます。
出力は > でファイルへ保存できます。保存した内容を grep に渡したり、wc -l で行数を数えたりできます。< はファイルの内容をコマンドの入力へ渡す指定です。別の題材で試すと次のようになります。
ターミナル
printf '%s\n' pear plum > fruit.txt
grep pear fruit.txt
wc -l < fruit.txt最後の表示は 2 です。wc -l は空白を添えて件数を表示する場合がありますが、採点では前後の空白を除いて比べます。
演習の準備済みコードを読む
pid=$! は直前に裏で起動したプロセスの番号を保存します。$pid でその値を使えます。変数への代入には = の前後に空白を入れません。
ターミナル
worker_pid=2100
ps -p "$worker_pid" -o comm=& による起動は後の回で練習するので、今回はひな形で渡します。$(コマンド) はコマンドの出力を値として使う書き方です。mktemp は空の一時ファイルを作り、その名前を出力します。
ターミナル
note_file=$(mktemp)
echo "memo" > "$note_file"
rm -f "$note_file"後始末の kill は終了の合図を送り、wait は自分が起動したプロセスの終了を待ちます。2>/dev/null はエラーの表示だけを捨てる指定です。これらは後の回で扱うため、自分で書き直す必要はありません。準備と後始末だけを読む小さな例は次のとおりです。sleep は指定した秒数だけ待つコマンドです。
ターミナル
sleep 10 &
worker_pid=$!
kill "$worker_pid"
wait "$worker_pid" 2>/dev/null空欄には、何もせず成功するコマンドを置いてあります。その書き方は次のとおりです。
ターミナル
:空欄だけを自分のコマンドへ置き換えます。
課題
- ps -p "$pid" -o comm= で用意したプロセスのコマンド名だけを取得し、"$ps_all" に保存する
- grep で sleep を含む行を "$ps_all" から取り出し、"$ps_out" に保存する
- wc -l に "$ps_out" を入力して件数を表示する。表示は 1 の後に、準備済みの後始末が出す done が続く