TL

例外・トラップ・フォルト・アボートの分類と処理

例外のどれが命令をやり直せてどれが致命的かを原理から判別できます。フォルト・トラップ・アボートの違いと、IDTディスパッチ・ダブルフォルトまで押さえます。

応用例外割り込みIDTページフォルトダブルフォルトOS最終更新: 2026-06-21
TL;DR要点だけ先に
  • 1.CPUの異常事象は、命令実行が原因の同期例外と、外部デバイス由来で命令とは無関係に届く非同期割り込みに大別される。返り先アドレスの意味が両者で根本的に異なる。
  • 2.同期例外はさらにフォルト(原因を直せば同一命令を再実行できる)、トラップ(次の命令から続行)、アボート(命令位置が不確定で復帰不能)に分かれる。ページフォルトがフォルト、INT3がトラップの代表。
  • 3.x86ではIDTが256ベクタを命令位置からハンドラへ写像する。例外処理中にさらに例外が起きるとダブルフォルト、その処理も失敗するとトリプルフォルトでCPUがリセットされる。

異常事象を二軸で分類する

CPU が通常の命令ストリームを中断して特別な処理へ飛ぶ事象は、まず二つの軸で整理すると混乱しません。第一の軸は 発生源が命令実行に同期しているか です。CPU が今まさに実行している命令そのものが原因で起きるものを 同期例外(exception)、外部デバイスのように命令とは無関係なタイミングで届くものを 非同期割り込み(interrupt) と呼びます。第二の軸は、同期例外の中で どこへ復帰するか、すなわち再実行できるのか・続行するのか・そもそも復帰できないのかという区別です。

この二軸を取り違えると、ハンドラ設計の前提が崩れます。たとえば「ページフォルトは同じ命令をやり直せる」という性質は、同期例外のうちフォルトに限った話で、非同期割り込みやアボートには当てはまりません。

CPU が命令ストリームを中断する事象
├─ 同期例外(exception)  原因=実行中の命令
│   ├─ フォルト(fault)   命令の前に復帰 → 同一命令を再実行
│   ├─ トラップ(trap)    命令の後に復帰 → 次の命令から続行
│   └─ アボート(abort)   復帰位置が不確定 → 通常は復帰不能
└─ 非同期割り込み(interrupt)  原因=外部デバイス等、命令と無関係

非同期割り込みの配送ハードウェアそのものは /os/interrupt-controller-apic/ で扱いました。本稿は同期例外の三分類と、x86 がそれらを統一的にディスパッチする仕組みを原理から掘り下げます。

返り先アドレスがすべてを決める

同期例外の三分類を分けるのは、ハードウェアが スタックに積む「戻り先(saved instruction pointer)」が原因命令を指すか、その次の命令を指すか です。x86 では例外発生時に CPU が CS:RIP などをスタックに退避しますが、その RIP がフォルトとトラップで意味が違います。

分類退避されるRIP復帰時の挙動代表例
フォルト(fault)原因命令の先頭原因を解消して同一命令を再実行ページフォルト、#GP、#UD、#NM
トラップ(trap)原因命令の次そのまま次の命令から続行INT3(#BP)、シングルステップ(#DB)、INTO
アボート(abort)不確定復帰せず、プロセスやシステムを終了#DF(ダブルフォルト)、#MC(マシンチェック)

フォルト は「この命令はまだ完了していない。前提条件が整えば実行できる」という性質を持ちます。だから戻り先は原因命令の 先頭 で、ハンドラが状態を整えてから iret すると、CPU は 同じ命令をもう一度 実行します。

トラップ は「命令は完了した。ただし通知したいことがある」という性質です。デバッガのブレークポイント命令 INT3 がこの典型で、その命令の効果(ブレーク通知)は済んでいるので、戻り先は 次の命令 です。トラップでフォルトのように同一命令へ戻すと、同じトラップが無限に再発します。

アボート は深刻なエラーで、CPU は どの命令まで進んだかを正確に報告できない(戻り先が信用できない)状態です。ハードウェア故障を示すマシンチェック例外(#MC)や、例外処理自体の失敗を示すダブルフォルト(#DF)がこれにあたり、原則として通常実行へ復帰できません。

同じ#DBでもフォルトとトラップを兼ねる

x86 のデバッグ例外 #DB は、原因によってフォルトにもトラップにもなります。命令フェッチに張ったハードウェアブレークポイント(実行ブレーク)は命令実行前に発火するフォルト型、シングルステップやデータ書き込みブレークは命令完了後に発火するトラップ型です。つまり分類はベクタ番号ではなく「いつ発火するか」で決まる、という原理を押さえると例外を機械的に暗記せずに済みます。

なぜフォルトは命令を再実行できるのか

フォルトの「同一命令の再実行」は、デマンドページングや CoW といった OS の遅延戦略すべての土台です。これが成立する条件は、例外を上げる時点で命令がまだ観測可能な副作用を残していないこと、つまり命令が 再実行可能(restartable)/アトミック であることです。

CPU は命令を実行する際、メモリアクセスのアドレス変換やアクセス権チェックを 副作用を確定させる前 に行います。ページフォルトはこのチェック段階で上がるため、レジスタもメモリも書き換わっていません。ハンドラが不在ページを埋めて iret すれば、CPU は何事もなかったかのように同じ命令を最初から実行できます。ページフォルトハンドラの分岐内容は /os/demand-paging-fault/ を参照してください。

mov rax, [rsi]   ; この1命令の内部で…
  1. アドレス変換  rsi の指す仮想アドレスを物理へ
  2. 権限チェック  読み取り可能か
       ↑ ここで PTE が無効 → ページフォルト発火(rax はまだ未更新)
  3. ロード実行    (フォルトしなければ)rax に値を書く
→ ハンドラがページを用意し iret → 同じ mov を 1 からやり直し → 今度は成功

ただし、複数のメモリに触れる命令(文字列命令 rep movs や、ページ境界をまたぐアクセス)では、途中までの進捗をどう扱うかが問題になります。x86 はこの種の命令について、再開に必要な進捗をレジスタ(rsi/rdi/rcx など)に残すか、命令を再実行しても結果が変わらないよう設計することで再実行可能性を保っています。これが崩れる命令はフォルトを起こせず、アーキテクチャ設計上の制約になります。

トラップを再実行すると無限ループ

分類を誤ってトラップを「フォルトのように」同一命令へ戻すと、その命令が再びトラップを上げ、永久に進みません。逆にフォルトを「トラップのように」次の命令へ飛ばすと、原因を解決しないまま原因命令をスキップしてしまい、本来あるべきロードやストアが実行されずデータが壊れます。ハンドラが復帰時に RIP をどう扱うかは、例外の分類と一対一で対応していなければなりません。

IDT によるディスパッチ:ベクタからハンドラへ

x86 は同期例外も非同期割り込みも、ベクタ番号(0〜255) という共通の通貨に変換し、IDT(Interrupt Descriptor Table) という 256 エントリの表でハンドラへ写像します。ベクタの割り当ては固定で、低位 0〜31 が CPU 予約の例外、32 以降が外部割り込みやソフトウェア割り込み用です。

ベクタ略号種別意味
0#DEフォルトゼロ除算など整数除算エラー
3#BPトラップブレークポイント(INT3)
6#UDフォルト未定義命令(無効オペコード)
13#GPフォルト一般保護例外(権限・セグメント違反)
14#PFフォルトページフォルト(原因アドレスはCR2)
8#DFアボートダブルフォルト(例外処理中の例外)
18#MCアボートマシンチェック(ハードウェア異常)

例外が発生すると CPU はベクタ番号で IDT を引き、エントリ(ゲートディスクリプタ)からハンドラのアドレスと、実行時の特権レベルや IST(割り込みスタックテーブル)の指定を読み出します。割り込みゲート(interrupt gate)とトラップゲート(trap gate)の違い はここで効きます。割り込みゲート経由で入ると CPU は自動で IF(割り込み許可フラグ)をクリアし、ハンドラ実行中の多重割り込みを抑止します。トラップゲートでは IF を変えず、ネストした割り込みを許します。

ハードウェアは例外に応じて エラーコード もスタックに積みます。#PF なら読み/書き・ユーザ/カーネル・存在/権限の別、#GP なら違反したセグメントセレクタなどがエンコードされ、ハンドラはこれを使って原因を細分化します。なお x86 では、原因命令のアドレスは退避された RIP、フォルトしたデータアドレスは専用レジスタ(#PF では CR2)と、「どの命令か」と「どのアドレスか」を別経路で渡す 点が設計の要です。

例外発生
 → CPU: ベクタ番号を決定(#PF なら 14)
 → IDT[14] を参照(ゲートディスクリプタ)
 → 必要なら特権レベル遷移+スタック切替(IST/TSS)
 → CS:RIP, RFLAGS, (あれば)エラーコードをスタックに退避
 → ハンドラへジャンプ(割り込みゲートなら IF=0)
 …ハンドラ処理…
 → iret で退避状態を復元(RIP の意味は分類依存)

カーネルモードへの遷移という観点では、システムコールの syscall 命令も「意図的に起こすトラップ的な制御移行」の一種です。両者の特権遷移の共通点は /os/system-call/ と合わせて読むと立体的に理解できます。

ダブルフォルトとトリプルフォルト

例外ハンドラへ入る処理そのものが失敗するとどうなるか。たとえば #PF が起きて IDT[14] のハンドラへ飛ぼうとしたが、そのハンドラのコードページや退避先スタックが不在・アクセス不能 だった、という状況です。最初の例外を配送している最中に第二の例外が発生したこのケースを、CPU は ダブルフォルト(#DF, ベクタ 8) として一本化します。

ダブルフォルトはアボートに分類され、エラーコードは常に 0、原因命令の特定もできません。重要なのは、ダブルフォルトハンドラを 確実に動かせるよう隔離する 設計です。x86-64 では IST(Interrupt Stack Table) を使い、#DF 専用の既知の正常なスタックへ無条件に切り替えられます。通常スタックが壊れていても #DF ハンドラだけは健全なスタックで走れるため、ここでパニック表示やダンプ採取という最後の仕事ができます。

そして、ダブルフォルトの配送中にさらに例外が起きる と、CPU は手を尽くした上での復旧不能と判断し、トリプルフォルト に至ります。これには対応するベクタもハンドラもありません。CPU は処理を諦めて ハードウェアリセット(多くの環境では即時リブート)を行います。

通常の例外      : 命令 → 例外 → ハンドラ正常起動 → iret
ダブルフォルト   : 例外 → ハンドラ起動に失敗(第二の例外)→ #DF(IST スタックで処理)
トリプルフォルト : #DF の処理中にさらに例外 → 復旧不能 → CPU リセット
トリプルフォルトは仮想化のリセット手段にもなる

トリプルフォルトは障害であると同時に、設計上「CPU を確実にリセットする」唯一確実な手段でもあります。初期の OS が x86 を 16 ビットから戻すためや、仮想マシンモニタがゲストのリセットを検知する契機として、あえてトリプルフォルトを利用することがあります。実機でブートローダや初期化コードのバグで「原因不明の瞬間リブート」が起きるとき、その正体はたいていトリプルフォルトです。IDT や GDT が未設定/破損していると最初の例外すら配送できず、容易にここへ転げ落ちます。

例外からシグナルへ:ユーザ空間への橋渡し

正当でない同期例外は、最終的にユーザプロセスへ届けられます。カーネルのハンドラは例外を解決できないと判断すると、対応する シグナル に変換してプロセスへ送ります。この対応関係を押さえると、ユーザ空間で観測する症状から CPU レベルの原因を逆引きできます。

CPU例外典型シグナルユーザ空間での意味
#PF(解決不能)SIGSEGV不正なアドレスアクセス、権限違反
#GPSIGSEGV / SIGBUS保護違反、アラインメント違反等
#DE / #MFSIGFPEゼロ除算・浮動小数点例外
#UDSIGILL未定義命令の実行
#BP / #DBSIGTRAPブレークポイント・シングルステップ

つまり、SIGSEGV を受け取ったプロセスは、その裏で CPU が #PF または #GP を上げ、カーネルがそれを「VMA に属さない正当化できないアクセス」と判定した結果を見ているわけです。デバッガが SIGTRAP を捕まえてブレークを実現できるのも、トラップ型例外 #BP がシグナルへ橋渡しされているからです。シグナル配送の仕組みそのものは /os/signals/ を参照してください。

試験・面接で問われる勘どころ

(1)同期例外(命令が原因)と非同期割り込み(命令と無関係)の区別、そして退避RIPの意味の違い。(2)フォルト=原因命令を再実行・トラップ=次の命令から続行・アボート=復帰不能、という三分類とその代表例(#PF/#BP/#DF)。(3)フォルトが再実行可能なのは命令が副作用を残す前に例外を上げるから。(4)IDTがベクタからハンドラへ写像し、ダブルフォルトはISTで隔離、トリプルフォルトはCPUリセット。この4点が頻出です。

まとめ

  • 異常事象は、命令が原因の 同期例外 と、外部由来の 非同期割り込み に大別される。両者で退避される戻り先アドレスの意味が根本的に異なる。
  • 同期例外は フォルト(原因命令を再実行)/トラップ(次命令から続行)/アボート(復帰不能) に三分される。分類はベクタ番号ではなく「いつ発火し、どこへ戻るか」で決まる。
  • フォルトが再実行可能 なのは、CPU が副作用を確定させる前に権限・変換チェックで例外を上げ、レジスタもメモリも未更新だから。これがデマンドページングや CoW の土台になる。
  • IDT はベクタ番号 0〜255 をハンドラへ写像する。例外処理中の例外は ダブルフォルト(#DF) に一本化され IST で隔離、その処理も失敗すると トリプルフォルト で CPU がリセットされる。
  • 解決できない同期例外は シグナル(#PF→SIGSEGV、#DE→SIGFPE、#BP→SIGTRAP 等)に変換され、ユーザ空間の症状として観測される。

配送ハードウェアは /os/interrupt-controller-apic/、フォルト処理の中身は /os/demand-paging-fault/、特権遷移の対は /os/system-call/、ユーザ空間への橋渡しは /os/signals/ を合わせて読むと、例外機構の全体像が繋がります。

OS Article

例外・トラップ・フォルト・アボートの分類と処理を実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

例外

比較で見る軸

難易度: advanced / カテゴリ: OS / タグ数: 6

導入後に効く点

同期例外はさらにフォルト(原因を直せば同一命令を再実行できる)、トラップ(次の命令から続行)、アボート(命令位置が不確定で復帰不能)に分かれる。ページフォルトがフォルト、INT3がトラップの代表。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
advanced
カテゴリ
OS
タグ数
6

判断チェックリスト

  • 自社の用途が「例外 / 割り込み」に近いか確認する。
  • 強みである「CPUの異常事象は、命令実行が原因の同期例外と、外部デバイス由来で命令とは無関係に届く非同期割り込みに大別される。返り先アドレスの意味が両者で根本的に異なる。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

例外割り込みIDTページフォルトダブルフォルト例外割り込みIDT
参考: 公式情報