killコマンドとプロセス管理:ps, lsofを使った安全なプロセス終了の手順

実務レシピ

「本番サーバーでアプリがフリーズして応答しない」「ポートが競合してWebサーバーが起動できない」――インフラ運用やシステム開発の現場で、誰もが一度は直面するトラブルです。このとき、原因を調べずに焦って kill -9(強制終了)を連発してしまうと、データベースの不整合やトランザクションの中断、ロックファイルの残留など、より深刻な2次障害を引き起こす危険性があります。

Linuxにおけるプロセス制御は、単に強制停止させることではなく、「調査・特定(ps/lsof/pgrep)」「安全なシグナル送信(SIGTERM)」「一括終了(pkill/killall)」「常駐・サービス管理(nohup/systemctl/journalctl)」までの一連のフローを正しく理解し、段階的に対処することが鉄則です。

本記事では、当サイトの「プロセス管理・監視・強制終了」クラスターの総合ピラー(親記事)として、安全にプロセスを終了させるための完全手順、主要シグナルの使い分け、実務で役立つ自動停止スクリプト、そして停止しないプロセスのトラブルシューティングまでを体系的に解説します。

  1. 1. プロセスを特定・調査する(ps, lsof, pgrep, htop)
    1. ① ps コマンド:稼働プロセスと実行状態(STAT)の確認
    2. ② lsof コマンド:ポート番号や開いているファイルから逆引き
    3. ③ pgrep コマンド:シェルスクリプト連携に最適なPID抽出
  2. 2. kill コマンドの基本と主要シグナル一覧・使い分けフロー
    1. 実務で必須の主要シグナル早見表
    2. 安全なプロセス終了の使い分けフローチャート
    3. なぜ kill -9 の常用は危険なのか?
  3. 3. 実務で役立つ安全なプロセス終了スクリプト(コピペ実装例)
  4. 4. プロセス名・パターンでの一括終了(pkill, killall)
    1. ① pkill コマンド:正規表現・パターンマッチによる柔軟な終了
    2. ② killall コマンド:正確なプロセス名による一括終了
    3. pkill と killall の違い・使い分け
  5. 5. バックグラウンド実行とサービス管理(nohup, systemctl, journalctl)
    1. ① nohup コマンド:簡易バックグラウンド常駐
    2. ② systemctl コマンド:systemd による堅牢なサービス管理
    3. ③ journalctl コマンド:プロセスクラッシュやシグナルログの追跡
  6. 6. よくある落とし穴・トラブルシューティング
    1. ① ゾンビプロセス(STAT: Z)が kill -9 でも消えない
    2. ② D状態(Uninterruptible sleep)のプロセスが止まらない
    3. ③ スクリプト内でシグナルを安全に捕捉する(trap コマンド)
  7. まとめ:状況別プロセス管理コマンド早見表
  8. 関連記事

1. プロセスを特定・調査する(ps, lsof, pgrep, htop)

プロセスを終了させるための第1歩は、対象となるプロセスID(PID)や状態を正確に特定することです。誤ったPIDを指定してシステムに必要なデーモンを落としてしまわないよう、状況に応じたコマンドを使い分けましょう。

① ps コマンド:稼働プロセスと実行状態(STAT)の確認

もっとも標準的な調査方法は、ps コマンドと grep をパイプで繋ぐ手法です。実務では BSD形式の aux オプションが広く使われます。

# "nginx" を含むプロセスを一覧表示(grep自身のプロセスを除外する工夫)
ps aux | grep '[n]ginx'

# 特定ユーザー(例: www-data)のプロセスのみを確認
ps -u www-data -f

出力結果の主要カラムの意味は以下の通りです:

カラム名説明注目すべきポイント
USER実行ユーザーroot権限か一般ユーザー権限か(終了時に sudo が必要か判断)
PIDプロセスIDkill コマンドに渡す識別番号
%CPU / %MEMCPU・メモリ使用率暴走しているプロセスの特定
STATプロセスの状態R(実行中)、S(休眠中)、D(ディスクI/O待ち)、Z(ゾンビ)
COMMAND実行コマンド文字列引数や起動スクリプトのフルパス

② lsof コマンド:ポート番号や開いているファイルから逆引き

「ポート8080でWebサーバーを起動したいのに、すでに使われていて起動できない(Address already in use)」という場合は、lsof(List Open Files)コマンドでポートを占有しているPIDを特定します。

# TCPポート8080をリッスン(LISTEN)しているプロセスを特定
sudo lsof -i :8080

