プロセスの終了 kill
止めたはずのアプリが、ポートを掴んだまま離さない
デプロイし直そうとすると address already in use と言われる。前のプロセスがまだ生きています。ターミナルは閉じたのに、そのプロセスは別のところで動き続けている、という状況です。
こういうときに使うのが kill です。名前は物騒ですが、実際にやっているのは合図を送ることだけで、その合図を受けてどうするかは相手のプロセスが決めます。だから kill を打っても止まらないことがありますし、止める以外の合図もあります。
まず「片付けてから終わって」と頼む
ターミナル
$ ps -ef | grep "node /app/server.js" | grep -v grep
deploy 2100 1 0 09:05 ? 00:01:23 node /app/server.js
$ kill 2100番号だけを渡すと、終了してほしいという依頼が送られます。これを受け取ったプロセスは、書きかけのファイルを閉じる、処理中のリクエストを返しきる、一時ファイルを消す、といった後片付けをしてから、自分で終了します。データベースやアプリケーションサーバーは、この依頼に対する処理を最初から持っているのが普通です。
止まったかどうかは、少し待ってからもう一度 ps で探せば分かります。見つからなければ成功です。
それでも消えないときだけ、問答無用に切り替える
ターミナル
$ kill -9 2100-9 を付けると、プロセス本人には一切通知されず、カーネルが直接そのプロセスを消します。相手が無限ループに入っているときや、終了処理そのものが固まっているときは、これしかありません。
ただし後片付けは一切行われません。書きかけのファイルは中途半端なまま残り、確保していた一時ファイルやロックファイルも消えずに残ります。データベースを -9 で落とすと、次の起動で復旧処理が走り、最悪の場合はデータが壊れます。最初の手段としては使わず、普通の kill を試して、数秒待っても消えないときだけに限ってください。
名前で送ると、似た名前まで巻き込む
ターミナル
$ pgrep -af "node /app/server.js"
2100 node /app/server.js
$ pkill -f "node /app/server.js"番号を毎回調べるのが面倒なときは、pkill で名前を指定できます。-f を付けるとコマンド行全体に対して照合するので、node とだけ書いて別の Node アプリまで巻き込む事故を避けられます。
pkillを打つ前に、同じ条件でpgrep -afを打ってください。合図が飛ぶ相手の一覧がそのまま出るので、意図しないものが混ざっていればそこで気づけます。pkillは確認を聞いてこないので、気づける場所はここだけです。
合図を送らずに確かめ、終了を待つ
kill -0 は終了の合図を送らず、指定した PID が存在し、自分に合図を送る権限があるかを確認します。成功すれば終了ステータスは0、存在しないか権限がなければ0以外になります。たとえば調べたアプリの PID が2100なら次の形です。
ターミナル
kill -0 2100if はコマンドの成功・失敗で処理を分けます。次は別の題材で、ログに Ready が見つかったときだけ表示する例です。
ターミナル
if grep Ready build.log; then
echo "準備完了"
fithen の前のコマンドが成功したときだけ、then と fi の間が動きます。空欄の false は必ず失敗するコマンドなので、ひな形では alive が表示されません。演習では条件の1行だけを書き換え、分岐の枠と表示はそのまま使います。
自分が起動したプロセスが終わるまで待つには wait PID を使います。次は終了させる演習とは別に、短い処理が自然に終わるのを待つ例です。
ターミナル
sleep 2 &
worker_pid=$!
wait "$worker_pid"
echo finished& は裏で起動する指定、$! は直前に裏で起動したプロセスの PID です。worker_pid=$! で番号を保存し、"$worker_pid" で使います。起動と番号保存は演習では用意済みです。
シグナルで終了したプロセスを wait すると0以外が返ることがありますが、終了を待つ処理自体は行われています。2>/dev/null はエラーの表示を捨てる指定です。たとえば ls missing.log 2>/dev/null は、ファイルがなくてもエラー文を表示しません。演習では終了通知を非表示にするために付けます。空欄には、何もせず成功するコマンドを置いてあります。その書き方は次のとおりです。
ターミナル
:空欄だけを自分のコマンドへ置き換えます。
課題
- kill -0 "$pid" で存在と送信権限を確認する。成功時の alive は用意済みの分岐が表示する
- kill "$pid" で通常の終了シグナルを送る。今回は -9 を使わない
- wait "$pid" 2>/dev/null で終了を待つ。最後の done の表示は用意済み