ぱんだツールズぱんだツールズ

技術背景

Linuxのexit codeとerrnoの読み方 — 137・ENOENT・EACCESの意味とデバッグ手順

約7分

「コンテナが exit code 137 で突然落ちた」「ENOENT が出ているのにファイルは確かに存在する」—— バックエンド開発やインフラ作業でこういったログに詰まった経験は誰にでもあるはずです。 exit code と errno は Linux がプロセスに渡す「なぜ失敗したか」の第一の手がかりです。 読み方を知っているだけで、デバッグにかかる時間が大きく変わります。 この記事では、exit code と errno の仕組みを体系的に整理し、頻出パターンの原因と対処法を解説します。

exit code とは

exit code(終了コード・終了ステータス)とは、プロセスが終了するときに親プロセス(シェルや OS)へ返す 0〜255 の整数値です。0 は正常終了、1 以上は何らかの異常を意味します。

シェルでは直前に実行したコマンドの exit code を $? で確認できます。

# exit code の確認方法

ls /存在しないパス

echo $? # → 2(ls の「No such file or directory」)

curl https://example.com -o /dev/null -s

echo $? # → 0(正常終了)

CI/CD パイプラインでは exit code が 0 以外になるとビルドが失敗扱いになります。 スクリプトの最後のコマンドが返す exit code がそのままスクリプト全体の exit code になるため、 意図しない「成功」を防ぐには set -eset -o pipefail を必ず設定してください。

重要な exit code 一覧

exit code には慣習的な意味があります。特に 1・2・126・127・130・137・139・141・143 は頻出です。

exit code意味主な原因Docker での発生ケース
0正常終了処理成功コンテナが正常終了
1一般的なエラーアプリ固有のエラー・スクリプト内の異常アプリのクラッシュ・設定ミス
2使い方の誤りコマンドの引数・オプションが不正ENTRYPOINT の引数ミス
126実行不可実行権限がない(パーミッション不足)entrypoint.sh に +x がない
127コマンドが見つからないPATH にコマンドが存在しないイメージにバイナリがない
130Ctrl+C による中断SIGINT(シグナル番号 2)を受信docker stop での割り込み
137SIGKILL による強制終了OOM Killer・メモリ超過・docker kill--memory 制限超過・OOM
139SIGSEGV(セグメンテーション違反)不正メモリアクセス・バッファオーバーフローC/C++ の null ポインタ参照等
141SIGPIPE(パイプ切断)パイプの読み取り側がすでに終了ログパイプ先の終了
143SIGTERM による正常終了docker stop・Kubernetes による pod 終了ヘルスチェック失敗・デプロイ時の入れ替え

128+N の法則 — シグナル番号との対応

シグナルによってプロセスが終了した場合の exit code は 128 + シグナル番号 になります。 たとえば SIGKILL のシグナル番号は 9 なので、128 + 9 = 137 です。

# シグナル番号の一覧を確認する

kill -l

# 出力例(抜粋)

1) SIGHUP 2) SIGINT 9) SIGKILL 11) SIGSEGV 13) SIGPIPE 15) SIGTERM

# 計算例

137 - 128 = 9 → SIGKILL

139 - 128 = 11 → SIGSEGV

143 - 128 = 15 → SIGTERM

ログに見慣れない exit code が出たときは、128 を引いてシグナル番号を求め、kill -l の出力と照合すればすぐに原因のシグナルを特定できます。 ぱんだツールズのエラーコード横断検索では、 exit code やシグナル名からまとめて意味を調べることができます。

errno とは — システムコールの失敗を伝える番号

errno(エラーナンバー)とは、システムコールが失敗したときにカーネルがセットするエラー番号です。 C 言語の errno.h ヘッダーで定義されており、 ENOENT・EACCES のようなシンボル名と対応する整数値(例: ENOENT = 2)を持ちます。

システムコール(open・read・write・connect など)が -1 を返したとき、 その理由を知るために errno を参照します。 C 言語では perror("操作名") を呼ぶとエラー番号と説明文を stderr に出力できます。

// C 言語でのエラー確認例

FILE *fp = fopen("/etc/shadow", "r");

if (fp == NULL) {

perror("fopen"); // → "fopen: Permission denied"

// errno は EACCES (13) にセットされている

}

Python・Go・Node.js などの高レベル言語でも、システムコール由来のエラーは内部で errno を受け取り、 ENOENT なら「ファイルが見つかりません」のメッセージに変換して例外やエラーオブジェクトとして アプリケーションに伝えています。

重要な errno 一覧

以下は実務で頻繁に遭遇する errno のシンボル名・番号・意味・典型的な発生ケースです。