# 出力例:
# COMMAND   PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
# node    12345 www-data   23u  IPv6 123456      0t0  TCP *:8080 (LISTEN)

lsof がインストールされていない環境では、ss コマンド(sudo ss -lptn 'sport = :8080')でも同様の調査が可能です。

③ pgrep コマンド:シェルスクリプト連携に最適なPID抽出

プロセス名からPIDだけをクリーンに取得したい場合は、pgrep コマンドを活用します。ps aux | grep | awk のような複雑なパイプラインを組む必要がありません。

# "php-fpm" のPID一覧を改行区切りで取得
pgrep php-fpm

# プロセス名とPIDをセットで確認(-l オプション)
pgrep -l php-fpm

# コマンドライン全体の引数も含めて完全一致検索(-f オプション)
pgrep -f "python worker.py"

2. kill コマンドの基本と主要シグナル一覧・使い分けフロー

kill コマンドの本来の役割は、プロセスを「抹殺する」ことではなく、「プロセスに対してシグナル(非同期通知)を送信する」ことです。シグナルの種類によって、プロセス側で後処理を行うか、OSが問答無用でメモリを回収するかが決定されます。

# 基本構文
kill [シグナル指定] [PID...]

実務で必須の主要シグナル早見表

Linuxには64種類以上のシグナルが存在しますが、システム管理の実務で扱う重要なシグナルは以下の7種類です:

シグナル名番号トラップ可否役割と動作実務でのユースケース
SIGHUP1ハングアップ(端末切断)Nginx/Apache等の設定ファイル再読み込み(リロード)
SIGINT2割り込み(Interrupt)キーボードの Ctrl + C による処理中断
SIGQUIT3終了+コアダンプCtrl + \ によるデバッグ用クラッシュダンプ出力
SIGKILL9不可(拒否不能)強制終了最終手段。OSが即時消滅させる(後処理不可)
SIGUSR1 / 210 / 12ユーザー定義シグナルアプリケーション独自のログ再オープン・状態ダンプ
SIGTERM15通常終了要求(デフォルト)標準の終了手順。安全にクリーンアップして終了
SIGSTOP / CONT19 / 18不可 / 可一時停止 / 再開CPU負荷軽減のためのプロセス一時停止と再開制御

安全なプロセス終了の使い分けフローチャート

実務では、いきなり kill -9 を打つのではなく、以下の4段階フローに従って安全に終了させます:

  1. 【ステップ1】SIGTERM(15)の送信
    kill <PID> または kill -15 <PID> を実行。プロセスに「後処理をして終了してほしい」と通知します。DBコミットやテンポラリファイルの削除が行われます。
  2. 【ステップ2】プロセスの生存確認(ポーリング待機)
    数秒〜数十秒待ち、ps -p <PID> でプロセスが正常消滅したか確認します。
  3. 【ステップ3】SIGINT / SIGHUP の試行(任意)
    対話型CLIツールやバッチ処理の場合、kill -2 <PID> で安全な中断フックを呼び出せる場合があります。
  4. 【ステップ4】SIGKILL(9)の行使(最終手段)
    一定時間(例: 30秒以上)待っても全く応答がない完全フリーズ時のみ、kill -9 <PID> でOS強制停止を行います。

なぜ kill -9 の常用は危険なのか?

