서강대학교 - 최재승 교수님의 강의 및 강의자료를 바탕으로 정리하였습니다.
프로세서는 한번에 한가지 동작만 수행한다. 시작부터 종료까지 CPU는 일련의 명령문을 한번에 하나씩 읽고 실행한다. 이러한 순서를 CPU의 control flow라고 한다.
이런 Control flow를 바꾸는 2가지 매커니즘이 존재한다. 이는 프로그램의 상태에 기반해 수행된다. (i.e 변수의 값들)
하지만, 이는 유용한 시스템이라고 하기에는 불충분하다. 시스템 상태의 변화에도 대응해야한다. 시스템에는 다음과 같은 상황을 위해 "Exceptional control flow" 메커니즘이 필요하다.
이러한 control flow는 컴퓨터 시스템의 모든 level에서 존재한다.
Exception은 어떤 이벤트가 발생했을 때 OS kernel로 control이 이전된다.
각 상황에 맞는 유니크한 Exception table이 존재하며, 마찬가지로 Handler 역시 각 상황에 맞게 유니크하게 존재한다.
Exception은 다음 표와 같이 사유에 따라 4가지 클래스로 나눌 수 있다.
Interrupts는 Asynchronuous Exception이다. 비동기란 동시에 일어나지 않고 서로 독립적으로 발생하는 것을 의미한다. Asynchronuous Exception는 프로그램 실행 도중 외부에서 발생한 이벤트로 인해 작업이 일시 중단되고 다른 작업이 처리되는 현상이다.
이러한 인터럽트들은 운영체제가 다양한 작업을 효율적으로 수행할 수 있도록 도와주며, 앞서 말했듯 인터럽트 처리 과정에서 Handler(ISR로도 불린다)가 사용된다. Handler가 예외를 처리하고 나면, 항상 next instruction으로 return 된다.
동기란 동시에 일어나거나 연관된 순서로 발생하는 것을 의미한다. Synchronous Exceptions는 프로그램의 명령어를 실행하는 결과로 발생하는 이벤트로, 발생 원인과 프로그램 실행과 직접적인 관련이 있다.
Traps : 의도적으로 발생하는 예외로, system calls이나 브레이크포인트 트랩 등이 있다. Interrupt와 동일하게 next instruction으로 return 한다.
Faults : 의도하지 않았으나 회복이 가능한 예외로, 페이지 폴트나 일반 보호 장애 등이 있다. current instruction을 다시 실행하거나 중단(abort)한다. 코드를 작성할 때 쉽게 볼 수 있는 Segmentation Fault는 위의 예를 다 포함한다.
Aborts : 의도하지 않았고 회복이 불가능한 예외로, 패리티 에러나 기계 체크 등이 있다. 현재 프로그램을 중단시킨다.
하지만, 이러한 분류는 명확한 합의점이 없기 때문에 너무 집착하지 않아도 된다!! 사람들마다 부르는 exception이 다르다. 일부는 Synchronous에 대해서만 exception이라 부르기도 한다. 또한, 일부는 위의 모두를 interrupt라 부르기도 한다.
오늘날 많이 사용되는 x86-64 시스템에서는 예외의 종류와 번호가 정의되어 있다. 인텔 아키텍처에서 0부터 31까지의 번호가 정의되어 있으며, 이는 모든 x86-64 시스템에서 동일하다. 32부터 255까지의 번호는 운영체제 개발자에 의해 정의된다. 리눅스에서는 128(0x80)을 system call이라 불리는 특별한 Trap으로 지정했다.
Exception Number에 따른 상황
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가지 경우가 있을 수 있다!
다음 장에서는 Process에 대해 다룬다.