jun-wiki

View My GitHub Profile

Posts (Latest 10 updated) :
Read all
Contents:
  1. 8 예외적인 제어 흐름
    1. 8.1 예외 상황
      1. 8.1.1 예외 처리
      2. 8.1.2 예외의 종류
      3. 8.1.3 리눅스/x86-64 시스템에서의 예외상황

오느르 목표는 csapp 8.1 E다



8 예외적인 제어 흐름

프로그램 카운터는 값들의 순서를 취한다 ak에서 ak+1이런 느낌으로

이를 제어 전이(control transfer) 라고 부른다
그리고 이러한 제어 전이들의 연속을 제어 흐름(flow of control, control flow) 라고 한다

프로그램에선 이러한 흐름을 관리하는데 시스템에서의 변화가 이에 영향을 주기도 한다

그러나 시스템은 내부 프로그램 변수로 포착못한다

현대 시스템은 이런 상황들에 제어 흐름의 급격한 변화로 반응한다
그리고 이러한 급격한 변화를 예외적 제어 흐름(ECF, exceptional control flow) 이라고 한다


그리고 ECF에 대해 알면 좋은 점이 나온다

대충 몸에 좋고 여자에게도 좋으며 수험생과 남자에게 좋다고 한다



8.1 예외 상황

프로세서에서 상태 변화(이벤트)가 일어나면 프로그램에서 예외 처리기로 제어의 급격한 전이(예외)를 유발하고 처리 후 중단된 프로그램으로 제어를 반환하거나 중단한다

어찌되었든 이벤트 발생을 감지하면
이를 처리하도록 특별히 설계된 운영체제 서브루틴 (예외 처리기)로 처리한다는거다
이후, 셋 중 하나가 일어난다

  1. 처리기가 이벤트가 발생했을 때 실행 중이던 현재 명령어 Icurr로 제어를 반환한다.

  2. 처리기가, 예외가 발생하지 않았더라면 다음에 실행되었을 Inext로 제어를 반환한다.

  3. 처리기가 중단된 프로그램을 중단(abort)한다.



8.1.1 예외 처리

  • 예외 처리 = 하드웨어 + 소프트웨어 협업: 역할이 섞여 보여 헷갈리기 쉬움.

  • 예외 번호 부여: 모든 예외는 고유한 정수 번호. 일부는 CPU 설계자(0으로 나누기, 페이지 폴트 등), 일부는 OS 커널(시스템 콜, 외부 I/O 신호 등)이 정함.

  • 예외 테이블: 부팅 시 OS가 점프 테이블을 만들고, 엔트리 k에 예외 k의 처리기 주소를 둠. 테이블 시작 주소는 전용 레지스터에 보관.

  • 런타임 동작: CPU가 이벤트 감지 → 예외 번호 k 결정 → 예외 테이블의 엔트리 k를 통해 해당 처리기로 간접 호출.

  • 프로시저 호출과 공통점/차이점

    • 공통: 처리기로 분기 전 반환 주소를 스택에 푸시.

    • 차이: 반환 주소가 현재 명령 또는 다음 명령 중 무엇인지는 예외 클래스에 따라 다름.

  • 추가 상태 저장: CPU는 재시작에 필요한 레지스터 상태(EFLAGS 등) 도 함께 스택에 푸시.

  • 스택과 모드: 사용자→커널 전이 시 이 값들은 커널 스택에 저장되고, 예외 처리기는 커널 모드에서 실행(시스템 자원에 풀 액세스).

  • 복귀: 하드웨어가 예외를 트리거한 뒤 나머지는 소프트웨어(처리기)가 수행. 처리 후 return-from-interrupt로 저장 상태 복원, 필요 시 사용자 모드로 전환, 중단된 프로그램으로 복귀.



8.1.2 예외의 종류

네 가지 예외 클래스

  • Interrupt (인터럽트)

    • 원인: I/O 장치 신호, 비동기
    • 복귀: 항상 다음 명령으로
    • 타이밍: 현재 명령 끝난 뒤 예외 처리기 실행
  • Trap (트랩)

    • 원인: 의도적 예외(예: 시스템 콜), 동기
    • 복귀: 항상 다음 명령
    • 특징: syscall n → 커널 모드에서 처리, 커널 스택 사용
  • Fault (폴트)

    • 원인: 복구 가능할 수도 있는 오류, 동기
    • 복귀: 처리기가 고치면 현재 명령 재실행, 아니면 어보트
    • 대표: 페이지 폴트(디스크에서 페이지 적재 후 재실행)
  • Abort (어보트)

    • 원인: 복구 불가 치명적 오류(예: 하드웨어 패리티/머신 체크), 동기
    • 복귀: 절대 복귀하지 않음(프로세스 종료)

흐름 한 줄씩

  • 인터럽트: I/O 장치가 예외 번호 신호 → 현재 명령 끝 → 처리기 실행 → 다음 명령으로 복귀
  • 트랩(시스템 콜): syscall 실행 → 처리기 실행(커널 모드/커널 스택) → 다음 명령
  • 폴트: 현재 명령이 오류 유발 → 처리기 시정 시 그 명령 재실행, 실패 시 어보트
  • 어보트: 치명적 오류 → 어보트 루틴 → 종료

x86-64 예외 번호 예

  • 0: 나눗셈 오류 → 폴트
  • 13: 일반 보호 예외 → 폴트
  • 14: 페이지 폴트 → 폴트
  • 18: 머신 체크 → 어보트
  • 32–255: OS 정의(인터럽트/트랩)


8.1.3 리눅스/x86-64 시스템에서의 예외상황

예외 번호 범위

  • 최대 256개: 0–31은 인텔이 정의(모든 x86-64 공통), 32–255는 OS가 정의(인터럽트/트랩).

Linux/x86-64의 주요 폴트/어보트

  • Divide error (0): 0으로 나눔 등 → 복구 안 함, 프로세스 종료(셸 메시지: Floating exception).
  • General protection fault (13): 잘못된 메모리 접근/권한 위반 등 → 복구 안 함, 종료(셸 메시지: Segmentation fault).
  • Page fault (14): 필요한 페이지를 디스크→메모리로 적재 후 같은 명령 재실행(성공 시 정상 진행).
  • Machine check (18): 치명적 하드웨어 오류복귀 없이 종료.

Linux/x86-64 시스템 콜 요약

  • 트랩 명령 syscall 로 진입(커널 모드, 커널 스택 사용).
  • 호출 규약

    • %rax: 시스템 콜 번호
    • 인자 6개까지: %rdi, %rsi, %rdx, %r10, %r8, %r9
    • 반환값: %rax
    • 파괴 레지스터: %rcx, %r11
    • 오류: −4095 ~ −1 반환 ⇒ −errno
  • C에선 보통 read/write/fork/execve/_exit 같은 래퍼 함수를 사용(래퍼가 syscall 세팅·호출·에러처리 담당). 직접 필요 시 syscall() 또는 인라인 ASM 사용.
  • 예시: write(1, "hello\n", 6)로 출력 후 _exit(0)로 종료(래퍼 없이도 위 레지스터 규약대로 설정하면 동작).