SIGKILL (9) は、プロセス自身にシグナルが届くのではなく、Linuxカーネルが直接プロセスのメモリ空間を強制解放・破棄します。そのため、以下の深刻なリスクが発生します:

  • 書き込み中バッファの消失:メモリ上の未保存データがディスクにフラッシュされず破棄され、DBやファイルが破損する。
  • ロックファイル・ソケットの残留/var/run/*.pid や共有メモリのロックが残り、次回以降のサービス再起動に失敗する。
  • 子プロセスの孤児化:親プロセスが後処理を行えないため、子プロセスがゾンビ化またはinit/systemd(PID 1)へ養子縁組され残存する。

3. 実務で役立つ安全なプロセス終了スクリプト(コピペ実装例)

実務の運用自動化やデプロイスクリプトでは、「まずSIGTERMを送り、一定時間待機しても停止しなければSIGKILLへフォールバックする」というグレースフルシャットダウン処理を実装するのが鉄則です。

#!/bin/bash
# --------------------------------------------------
# 安全なグレースフル・プロセス終了スクリプト
# --------------------------------------------------
set -euo pipefail

target_pid="${1:-}"

if [[ -z "$target_pid" ]]; then
  echo "使用方法: $0 <PID>" >&2
  exit 1
fi

# プロセスの存在確認
if ! kill -0 "$target_pid" 2>/dev/null; then
  echo "プロセス (PID: $target_pid) は存在しません。"
  exit 0
fi

echo "PID $target_pid に SIGTERM (15) を送信して安全な終了を試みます..."
kill -15 "$target_pid"

# 最大待機秒数(タイムアウト)
timeout_sec=10
elapsed=0

while kill -0 "$target_pid" 2>/dev/null; do
  if (( elapsed >= timeout_sec )); then
    echo "警告: ${timeout_sec}秒経過しても終了しないため、SIGKILL (9) で強制終了します。" >&2
    kill -9 "$target_pid"
    break
  fi
  sleep 1
  (( elapsed++ ))
  echo "終了待機中... (${elapsed}/${timeout_sec}s)"
done

echo "プロセス (PID: $target_pid) の停止を確認しました。"

スクリプト内の kill -0 "$target_pid" は、シグナルを実際には送らずに「プロセスが存在し、シグナル送信権限があるか」だけをチェックする実務の定石テクニックです。

4. プロセス名・パターンでの一括終了(pkill, killall)

Webサーバーやワーカープロセス(PHP-FPM、Nginx、Gunicornなど)のように複数の子プロセスが存在する場合、PIDを1つずつ手動で調べるのは非効率です。プロセス名でまとめて制御できる pkillkillall を活用しましょう。

① pkill コマンド:正規表現・パターンマッチによる柔軟な終了

pkill は、pgrep と同様の柔軟なパターンマッチングを使ってプロセスにシグナルを送ります。

# "php-fpm" を含むすべてのプロセスに SIGTERM を送信
pkill php-fpm

# 完全一致するプロセス名のみを対象(-x オプション)
pkill -x nginx

# 特定のユーザー(www-data)が実行している node プロセスのみを停止
pkill -u www-data node

# スクリプト名や引数を含むコマンドライン全体にマッチ(-f オプション)
pkill -f "python batch_job.py"

② killall コマンド:正確なプロセス名による一括終了

killall は、指定したプロセス名に完全一致するプロセスのみを対象とします。部分一致による誤爆を防ぎたい場合に安全です。

# すべての httpd プロセスを安全に終了
killall httpd

# シグナルを指定して設定ファイルをリロード(SIGHUP)
killall -HUP nginx

# 指定時間(例: 2時間以上)経過した古いプロセスのみ終了(-o オプション)
killall -o 2h worker_process

pkill と killall の違い・使い分け

比較項目pkillkillall
マッチ方式部分一致 / 正規表現(-fで引数マッチ可能)デフォルトで完全一致
ユーザー指定-u username で指定可能-u username で指定可能
主な用途引数を含むバッチスクリプトの一括停止デーモン(Nginx, Apache等)の確実な全停止

5. バックグラウンド実行とサービス管理(nohup, systemctl, journalctl)

長時間のバッチ処理やWebサービスを運用する場合、SSHセッションが切れただけでプロセスが終了してしまっては困ります。プロセスの常駐化と、障害時のログ追跡の仕組みを整えておくことが重要です。

① nohup コマンド:簡易バックグラウンド常駐

SSH端末をログアウト(SIGHUP受信)してもプロセスが途中で終了しないようにするには、nohup& を組み合わせます。

# 標準出力・標準エラー出力をログファイルへリダイレクトしてバックグラウンド実行
nohup python long_task.py > task.log 2>&1 &

# 直近にバックグラウンド実行したPIDの確認
echo "実行PID: $!"

停止したい場合は、記録したPIDに対して kill -15 <PID> を送信します。

② systemctl コマンド:systemd による堅牢なサービス管理

本番サーバーの常駐デーモンは、単体の nohup ではなく systemdsystemctl)経由で管理するのが標準です。自動起動や異常終了時の自動リカバリ(Restart=always)、安全な終了タイムアウト(TimeoutStopSec)が設定されています。

# サービスの状態確認
systemctl status nginx

# サービスの安全な停止
sudo systemctl stop nginx

# 設定ファイルのリロード(SIGHUP送信に相当)
sudo systemctl reload nginx

# サービスの自動起動有効化
sudo systemctl enable nginx

③ journalctl コマンド:プロセスクラッシュやシグナルログの追跡

プロセスが予期せず終了した場合や、SIGKILL を受けて死んだ原因を調査するには、journalctl でシステムログを確認します。

# 特定サービスのログをリアルタイムで追跡(-f)
journalctl -u nginx -f

# 本日発生したエラーログのみを抽出(-p err)
journalctl -u my_service.service -p err --since today

# LinuxカーネルのOOM Killer(メモリ枯渇による強制終了)ログを確認
sudo journalctl -k | grep -i "out of memory"

6. よくある落とし穴・トラブルシューティング

① ゾンビプロセス(STAT: Z)が kill -9 でも消えない

ps auxSTATZ または <defunct> と表示されるプロセスは「ゾンビプロセス」です。すでに処理は完了してメモリも解放されていますが、親プロセスが終了ステータス(wait システムコール)を受け取っていないため、プロセステーブルにのみ情報が残っています。

  • 対処法:ゾンビ自身はすでに死んでいるため kill -9 は効きません。ゾンビの親プロセス(PPID)を特定(ps -o ppid= -p <ZOMBIE_PID>)し、親プロセスに SIGCHLD を送るか、親プロセスを再起動・終了させることで解消します。

② D状態(Uninterruptible sleep)のプロセスが止まらない

STATD となっているプロセスは、NFSやディスクI/O、ハードウェア応答待ちの「割り込み不可スリープ」状態です。カーネル空間のI/O処理完了を待っているため、kill -9 を含めいかなるシグナルも受け付けません。

  • 対処法:マウント先のストレージ障害やネットワーク遅延を解消するか、最終的にはサーバーの再起動(reboot)が必要になります。

③ スクリプト内でシグナルを安全に捕捉する(trap コマンド)

自作のBashスクリプトが SIGTERMSIGINT で中断された際、テンポラリファイルやロックディレクトリを確実に削除するには trap コマンドを実装します。

#!/bin/bash
# 一時ディレクトリの作成
tmp_dir=$(mktemp -d)

# スクリプト終了時(EXIT)およびシグナル受信時(INT, TERM)に自動クリーンアップ
cleanup() {
  echo "終了処理中: 一時ファイル $tmp_dir を削除します..."
  rm -rf "$tmp_dir"
}
trap cleanup EXIT INT TERM

# メイン処理
echo "バッチ処理実行中..."
sleep 100

まとめ:状況別プロセス管理コマンド早見表

システム運用・障害対応で迷ったときは、以下の状況別マトリクスを参考に最適なコマンドを選択してください。

実現したい目的実行コマンド例備考・注意点
特定のPIDを安全に終了kill <PID>(SIGTERM)まずは標準の15で後処理を待つ
完全フリーズ時の強制停止kill -9 <PID>(SIGKILL)最終手段。データ破損リスクを認識して実行
ポート競合プロセスの特定sudo lsof -i :8080PIDを確認して個別に終了要求
複数ワーカーの一括終了pkill <name> / killall <name>誤爆防止に -x-u オプションを活用
設定ファイルの無停止リロードkill -1 <PID> / systemctl reloadSIGHUPに対応したデーモンで有効
ログアウト後も常駐実行nohup <cmd> > app.log 2>&1 &簡易バッチ向け。本番サービスは systemd を推奨
サービス全体の管理・状態監視systemctl status / stop <svc>ログ確認は journalctl -u <svc> -f を併用

プロセス管理は、Linuxエンジニアとしての基本でありながら、システムの信頼性を左右する最重要オペレーションです。「①特定 → ②SIGTERMによる安全停止 → ③ログ確認と必要に応じたSIGKILL」という3原則を意識して、日々の運用業務を安全かつ効率的に進めていきましょう!

Bash玄

はじめまして!Bash玄です。

エンジニアとしてシステム運用に携わる中で、手作業の多さに限界を感じ、Bashスクリプトを活用して業務を効率化したのがきっかけで、この道に入りました。「手作業は負け」「スクリプトはシンプルに」をモットーに、誰でも実践できるBashスクリプトの書き方を発信しています。

このサイトでは、Bashの基礎から実践的なスクリプト作成まで、初心者でもわかりやすく解説しています。少しでも「Bashって便利だな」と思ってもらえたら嬉しいです!

# 好きなこと
- シンプルなコードを書くこと
- コマンドラインを快適にカスタマイズすること
- 自動化で時間を生み出すこと

# このサイトを読んでほしい人
- Bashに興味があるけど、何から始めればいいかわからない人
- 定型業務を自動化したい人
- 効率よくターミナルを使いこなしたい人

Bashの世界に一歩踏み出して、一緒に「Bash道」を極めていきましょう!

Bash玄をフォローする