Exceptional Control Flow (ECF)는 프로그램의 일반적인 순차 흐름을 예외적으로 변경하는 메커니즘으로, 운영체제의 핵심 기능인 입출력, 프로세스, 가상 메모리 등을 구현하는 데 사용된다. 프로그래머에게 중요한 이유는 다음과 같다:
- 시스템 개념 이해에 필수적: 프로세스 생성, 종료, 신호 처리 등은 모두 ECF를 기반으로 구현된다.
- 운영체제와의 상호작용 이해: 응용 프로그램이 시스템 콜을 통해 OS 서비스를 요청할 때 trap이라는 형태의 ECF를 사용한다.
- 응용 프로그램 작성 능력 향상: 예를 들어 유닉스 셸이나 웹 서버 같은 프로그램은 ECF 메커니즘을 적극적으로 활용한다.
- 동시성(concurrency) 이해의 기초: 인터럽트, 프로세스, 스레드, 시그널 핸들러 등의 개념은 모두 ECF의 예다.
- 소프트웨어 예외 처리의 이해: C++, Java 등의 언어에서 사용하는 예외 처리(try, catch, throw)는 낮은 수준의 ECF(setjmp, longjmp 등)를 기반으로 구현된다.
8.1 : Exceptions
Exception(예외)은 하드웨어와 운영체제가 공동으로 처리하는 ECF의 한 형태로, 프로세서 상태의 변화에 따라 발생하는 갑작스러운 제어 흐름 변경이다. 예외가 발생하면 exception handler라는 특수 서브루틴으로 흐름이 전환된다.
8.1.1: Exception Handling (예외 처리)
예외는 일반적으로 현재 실행 중인 명령어(Icurr)의 실행 중 발생한 이벤트로 인해 트리거된다. 예외가 발생하면 다음과 같은 과정이 이루어진다:
- 현재 프로세서 상태 저장:
- 현재 프로그램 카운터(PC, 즉 다음에 실행할 명령어의 주소), 레지스터 등 시스템 상태를 저장.
- 이는 이후 예외 처리 루틴을 마친 뒤 원래 흐름으로 되돌아가기 위해 필수.
- 핸들러 주소 결정 및 점프:
- 시스템은 예외의 원인에 해당하는 고유 번호(예외 번호)를 기반으로, exception table이라는 점프 테이블에서 적절한 exception handler의 주소를 찾는다.
- 그 주소로 제어 흐름이 전환 (즉, 함수 호출과 비슷하게 handler로 jump).
- 예외 핸들러 실행:
- 예외에 따라 복구 작업, 메시지 출력, 시스템 콜 수행, 또는 프로세스 종료 등 적절한 조치를 수행한다.
- 정상 복귀 or 종료:
- 예외의 종류에 따라:
- Icurr로 복귀 (예: 페이지 폴트)
- Inext로 복귀 (예: 시스템 콜)
- 복귀 불가 → 종료 (예: 하드웨어 오류)
- 예외의 종류에 따라:
예외 처리의 흐름 요약
단계 | 설명 |
1 | 예외 발생 (I<sub>curr</sub> 실행 중 이벤트) |
2 | 예외 번호에 따라 핸들러 주소 탐색 (exception table 참조) |
3 | 핸들러로 점프 → 예외 처리 루틴 실행 |
4 | 예외 처리 완료 후 흐름 복귀 또는 종료 |
- 예외는 일반적인 호출이 아닌 강제적인 함수 호출처럼 볼 수 있다.
- 예를 들어, 프로그램이 나뭇가지를 쭉 따라가며 실행 중인데, 갑자기 벼락이 떨어져서 다른 길(exception handler)로 밀려난 후, 다행히 다시 원래 길로 복귀하거나, 너무 큰 사고면 진행이 불가능해지는 상황이다.
8.1.2: Classes of Exceptions (예외의 분류)
예외는 발생 원인과 복귀 방식에 따라 4가지 주요 클래스로 분류된다.
1. Interrupt (인터럽트)
- 정의: 비동기적 예외로, CPU 외부에서 발생하는 이벤트에 의해 발생
- 예시: 하드 디스크의 I/O 완료, 타이머 인터럽트
- 흐름 복귀: 항상 다음 명령어(I<sub>next</sub>)로 복귀
- 특징:
- 현재 실행 중인 명령어와 무관
- 예외 발생 시점은 외부 요인에 따라 결정
2. Trap (트랩)
- 정의: 명시적인 요청에 의해 발생하는 예외 (즉, 정상적인 제어 흐름 일부)
- 예시: 시스템 콜 (OS 서비스 요청), int $0x80, syscall
- 흐름 복귀: 다음 명령어(Inext)로 복귀
- 특징:
- 사용자 프로그램이 직접 의도적으로 유발
- 예외 처리 후 일반적으로 정상 흐름으로 복귀
3. Fault (폴트)
- 정의: 잠재적으로 복구 가능한 에러
- 예시: 페이지 폴트 (해당 페이지를 메모리에 로딩하면 복구 가능), 0으로 나누기
- 흐름 복귀: 현재 명령어(Icurr)로 복귀 (예외 발생 당시로 되돌림)
- 특징:
- 커널이 문제를 해결한 후 재시도 가능
- 일부는 복구되지 못하고 abort로 전환될 수도 있음
4. Abort (어보트)
- 정의: 복구 불가능한 치명적 오류
- 예시: 하드웨어 오류, 시스템 상태 손상
- 흐름 복귀: 복귀 불가, 보통 프로그램을 강제 종료
- 특징:
- 핸들러가 거의 항상 프로그램 종료를 유도
- 디버깅 정보 제공 가능
예외 클래스 정리
클래스 | 발생 시점 | 복귀 위치 | 예시 |
Interrupt | 비동기 | 다음 명령어 | 타이머, 디스크 완료 |
Trap | 동기, 명시적 | 다음 명령어 | 시스템 콜 |
Fault | 동기, 암시적 | 현재 명령어 | 페이지 폴트 |
Abort | 동기, 복구 불가 | 없음 | 하드웨어 오류 |
8.1.3: Exceptions in Linux/x86-64 Systems
리눅스와 x86-64 시스템에서 예외 처리 방식
x86-64 아키텍처에서 예외 번호(exception number)와 예외 벡터(exception vector)를 어떻게 정의하고 사용하는지, 그리고 리눅스 커널이 이 예외들을 어떻게 분류하고 처리하는지에 대해 설명한다.
예외 벡터 (Exception Vector)
- 각 예외에는 고유한 숫자 ID가 부여된다. 이 숫자를 예외 벡터 번호라고 하며, 0부터 255번까지 정의되어 있다.
- 벡터 번호는 예외가 발생했을 때 실행할 핸들러 주소를 찾는 데 사용된다.
- 이 번호는 정수이며, x86-64 하드웨어에서 자동으로 커널에게 전달된다.
x86-64 예외 벡터 예시
예외 벡터 번호 | 예외 이름 | 클래스 | 설명 |
0 | Divide error | Fault | 0으로 나누기 |
1 | Debug exception | Trap | 디버그용 (single step 등) |
2 | Nonmaskable interrupt | Interrupt | 마스크 불가 인터럽트 |
3 | Breakpoint | Trap | 사용자 정의 중단점 |
6 | Invalid opcode | Fault | 잘못된 명령어 실행 |
13 | General protection | Fault | 권한 위반 등 일반 보호 오류 |
14 | Page fault | Fault | 메모리 페이지 부재 |
18 | Machine check | Abort | 하드웨어 오류 감지 |
32–255 | 사용자 정의 가능 | Trap 또는 Interrupt | 시스템 콜 등 |
시스템 콜 예외
- 시스템 콜은 trap을 통해 발생한다.
- x86-64에서는 syscall 명령어가 사용되며, 예외 벡터 번호는 128 또는 0x80 (구버전) 등이 사용됨.
- 이 trap은 사용자 모드에서 커널 모드로 전환하는 메커니즘이다.
리눅스에서의 예외 처리 요약
- 리눅스 커널은 이 예외 벡터들을 기반으로 핸들러 테이블을 유지하며, 발생 시 적절한 루틴을 실행함.
- 예외는 IDT(Interrupt Descriptor Table)에 매핑되어 있음.
- 시스템 콜, 페이지 폴트 등 다양한 이벤트는 이 방식으로 처리됨.
'크래프톤 정글 (컴퓨터 시스템: CSAPP) > 8장 예외적 제어 흐름' 카테고리의 다른 글
컴퓨터 시스템 : CSAPP 8장 정리 - 8.5 Signals Part.2 8.5.7 까지 (1) | 2025.04.19 |
---|---|
컴퓨터 시스템 : CSAPP 8장 정리 - 8.5 Signals Part.1 8.5.4 까지 (0) | 2025.04.19 |
컴퓨터 시스템 : CSAPP 8장 정리 - 8.4 Process Control (0) | 2025.04.19 |
컴퓨터 시스템 : CSAPP 8장 정리 - 8.3 System Call Error Handling (1) | 2025.04.19 |
컴퓨터 시스템 : CSAPP 8장 정리 - 8.2 Processes (0) | 2025.04.19 |