シンボル名番号意味典型的な発生ケース
EPERM1Operation not permittedroot 権限が必要な操作・コンテナの Capability 不足
ENOENT2No such file or directoryファイル・ディレクトリが存在しない・パスの途中が欠け
EACCES13Permission deniedファイルの rwx パーミッションによる拒否
EBUSY16Device or resource busyマウント中のデバイスをアンマウント・使用中のファイル削除
EEXIST17File exists作成しようとしたファイル・ディレクトリが既に存在
EINVAL22Invalid argumentシステムコールに渡した引数が不正・フラグの組み合わせが無効
ENOMEM12Out of memorymalloc 失敗・カーネルのメモリ割り当て枯渇
ENOSPC28No space left on deviceディスク残量ゼロ・inode 枯渇
EPIPE32Broken pipeパイプの読み取り側が先に終了・TCP コネクション切断後の書き込み
EADDRINUSE98Address already in useポートが既に使用中(サーバー二重起動・TIME_WAIT 残留)
ECONNREFUSED111Connection refused接続先がリッスンしていない・ファイアウォールでポートが閉じている
ETIMEDOUT110Connection timed outネットワーク到達不能・ルーティングミス・SYN パケットのドロップ

デバッグ実践 — ログとコマンドの使い方

strace でシステムコールを追う

strace はプロセスが発行するシステムコールをリアルタイムで表示するツールです。 「なぜ ENOENT が出るのか」「どのファイルを開こうとしているか」を一発で確認できます。

# コマンドをトレース(失敗したシステムコールのみ表示)

strace -e trace=openat,connect,stat myapp 2>&1 | grep -v "= 0"

# 既存プロセスに attach(PID 1234 の場合)

strace -p 1234 -e trace=network

# ファイル操作をすべて記録してファイルに保存

strace -ff -o strace.log myapp

journalctl と dmesg でシステムログを確認

# システムログの末尾をリアルタイム追跡

journalctl -xe -f

# OOM Killer のログを確認(メモリ不足による強制終了)

dmesg | grep -i "oom\|killed process\|out of memory"

# 特定サービスのログ

journalctl -u nginx.service --since "10 minutes ago"

errno のシンボル名を調べる

# errno コマンド(moreutils パッケージ)で全一覧

errno -l

# 番号から名前を調べる

errno 2 # → ENOENT 2 No such file or directory

# Python でも確認できる

python3 -c "import errno; print(errno.ENOENT)" # → 2

エラー番号・シンボル名・HTTPステータスコードをまとめて調べたいときは、エラーコード横断検索が便利です。 キーワードで絞り込んで errno・exit code・HTTP ステータスを横断的に検索できます。 シェルコマンドの使い方に迷ったときはシェルコマンド横断検索も合わせてお使いください。

Docker コンテナの exit code 読み方

Docker コンテナが終了したときの exit code は docker inspect で確認できます。

# exit code を確認

docker inspect <コンテナID> --format="{{.State.ExitCode}}"

# 終了理由と OOM フラグも確認

docker inspect <コンテナID> --format="{{.State.OOMKilled}}"

# コンテナのログを確認

docker logs --tail 100 <コンテナID>

Kubernetes の場合

kubectl describe pod <pod名>Last State セクションに exit code と Reason: OOMKilled / Error / Completed が表示されます。 exit code 137 + OOMKilled: true なら、Pod の resources.limits.memory を増やすか、 アプリのメモリ使用量を削減してください。

まとめ

  • exit code は 0 が正常終了、1 以上が異常終了。シグナルによる終了は 128 + シグナル番号
  • 137(SIGKILL)は OOM が最多原因。dmesg | grep oom で確認
  • 143(SIGTERM)は docker stop や Kubernetes の正常な再起動によるもの。異常ではないケースも多い
  • errno はシステムコール失敗時にカーネルがセットするエラー番号。ENOENT・EACCES・ECONNREFUSED が特に頻出
  • ENOENT はファイル本体だけでなく、パス途中のディレクトリが欠けていても発生する
  • EACCES はパーミッション、EPERM は権限自体が存在しない(root 権限・Capability 不足)
  • strace でシステムコールを追跡すると、エラーの根本原因を最速で特定できる
  • Docker では docker inspect --format="{{.State.ExitCode}}" で exit code を確認する

よくある質問

exit code 137 の原因は何ですか?

exit code 137 は「128 + 9(SIGKILL)」を意味します。プロセスが OS から強制終了されたことを示します。最も多い原因はメモリ不足(OOM: Out of Memory)です。Linux カーネルの OOM Killer がメモリを圧迫しているプロセスを強制終了したとき、終了コードは 137 になります。Docker コンテナでは `--memory` フラグで割り当てメモリを制限しているとき、超過すると 137 で落ちます。`dmesg | grep -i oom` や `journalctl -ke` でカーネルの OOM ログを確認してください。

ENOENT が出ているのにファイルが存在する場合はなぜですか?

