크래프톤 정글 (컴퓨터 시스템: CSAPP)/8장 예외적 제어 흐름

컴퓨터 시스템 : CSAPP 8장 정리 - 8.1 Exceptions

고웅 2025. 4. 19. 08:36

Exceptional Control Flow (ECF)는 프로그램의 일반적인 순차 흐름을 예외적으로 변경하는 메커니즘으로, 운영체제의 핵심 기능인 입출력, 프로세스, 가상 메모리 등을 구현하는 데 사용된다. 프로그래머에게 중요한 이유는 다음과 같다:

  1. 시스템 개념 이해에 필수적: 프로세스 생성, 종료, 신호 처리 등은 모두 ECF를 기반으로 구현된다.
  2. 운영체제와의 상호작용 이해: 응용 프로그램이 시스템 콜을 통해 OS 서비스를 요청할 때 trap이라는 형태의 ECF를 사용한다.
  3. 응용 프로그램 작성 능력 향상: 예를 들어 유닉스 셸이나 웹 서버 같은 프로그램은 ECF 메커니즘을 적극적으로 활용한다.
  4. 동시성(concurrency) 이해의 기초: 인터럽트, 프로세스, 스레드, 시그널 핸들러 등의 개념은 모두 ECF의 예다.
  5. 소프트웨어 예외 처리의 이해: C++, Java 등의 언어에서 사용하는 예외 처리(try, catch, throw)는 낮은 수준의 ECF(setjmp, longjmp 등)를 기반으로 구현된다​.

8.1 : Exceptions

Exception(예외)은 하드웨어와 운영체제가 공동으로 처리하는 ECF의 한 형태로, 프로세서 상태의 변화에 따라 발생하는 갑작스러운 제어 흐름 변경이다. 예외가 발생하면 exception handler라는 특수 서브루틴으로 흐름이 전환된다.

8.1.1: Exception Handling (예외 처리)

예외는 일반적으로 현재 실행 중인 명령어(Icurr)의 실행 중 발생한 이벤트로 인해 트리거된다. 예외가 발생하면 다음과 같은 과정이 이루어진다:

  1. 현재 프로세서 상태 저장:
    • 현재 프로그램 카운터(PC, 즉 다음에 실행할 명령어의 주소), 레지스터 등 시스템 상태를 저장.
    • 이는 이후 예외 처리 루틴을 마친 뒤 원래 흐름으로 되돌아가기 위해 필수.
  2. 핸들러 주소 결정 및 점프:
    • 시스템은 예외의 원인에 해당하는 고유 번호(예외 번호)를 기반으로, exception table이라는 점프 테이블에서 적절한 exception handler의 주소를 찾는다.
    • 그 주소로 제어 흐름이 전환 (즉, 함수 호출과 비슷하게 handler로 jump).
  3. 예외 핸들러 실행:
    • 예외에 따라 복구 작업, 메시지 출력, 시스템 콜 수행, 또는 프로세스 종료 등 적절한 조치를 수행한다.
  4. 정상 복귀 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)에 매핑되어 있음.
  • 시스템 콜, 페이지 폴트 등 다양한 이벤트는 이 방식으로 처리됨.