「kill -9 で強制終了していいのか、毎回不安になる」
サーバー運用の現場では、応答しなくなったプロセスを安全に停止させる必要がある場面に必ず遭遇します。
この記事では、プロセスにシグナルを送る
kill(キル)コマンド の実践的な使い方を解説します。SIGTERM と SIGKILL の違いから、プロセス名で終了できる
killall / pkill、子プロセスごと止める setsid の併用、ゾンビプロセスへの対処、timeout コマンドと組み合わせた2段階の自動終了まで、現場で20年以上サーバーを運用してきた経験から、安全にプロセスを管理するためのノウハウをまとめました。この記事のポイント
・kill PID は SIGTERM(後始末あり)、kill -9 PID は SIGKILL(強制)
・停止の鉄則は「SIGTERM → 数秒待つ → 残っていたらSIGKILL」の2段階
・killall や pkill は一括終了できる分、実行前に pgrep で必ず対象確認
・子プロセスごと止めるには setsid、時間制限には timeout --kill-after が鉄板
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
killコマンドとは?(プロセスにシグナルを送るコマンド)
kill は、その名前から「プロセスを殺すコマンド」と思われがちですが、正確には プロセスに「シグナル」と呼ばれる通知を送るコマンド です。シグナルとは、Linuxカーネルがプロセスに対して「終了してください」「設定を再読み込みしてください」などの指示を伝える仕組みです。終了以外にもさまざまなシグナルがあり、用途に応じて使い分けます。
本記事の動作確認は RHEL 9.4 / Ubuntu 24.04 LTS で実施しています。
kill はシェル組み込みコマンド(bashビルトイン)と外部コマンド(/usr/bin/kill)の両方が存在しますが、基本的な使い方は同じです。終了したいプロセスのPIDを調べる
kill コマンドを実行するには、対象プロセスの PID(プロセスID)が必要です。まずはPIDの調べ方を確認しましょう。1. ps aux | grep で探す
最も基本的な方法です。ps aux で全プロセスを一覧表示し、grep で絞り込みます。# httpd のプロセスを探す(grep 自身を除外する) $ ps aux | grep httpd | grep -v grep root 1234 0.0 0.5 230000 5000 ? Ss 10:00 0:00 /usr/sbin/httpd -DFOREGROUND apache 1235 0.0 0.3 230000 3000 ? S 10:00 0:00 /usr/sbin/httpd -DFOREGROUND
1234 です。※
grep -v grep を付けないと、grep自身のプロセスもヒットしてしまうため、パイプ3段(ps aux | grep xxx | grep -v grep)が定番の書き方です。2. pgrep でPIDだけを取得する
pgrep を使えば、プロセス名からPIDだけをシンプルに取得できます。# httpd のPIDだけを表示する $ pgrep httpd 1234 1235 # プロセス名も一緒に表示する $ pgrep -a httpd 1234 /usr/sbin/httpd -DFOREGROUND 1235 /usr/sbin/httpd -DFOREGROUND
基本的な使い方(シグナルの種類と違い)
PIDが分かったら、kill コマンドでシグナルを送ります。シグナルの使い分けが非常に重要なので、しっかり理解してください。1. まずは kill(SIGTERM)で「お願い」する
シグナルを指定せずにkill を実行すると、デフォルトで SIGTERM(シグナル番号15)が送られます。これは「正常に終了してください」というお願いです。# PID 1234 のプロセスに終了をお願いする(SIGTERM) $ kill 1234 # 明示的にシグナル番号を指定する書き方(上と同じ意味) $ kill -15 1234
2. kill -9(SIGKILL)で「強制終了」する
SIGTERM を送っても終了しないプロセスには、SIGKILL(シグナル番号9)を使います。これはカーネルが直接プロセスを停止させるため、プロセス側で無視することができません。# PID 1234 のプロセスを強制終了する $ kill -9 1234 # シグナル名で指定する書き方 $ kill -SIGKILL 1234
timeout --kill-after でもそのまま応用できる鉄則です。3. kill -1(SIGHUP)で「設定を再読み込み」させる
SIGHUP(シグナル番号1)は、多くのデーモン(サーバープロセス)で「設定ファイルの再読み込み」として解釈されます。プロセスを停止せずに設定変更を反映したい時に使います。# Apache に設定ファイルを再読み込みさせる # kill -1 1234 # または # kill -HUP 1234
なお、systemd環境では
systemctl reload httpd のようにサービス単位で再読み込みする方が確実です。kill でシグナルを送るのは、systemctl が使えない場面やデーモン以外のプロセスが対象の場合に使ってください。以下が、覚えておくべき主要シグナルの一覧です。
・SIGTERM(15):正常終了を要求する。プロセスが後始末をしてから終了する(デフォルト)
・SIGKILL(9):強制終了。プロセスは無視できない。後始末は行われない
・SIGHUP(1):設定ファイルの再読み込み(対応しているデーモンのみ)
・SIGINT(2):割り込みによる終了。キーボードの Ctrl + C と同じ動作
・SIGSTOP(19):プロセスを一時停止する。SIGCONT(18)で再開できる
プロセス名で終了する(killall / pkill)
PIDをいちいち調べるのが面倒な場合、プロセス名を直接指定して終了させるコマンドがあります。1. killall でプロセス名を指定する
killall は、指定した名前に完全一致するすべてのプロセスにシグナルを送ります。# httpd という名前のプロセスをすべて終了する $ killall httpd # 強制終了する場合 $ killall -9 httpd
2. pkill で柔軟に検索して終了する
pkill は部分一致で検索できるため、killall よりも柔軟にプロセスを指定できます。# プロセス名に "http" を含むプロセスをすべて終了する $ pkill http # 特定のユーザーが実行しているプロセスだけを終了する $ pkill -u apache httpd
【重要】killall / pkill の危険性
killall や pkill は 条件に一致するすべてのプロセスにシグナルを送る ため、意図しないプロセスまで終了させてしまうリスクがあります。特に本番サーバーでは、まず
pgrep で対象のプロセスを確認してから実行するのが鉄則です。# まず対象を確認する(終了はしない) $ pgrep -a httpd 1234 /usr/sbin/httpd -DFOREGROUND 1235 /usr/sbin/httpd -DFOREGROUND # 確認できたら終了する $ pkill httpd
実務で使えるkillのTips
ゾンビプロセスが残った時の対処法
ps aux の STAT 列に Z と表示されるプロセスは「ゾンビプロセス」です。これはすでに実行が終了しているが、親プロセスが終了状態を回収していないプロセスです。ゾンビプロセス自体にはCPUやメモリを消費しないため実害は少ないですが、大量に溜まるとプロセステーブルを圧迫します。
ゾンビプロセスに
kill を送っても効果はありません。対処法は 親プロセスにSIGCHLDを送る か、親プロセスを終了させる ことです。# ゾンビプロセスを確認する $ ps aux | grep -w Z user 5678 0.0 0.0 0 0 ? Z 10:00 0:00 [defunct] # 親プロセスのPIDを調べる(PPIDが親のPID) $ ps -o pid,ppid,stat,cmd -p 5678 PID PPID STAT CMD 5678 4321 Z [defunct] # 親プロセスにSIGCHLDを送る $ kill -SIGCHLD 4321
kill -0 でプロセスの生存確認をする
シグナル番号0 は特殊なシグナルで、実際にはシグナルを送らず、プロセスが存在するかどうかだけを確認できます。シェルスクリプトでプロセスの生存チェックに使えます。# PID 1234 のプロセスが存在するか確認する $ kill -0 1234 && echo "プロセスは実行中" || echo "プロセスは存在しない"
子プロセスごとまとめて止める(setsid の併用)
シェルスクリプトやPerl/Pythonスクリプトなど、内部で fork して子プロセスを生み出すコマンドでは、親プロセスだけを kill しても子プロセスが生き残る「ゾンビ残存」が発生しがちです。この場合は
setsid で新しいセッションに切り離した状態で起動し、プロセスグループ全体にシグナルを送ると、子プロセスごと確実に止められます。# setsid で新規セッションとして起動する $ setsid ./long_batch.sh & [1] 8900 # プロセスグループ全体にSIGTERMを送る(PIDの前にマイナス記号) $ kill -TERM -8900 # 強制終了も同じ書き方 $ kill -9 -8900
-PID のようにマイナス記号を付けると、PIDをプロセスグループIDとして解釈し、同じグループの全プロセスに一斉にシグナルを送ります。fork型のスクリプトを止める時の定番テクニックです。timeoutコマンドと組み合わせて自動killする
cronで回しているバックアップが何時間も終わらない、APIを叩くスクリプトが応答なしのまま固まっている。こうした「いつ終わるか分からないコマンド」を放置すると、サーバー負荷・ジョブ多重起動・障害通知の遅れなど、致命的なトラブルに直結します。こうした場面では、GNU coreutils の
timeout コマンドと kill の思想を組み合わせるのが定石です。timeout は「指定時間を経過したら自動でシグナルを送る」という1点に特化したコマンドで、kill の2段階ロジック(SIGTERM → 猶予 → SIGKILL)をそのままワンコマンドで実現できます。なお、RHEL / Ubuntu などの Linux 環境では GNU coreutils に標準で含まれています。macOS 標準の BSD 系には
timeout が入っていないため、Homebrew で coreutils をインストールして gtimeout として使う必要があります。1. 秒数・分・時で自動kill する基本形
# 5秒経過したらSIGTERMを送って打ち切る $ timeout 5 sleep 30 $ echo $? 124 # 10分でrsyncを打ち切る $ timeout 10m rsync -avz /data/ backup01:/data/ # 2時間でバッチ処理を打ち切る $ timeout 2h /usr/local/bin/nightly_batch.sh # 30分でpg_dumpを打ち切る(DB側の負荷にも上限を設ける) $ timeout 30m pg_dump -U postgres mydb > /backup/mydb.sql
124 は timeout による強制終了を示します。この数字はシェルスクリプト側のエラーハンドリングで重要なので必ず覚えておいてください。時間の単位は s(秒)・m(分)・h(時)・d(日)が使え、小数指定も可能です(例:timeout 0.5 コマンド で500ミリ秒、timeout 1.5m コマンド で90秒)。2. --kill-afterで2段階停止(SIGTERM→SIGKILL)
ここが一番重要です。timeout は標準で SIGTERM を送りますが、プロセスが SIGTERM を無視すると止まりません。そこで--kill-after を付けて、まずは SIGTERM で優しく終了を促し、それでもダメなら問答無用で SIGKILL を叩き込む2段階構成にします。# 60秒でSIGTERM、そこから10秒経っても残っていたらSIGKILLで強制終了 $ timeout --kill-after=10s 60s ./stubborn_script.sh # SIGKILLで殺された場合の終了コード確認 $ timeout --kill-after=5s 3s sleep 30 $ echo $? 137 # 128 + シグナル番号9 = 137
137 は「SIGKILL(シグナル番号9)で殺された」を意味します(128 + 9 = 137)。143 は「SIGTERM(15)で終了した」です。私が現場で timeout を書く時は 必ず --kill-after を付けるようにしています。付け忘れて「timeoutを指定したのにジョブが止まらない」と悩む現場をよく見かけますが、8割はこれで解決します。3. --signalで送るシグナルを切り替える
--signal(-s)オプションで、最初に送るシグナルを変更できます。# いきなりSIGKILLで即時強制終了(クリーンアップは走らない) $ timeout -s KILL 10 ./broken_process # SIGINT(Ctrl+C相当)で止める(Pythonスクリプト等でKeyboardInterrupt扱いにしたい時) $ timeout --signal=INT 10 ./interactive_tool.sh # SIGHUPで止める(端末切断相当。デーモン系で使う時は要注意) $ timeout -s HUP 30 ./long_task.sh # 使用可能なシグナル一覧 $ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 9) SIGKILL 15) SIGTERM
--preserve-status を付けると、timeout 自身の終了コード 124 ではなく、対象コマンド本来の終了コードを保持してくれます。異常終了を上位のジョブ管理に正確に伝えたい時に使います。4. cron・シェルスクリプトに組み込む
cron は「前回のジョブがまだ動いていても、次回の時間になれば新しいジョブを起動する」性質があります。ハングしたジョブが溜まるとサーバーが窒息するので、上限時間を必ず決めるべきです。# crontab例: 毎時0分にバックアップ、55分で必ず打ち切る # cron実行時はPATHが短いので /usr/bin/timeout をフルパス指定 0 * * * * /usr/bin/timeout --kill-after=30s 55m /opt/bin/backup.sh >> /var/log/backup.log 2>&1
#!/bin/bash # set -o pipefail はパイプ経由での終了コード埋もれ対策 set -o pipefail timeout --kill-after=5s 30s curl -sSf https://api.example.com/health rc=$? if [ $rc -eq 124 ]; then echo "API応答がタイムアウトしました" | mail -s "API異常" admin@example.com exit 2 elif [ $rc -eq 137 ]; then echo "SIGKILLで強制終了されました(SIGTERMを無視)" exit 2 elif [ $rc -ne 0 ]; then echo "curlが異常終了(exit=$rc)" exit 1 fi echo "正常応答"
bash -c '...' で1コマンドにまとめてから timeout に渡します。timeout に直接パイプを渡すと、最初のコマンドにしか時間制限がかからない点に注意してください。もう1つ注意が必要な落とし穴があります。パイプ後続のコマンドが正常終了すると、timeout の終了コード 124 が上書きされてしまう問題です。
# NG: パイプ先のtee成功で終了コードが0になってしまう $ timeout 3 ./long.sh | tee /tmp/out.log $ echo $? 0 # OK: pipefailを有効化してから実行する $ set -o pipefail $ timeout 3 ./long.sh | tee /tmp/out.log $ echo $? 124
set -o pipefail を入れておくと、パイプのどこかで失敗した場合にスクリプト全体の終了コードへ反映されます。timeout を使うスクリプトでは必ずセットにしてください。本格的に運用を整えるなら、cron の代わりに systemd タイマーを使う選択肢もあります。その場合はサービスユニット側に
RuntimeMaxSec= を設定するか、ExecStart 内で timeout を噛ませるのが安全です。「kill しても終了しない」時の対処法
kill(SIGTERM)を送ってもプロセスが終了しない場合、いくつかの原因が考えられます。1. プロセスがSIGTERMをハンドリングしている
一部のプロセスは、SIGTERM を受け取った時に独自の処理を行い、すぐには終了しないことがあります。数秒から数十秒待ってもまだ終了しない場合は、kill -9(SIGKILL)を使ってください。# SIGTERM を送る $ kill 1234 # 5秒待ってもプロセスが残っている場合 $ sleep 5 && kill -0 1234 && kill -9 1234 # timeout を使えば2段階を1行で書ける(内部でkill相当の動きをする) $ timeout --kill-after=5s 5s bash -c 'while kill -0 1234 2>/dev/null; do sleep 1; done'
2. 子プロセスが親の影に隠れている
前述のとおり、シェルスクリプトやPythonスクリプトが fork で子を生み出している場合、親だけをkillしても子が残ります。pstree -p 親PID でプロセスツリーを確認し、setsid で起動してプロセスグループごと kill -TERM -PID で一斉に止めるのが確実です。timeout と組み合わせる場合は
timeout 60s setsid コマンド のように書くと、子プロセスごと一括で時間制限をかけられます。3. D状態(ディスクI/O待ち)のプロセス
ps aux の STAT 列が D(割り込み不可のスリープ状態)になっているプロセスは、ディスクI/Oの完了を待っている状態です。D状態のプロセスは SIGKILL でも終了できません。これはカーネルレベルでI/O完了を待っているためで、kill では対処できません。
NFSマウントのハングやディスク障害が原因のことが多いため、マウントポイントの状態やストレージを確認してください。
4. 権限が足りない(Operation not permitted)
一般ユーザーで他のユーザーが起動したプロセスを kill しようとすると「Operation not permitted」エラーになります。root ユーザーかsudo を使って実行してください。# 一般ユーザーで実行するとエラーになる場合 $ kill 1234 -bash: kill: (1234) - Operation not permitted # sudo を付けて実行する $ sudo kill 1234
5. timeout が cron 上で効かない(PATH が短い問題)
cron 環境は PATH が極端に短く、単にtimeout と書いただけでは「コマンドが見つからない」とエラーになることがあります。crontab 内では必ず /usr/bin/timeout のようにフルパスで記述してください。which timeout でパスを確認してから crontab に書くのが確実です。RHEL / Ubuntu どちらも通常 /usr/bin/timeout に置かれています。cron ジョブの実行ログは RHEL 系は /var/log/cron、Ubuntu 系は /var/log/syslog で確認できます。# timeout のパスを確認する $ which timeout /usr/bin/timeout # crontab 内ではフルパスで書く 0 * * * * /usr/bin/timeout --kill-after=30s 55m /opt/bin/backup.sh
本記事のまとめ(kill関連コマンド早見表)
| やりたいこと | コマンド |
|---|---|
| プロセスを正常終了させる(SIGTERM) | kill PID |
| プロセスを強制終了させる(SIGKILL) | kill -9 PID |
| 設定ファイルを再読み込みさせる(SIGHUP) | kill -1 PID |
| プロセスの生存を確認する | kill -0 PID |
| プロセス名でPIDを調べる | pgrep -a プロセス名 |
| プロセス名を指定して終了する(完全一致) | killall プロセス名 |
| プロセス名を指定して終了する(部分一致) | pkill プロセス名 |
| 子プロセスごと止める(プロセスグループ) | kill -TERM -PID |
| 指定時間で自動打ち切り(2段階停止) | timeout --kill-after=10s 60s コマンド |
| 最初からSIGKILLで強制終了(timeout版) | timeout -s KILL 30s コマンド |
| 子プロセスごと時間制限をかける | timeout 60s setsid コマンド |
| 終了コードを本来のものに保つ | timeout --preserve-status 30s コマンド |
| パイプ全体に時間制限をかける | timeout 10s bash -c 'cmd1 | cmd2' |
| 使用できるシグナル一覧を表示する | kill -l |
プロセス管理の基本をもっと体系的に学びたいですか?
killコマンドはプロセス管理の基本ですが、実務のサーバー運用では、プロセスの監視・トラブルシュート・安全な停止手順を体系的に身につけることが重要です。
ネットの切れ端の情報をコピペするだけでなく、現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら
- 次のページへ:dfコマンドでディスク容量を確認する方法|使用率の見方や容量不足の対処も
- 前のページへ:diffコマンドでファイルを比較する方法|unified形式やディレクトリ比較も
- この記事の属するカテゴリ:Linuxtipsへ戻る

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます