예외
프로그램 실행 도중 CPU가 감지하는 비정상적인 상황으로, 동기적으로 발생하는 소프트웨어 인터럽트의 한 종류입니다.
예외의 3가지 분류
| 분류 | 설명 | 복귀 지점 | 예시 |
| Fault | 복구 가능, 원인 해결 후 해당 명령어 재실행 | 오류 발생 명령어 | 페이지 폴트, 세그먼트 부재 |
| Trap | 정상 처리 후 다음 명령어로 진행 | 다음 명령어 | 디버그 브레이크포인트, 오버플로우 |
| Abort | 복구 불가능한 심각한 오류 | 복귀 불가 | 이중 폴트(Double Fault), 하드웨어 오류 |
예외 (Exception)
│
├── Fault ──── 복구 가능 → 같은 명령어 재실행
│ ex) 페이지 폴트: 디스크에서 페이지 로드 후 재실행
│
├── Trap ───── 정상 → 다음 명령어로 진행
│ ex) int 3 브레이크포인트: 디버거에 제어 넘긴 후 계속
│
└── Abort ──── 복구 불가 → 프로세스/시스템 종료
ex) Double Fault: 예외 처리 중 또 예외 발생
예외 vs 트랩 vs 하드웨어 인터럽트
| 항목 | 예외 | 트랩 (시스템 콜) | 하드웨어 인터럽트 |
| 발생 원인 | 프로그램 오류·비정상 상황 | 프로그래머의 의도적 요청 | 외부 장치 이벤트 |
| 동기/비동기 | 동기적 | 동기적 | 비동기적 |
| 의도성 | 비의도적 | 의도적 | 비의도적 |
| 예시 | 0으로 나누기, 페이지 폴트 | read(), fork() | 키보드, 타이머 |
x86 주요 예외 벡터
| 벡터 | 이름 | 분류 | 설명 |
| 0 | Divide Error | Fault | 0으로 나누기 |
| 1 | Debug | Trap/Fault | 디버그 예외 |
| 3 | Breakpoint | Trap | int 3 브레이크포인트 |
| 6 | Invalid Opcode | Fault | 잘못된 명령어 |
| 8 | Double Fault | Abort | 예외 처리 중 또 예외 |
| 11 | Segment Not Present | Fault | 세그먼트 미존재 |
| 12 | Stack-Segment Fault | Fault | 스택 세그먼트 오류 |
| 13 | General Protection Fault | Fault | 권한 위반, 잘못된 메모리 접근 |
| 14 | Page Fault | Fault | 페이지 미존재 |
페이지 폴트 처리 흐름 (Fault 예시)
① 프로그램이 가상 주소 접근
② MMU가 페이지 테이블 확인 → 물리 메모리에 없음
③ Page Fault 예외 발생 (벡터 14)
④ CPU → IDT[14] → Page Fault 핸들러 실행
⑤ 커널이 디스크에서 해당 페이지를 메모리에 로드
⑥ 페이지 테이블 갱신
⑦ 원래 명령어 재실행 (Fault이므로 같은 명령어로 복귀)
프로그램: arr[1000] 접근
│
▼
MMU: 페이지 없음! → Page Fault
│
▼
커널: 디스크 → 메모리 로드, 페이지 테이블 갱신
│
▼
프로그램: arr[1000] 재실행 → 이번엔 성공
Double Fault / Triple Fault (Abort 예시)
예외 발생 → 핸들러 실행 시도
│
├── 핸들러 정상 실행 → 복구 완료
│
└── 핸들러 실행 중 또 예외 발생
│
├── Double Fault (벡터 8) → 별도 핸들러로 처리 시도
│ │
│ └── 여기서도 예외 발생
│ │
│ └── Triple Fault → CPU 리셋 (재부팅)
│
└── 복구 불가 → 프로세스 또는 시스템 종료
Java 예외와 CPU 예외의 관계
Java의 예외(Exception)와 CPU 예외는 다른 개념이지만, 일부 Java 예외는 CPU 예외에서 비롯됩니다.
| CPU 예외 | Java 예외 | 설명 |
| Divide Error (벡터 0) | ArithmeticException | 0으로 나누기 |
| Page Fault (벡터 14) | - | JVM이 내부적으로 처리 (사용자에게 노출 안 됨) |
| General Protection Fault (벡터 13) | NullPointerException | null 참조 접근 시 JVM이 변환 |
| - | ArrayIndexOutOfBoundsException | JVM 자체 경계 검사 (CPU 예외 아님) |
| Stack-Segment Fault (벡터 12) | StackOverflowError | 스택 오버플로우 |
// CPU 예외 → JVM이 Java 예외로 변환
try {
int result = 10 / 0; // CPU: Divide Error (벡터 0)
} catch (ArithmeticException e) { // JVM이 ArithmeticException으로 변환
System.out.println("0으로 나눌 수 없습니다");
}
try {
Object obj = null;
obj.toString(); // CPU: GPF 또는 SIGSEGV
} catch (NullPointerException e) { // JVM이 NullPointerException으로 변환
System.out.println("null 참조");
}
// JVM 자체 검사 (CPU 예외가 아닌 경우)
try {
int[] arr = new int[3];
arr[10] = 5; // JVM이 배열 경계 직접 검사
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println("배열 범위 초과");
}
Java 예외 vs CPU 예외 처리 흐름 비교
[CPU 예외 처리]
오류 발생 → CPU가 감지 → IDT 조회 → 커널 핸들러 실행 → 시그널 전달
[Java 예외 처리 - CPU 예외 기반]
오류 발생 → CPU 예외 → 커널 → SIGSEGV/SIGFPE 시그널
→ JVM 시그널 핸들러 → Java Exception 객체 생성 → catch 블록
[Java 예외 처리 - JVM 자체 검사]
오류 조건 감지 → JVM이 직접 Exception 객체 생성 → catch 블록
(CPU 예외 없이 JVM 레벨에서 처리)