Exception

리치·2023년 4월 7일
0

서강대학교 - 최재승 교수님의 강의 및 강의자료를 바탕으로 정리하였습니다.

Control Flow

프로세서는 한번에 한가지 동작만 수행한다. 시작부터 종료까지 CPU는 일련의 명령문을 한번에 하나씩 읽고 실행한다. 이러한 순서를 CPU의 control flow라고 한다.

Altering the Control Flow

이런 Control flow를 바꾸는 2가지 매커니즘이 존재한다. 이는 프로그램의 상태에 기반해 수행된다. (i.e 변수의 값들)

  • Jump and branches
  • Call and return

하지만, 이는 유용한 시스템이라고 하기에는 불충분하다. 시스템 상태의 변화에도 대응해야한다. 시스템에는 다음과 같은 상황을 위해 "Exceptional control flow" 메커니즘이 필요하다.

  • 데이터는 디스크 또는 네트워크 어댑터에서 도착한다.
  • 명령문이 유효하지 않은 메모리 주소에 액세스한다.
  • 사용자가 키보드에서 Ctrl-C를 누른다.
  • 시스템 타이머가 만료된다.

Exceptional Control Flow

이러한 control flow는 컴퓨터 시스템의 모든 level에서 존재한다.

  • Low level 매커니즘
  1. Exceptions
    시스템에서 발생하는 이벤트에 대한 control flow의 변화 (i.e 시스템 상태의 변화)
    하드웨어와 OS 소프트웨어의 조합을 사용한 구현
  • Higher level 매커니즘
  1. Process context switch
  2. Signals

Exceptions

Exception은 어떤 이벤트가 발생했을 때 OS kernel로 control이 이전된다.

  • Kernel은 OS상에서 메모리가 상주하는 부분이다.
  • 이때 이벤트는 0으로 나누기, 오버플로우, I/O 요청, Ctrl-C 등이 있다.

    그림에서 I_current 시점에 Event가 발생하면 Exception이 일어나게 된다. Kernel이 제어권을 가지게 되고 Exception Handler에 의해 처리된다. 이후, 상황에 따라 프로그램이 abort되거나 I_current로 돌아가거나, I_next로 이동된다.

각 상황에 맞는 유니크한 Exception table이 존재하며, 마찬가지로 Handler 역시 각 상황에 맞게 유니크하게 존재한다.

Exception은 다음 표와 같이 사유에 따라 4가지 클래스로 나눌 수 있다.

Interrupts

Interrupts는 Asynchronuous Exception이다. 비동기란 동시에 일어나지 않고 서로 독립적으로 발생하는 것을 의미한다. Asynchronuous Exception는 프로그램 실행 도중 외부에서 발생한 이벤트로 인해 작업이 일시 중단되고 다른 작업이 처리되는 현상이다.

  • Timer interrupt : 커널이 사용자 프로그램으로부터 제어를 되찾기 위해 발생한다. 매 ms초 마다 외부 타이머 칩이 interrupt를 발생시킨다.
  • I/O interrupt : 외부 장치로부터의 인터럽트로, 예를 들면 키보드의 Ctrl-C 입력이 있다. 그 외에도 네트워크 패킷 도착, 디스크로부터 데이터 도착 등이 있다.

이러한 인터럽트들은 운영체제가 다양한 작업을 효율적으로 수행할 수 있도록 도와주며, 앞서 말했듯 인터럽트 처리 과정에서 Handler(ISR로도 불린다)가 사용된다. Handler가 예외를 처리하고 나면, 항상 next instruction으로 return 된다.

Synchronous Exceptions

동기란 동시에 일어나거나 연관된 순서로 발생하는 것을 의미한다. Synchronous Exceptions는 프로그램의 명령어를 실행하는 결과로 발생하는 이벤트로, 발생 원인과 프로그램 실행과 직접적인 관련이 있다.

  • Traps : 의도적으로 발생하는 예외로, system calls이나 브레이크포인트 트랩 등이 있다. Interrupt와 동일하게 next instruction으로 return 한다.

  • Faults : 의도하지 않았으나 회복이 가능한 예외로, 페이지 폴트나 일반 보호 장애 등이 있다. current instruction을 다시 실행하거나 중단(abort)한다. 코드를 작성할 때 쉽게 볼 수 있는 Segmentation Fault는 위의 예를 다 포함한다.

  • Aborts : 의도하지 않았고 회복이 불가능한 예외로, 패리티 에러나 기계 체크 등이 있다. 현재 프로그램을 중단시킨다.


하지만, 이러한 분류는 명확한 합의점이 없기 때문에 너무 집착하지 않아도 된다!! 사람들마다 부르는 exception이 다르다. 일부는 Synchronous에 대해서만 exception이라 부르기도 한다. 또한, 일부는 위의 모두를 interrupt라 부르기도 한다.

Example of Exceptions

오늘날 많이 사용되는 x86-64 시스템에서는 예외의 종류와 번호가 정의되어 있다. 인텔 아키텍처에서 0부터 31까지의 번호가 정의되어 있으며, 이는 모든 x86-64 시스템에서 동일하다. 32부터 255까지의 번호는 운영체제 개발자에 의해 정의된다. 리눅스에서는 128(0x80)을 system call이라 불리는 특별한 Trap으로 지정했다.

Exception Number에 따른 상황

  • 0: 나눗셈 에러 (Fault)
  • 13: 일반 보호 장애 (Fault)
  • 14: 페이지 폴트 (Fault)
  • 18: 기계 체크 (Abort)
  • 32-255: 운영체제 정의 예외 (Interrupt or trap)

Trap은 Kernel에 의도적으로 발생시키는 exception의 종류이다. Exception과 비슷하게 Trap의 System call에는 고유한 ID번호가 존재한다. 다음은 그 예이다.

System call의 Opening File의 예제를 살펴보자. 유저는 파일을 열기위해 open(filename, options) 함수를 사용한다.

open은 POSIX (Portable Operating System Interface) 시스템 호출 중 하나로, Unix 및 Unix 계열 운영체제에서 사용할 수 있다. 이 함수는 운영체제 수준에서 직접적으로 파일을 열고 관리한다. 따라서 C언어에서 사용되는 fopen은 C 표준 라이브러리 함수로서 높은 수준의 파일 처리와 플랫폼 간 호환성을 제공하며 추상화된 버전으로 생각할 수 있다. 반면, open은 보다 낮은 수준의 파일 처리를 제공한다.

우선, open을 사용하면 이는 System call의 일종이므로 리눅스상에서 Exception table 128번을 찾아가게 된다. 이제, 어셈블리 코드를 보면 mov $0x2, %rax를 볼 수 있다. 이는 레지스터에 open의 Syscall Number인 2를 저장한다. 다음 문장에서 syscall(open)이 호출된다. System call의 인자는 레지스터를 통해 전달되며, 반환값은 %rax 레지스터에 저장된다. 이후, 레지스터와 값을 비교하여 반환 값이 오류를 나타내는지 확인한다. 일반적으로 syscall이 실패하면 반환값은 -1에서 -4095 사이의 값이다. (errono)

이번에는 Fault 예제를 살펴보자.

int a[1000]; 
main() {
	a[500] = 13;
    a[5000] = 13;
}

2가지 경우가 있을 수 있다!

  1. Page Fault
    80483b7: c7 05 10 9d 04 08 0d movl $0xd,0x8049d10
    우선, a[500] = 13; 의 경우 분명 아무 문제 없는 코드이지만(주소값이 valid) 물리적 메모리에 해당 페지이가 없을 때 발생한다. 이럴 때 역시 Handling을 하게 되며 운영체제가 디스크에서 해당 페이지를 메모리로 가져와서 프로그램이 계속 실행될 수 있도록 한다.
  2. Invalid Memory Access
    80483b7: c7 05 60 e3 04 08 0d movl $0xd,0x804e360
    a[5000] = 13; 은 프로그램이 유효하지 않은 메모리 주소(주소값이 Invalid)에 액세스하려고 하며 이때 에러가 발생한다. (코드 자체에 문제) 이 경우 시그널(SIGSEGV)이 사용자 프로세스로 전송되며, 프로세스는 "segmentation fault"라는 메시지와 함께 종료된다.
    (실제로 segmentaion fault는 다양한 상황에서 발생한다. 예로 스택 오버플로우, 힙 메모리 오버플로우 등이 있다.)

다음 장에서는 Process에 대해 다룬다.

profile
이것저것

0개의 댓글