ENOENT(No such file or directory)はファイル本体だけでなく、パスの途中のディレクトリが存在しない場合にも発生します。たとえば `/app/logs/app.log` を開こうとしたとき、`/app/logs/` ディレクトリが存在しないだけで ENOENT になります。また、シンボリックリンクのリンク先が存在しない場合や、Docker コンテナ内でホスト側のパスをマウントし忘れた場合も同じエラーになります。`ls -la` でパスの各コンポーネントを順番に確認し、どの階層が欠けているかを特定してください。

EACCES と EPERM の違いは何ですか?

EACCES(Permission denied)はファイル・ディレクトリのパーミッション(rwx ビット)によるアクセス拒否です。`chmod` や `chown` で解決できることが多いです。EPERM(Operation not permitted)はパーミッションとは別に、OS レベルの操作権限がない場合に返ります。たとえば非 root ユーザーが 1023 番以下のポートをバインドしようとしたとき、`kill -9` で他ユーザーのプロセスを終了しようとしたとき、コンテナ内で `--cap-drop` により削除されたカーネル権限が必要な操作をしたときなどに発生します。EACCES は「誰のファイルか」、EPERM は「その操作自体が許可されていない」というイメージで区別すると覚えやすいです。

SIGKILL と SIGTERM の違いは何ですか?プロセスの終了に使い分けはありますか?

SIGTERM(シグナル番号 15)はプロセスに「終了してほしい」とお願いするシグナルです。プロセス側でシグナルハンドラを設定すれば、受け取ったあとに接続を切ったりファイルをクローズしてからグレースフルにシャットダウンできます。SIGKILL(シグナル番号 9)はカーネルが直接プロセスを強制終了するシグナルで、プロセス側はハンドリングできません。`docker stop` は最初に SIGTERM を送り、デフォルト 10 秒待ってもプロセスが終了しなければ SIGKILL に切り替えます。まず SIGTERM を試し、応答しない場合のみ SIGKILL を使うのがベストプラクティスです。

errno の値はどこで確認できますか?コマンドはありますか?

C 言語では `#include <errno.h>` でマクロが定義されており、`perror("操作名")` を呼ぶとエラー番号と説明文が stderr に出力されます。シェルから確認するには `errno -l`(moreutils パッケージ)コマンドで全一覧を表示できます。Python では `import errno; errno.ENOENT` で数値(2)を確認できます。Go では `syscall.ENOENT` として参照できます。また `man errno` や Linux カーネルの `/usr/include/asm-generic/errno-base.h` に定義が載っています。ぱんだツールズの<Link href="/tools/error-code-search" className="text-blue-600 hover:underline">エラーコード横断検索</Link>では、ENOENT・EACCES などのシンボル名や番号からすぐに意味と対処法を調べられます。

Docker コンテナが突然落ちる原因はどうやって調べればいいですか?

まず `docker inspect <コンテナID> --format="{{.State.ExitCode}}"` で exit code を確認します。137 なら OOM、139 ならセグメンテーション違反(SIGSEGV)、143 なら SIGTERM による正常終了(ヘルスチェック失敗や Kubernetes による再起動)が考えられます。次に `docker logs <コンテナID>` でアプリ側のログを確認し、ホスト側では `dmesg | grep -E "oom|kill"` でカーネルの OOM ログを確認してください。Kubernetes 環境では `kubectl describe pod <pod名>` の `Last State` に終了コードと理由が表示されます。

strace でシステムコールを追うにはどうすればいいですか?

`strace -e trace=open,read,write,connect <コマンド>` で特定のシステムコールだけを絞り込んで追跡できます。既に起動しているプロセスに attach する場合は `strace -p <PID>` を使います。失敗したシステムコールだけを確認したいときは `strace -e fault= <コマンド> 2>&1 | grep -v "= 0"` のように出力をフィルタリングすると効率的です。出力が多すぎる場合は `-o strace.log` でファイルに保存してから解析してください。なお macOS では strace は使えません。代わりに `dtruss`(要 SIP 無効)または `dtrace` を使います。

exit code 0 なのに処理が失敗しているように見えます。なぜですか?

exit code 0 はプロセス自体が「正常終了した」と報告しているだけで、ビジネスロジック上の成功を保証しません。たとえばスクリプト内でエラーが起きても最後のコマンドが成功すれば 0 を返すことがあります。シェルスクリプトでは `set -e`(エラーで即終了)と `set -o pipefail`(パイプ内のエラーも伝播)を冒頭に書くことで、意図しない exit code 0 を防げます。また一部のコマンドは仕様として常に 0 を返す場合があるため(`grep` はマッチなしで 1 を返す等)、コマンドのマニュアルで exit code の意味を確認することが重要です。

この記事で紹介したツール