본문 바로가기

컴퓨터 시스템 이야기

Exceptional control flow

줄여서 ECF 라 한다. 프로그램 카운터는 연속된 값들을 가정하고 움직인다. 특정 주소에서 다른 주소로의 전환을 control transfer라 한다. 가장 간단한 유형의  control flow는 점진적인 순서로 메모리에 있는 경우이다.  갑자기 인접해 있지 않은 명령어로 넘어가는 경우도 있는데, 이러한 경우들도 우리가 흔히 아는 jump, call, return  같은 인스트럭션들에 의해 발생한다. 그러나 시스템들은 또한 내부 프로그램 변수에 의해 표시되지 않으며, 프로그램 실행과는 반드시 관련되지 않은 시스템 상태 변화도 반응해야 한다. 예를 들면 하드웨어 타이머가 규칙적으로 꺼지는 것과 패킷들이 네트워크 어댑터에 도착하고 메모리에 저장되는 것과 같은 일들이다.

이와 같은 급격한 변화를 ECF라 한다. ECF는 컴퓨터 시스템의 모든 수준에서 발생함. 예를 들면 하드웨어에 의해서 검출되는 이벤트들은 예외 핸들러로 갑작스런 control transfer를 실행하거나 운영체제 커널 수준의 문맥 전환을 통해 사용자 프로세스에서 다른 프로세스로 control이 이동하는 동작들이 있다.

프로그래머로서 ECF를 이해하는 여러 가지 이유가 있다고 한다.

  • ECF는 운영체제가 입출력, 프로세스, 가상메모리를 구현하기 위해 사용하는 기본 메커니즘
  • ECF를 이해하면 어떻게 applicaiton이 운영체제와 상호작용하는지를 이해하는 데 큰 도움이 됨
  • ECF는 동시성을 구현하는 기본 메커니즘

등등 이 있다.

8.1 Exeptions

예외 상황은 어떤 프로세서 상태의 변화에 대한 대응이다

execption 기본 아이디어

프로세서는 명령어를 실행하고 있을 때 프로세서 상태에 중요한 변화가 일어남. 이러한 상태 변화는 이벤트로 알려져 있다. 이벤트의 예시를 들면 가상 메모리 페이지 오류, 산술 오버플로우 등등. 또한 현재 명령어로 관련 없는 시스템 타이머가 정지하거나 I/O 요청 완료 등도 있음 어느 경우든 프로세서가 이벤트가 발생했다는 것을 감지하면, 예외 테이블이라고 하는 점프 테이블을 통해 이 특정 종류의 이벤트를 처리하기 위해 특별히 설계된 운영 체제 서브루틴(예외처리 핸들러)로 간접 프로시저 콜을 함

예외처리 핸들러가 처리를 끝내면, 예외상황을 발생시킨 이벤트에 따라 세가지 일 중 하나가 발생

  • 핸들러는 제어를 현재 인스트럭션으로 돌려줌, 이 인스트럭션은 이벤트가 발생했을 때의 명령어
  • 핸들러는 control을 다음 인스트럭션으로 돌려줌, 
  • 핸들러는 프로그램을 중단

8.1.1 예외처리

한 시스템 내에서 가능한 예외상황의 종류마다 중복되지 않는 양의 정수를 예외 번호로 할당함. 이 숫자들의 일부는 프로세서 설계자가 부여한 것, 나머지는 운영체제 커널 설계자가 할당. 전자의 예시는 divde by zero, 페이지 오류 등. 후자의 예로는 시스템 콜, 외부 I/O 디바이스로부터의 시그널이 포함됨.

시스템 부팅 시 운영체제는 예외 테이블이라고 하는 점프 테이블을 할당하고 초기화해서 엔트리 k가 예외상황 k에 대한 핸들러의 주소를 갖음. 런타임에서 프로세서는 이벤트가 발생한 것을 감지하고, 대응되는 예외 번호 k를 결정. 그 후에 예외 테이블의 엔트리 k를 통해서 간점 프로시저 콜을 하는 방법으로 예외상황을 발생. 예외 번호는 예외 테이블에서 인덱스이며, 이 테이블의 시작 주소는 예외 테이블 베이스 레지스터라는 특별한 cpu 레지스터에 저장되어 있다.

예외 상황은 프로시저 콜과 유사하지만 일부 중요한 차이점이 있다.

  • 프로세서는 프로시저 콜을 사용해서 핸들러로 분기하기 전에 스택에 리턴주소를 푸시한다.  그렇지만 , 예외의 종류에 따라 리턴 주소는 현재 명령어 이거나 다음 명령어 일 수 있다.
  • 또한 프로세서는 핸들러가 리턴할 때 중단된 프로그램을 다시 시작하기 위해 필요하게 될 스택 상에 추가적인 프로세서 상태를 푸시. 예를 들어 x86-64 시스템은 다른 것보다 현재 조건 코드를 포함하는 EFLAGS 레지스터를 푸시.
  • control이 사용자 프로그램에서 커널로 전환하고 있을 때, 이 모든 아이템들은 사용자 스택 위가 아니라 커널 스택 상에 푸시됨.
  • 예외 핸들러는 커널 모드에서 돌아가는데, 이것은 이들이 모든 시스템 자원에 완전히 접근할 수 있는 것을 의미.

하드웨어가 예외상황을 촉발해서 남은 작업은 예외 핸들러에 의해 소프트웨어로 진행됨. 핸들러는 예외 처리로 인해 발생된 이벤트를 처리한 후 경우에 따라서는 인터럽트에서 원래 위치로 복귀라는 특별한 명령어를 실행시켜서 인터럽트에 의해서 중단된 프로그램으로 돌아감. 이 명령어는 인터럽트가 발생되기 전의 원래 프로세서의 제어 상태와 데이터 레지스터 상태를 스택으로부터 팝 해서 돌려주고, 만일 예외가 사용자 프로그램을 중단했으면 상태를 사용자 모드로 되돌리고, 최종적으로 control을 중단되었던 프로그램으로 리턴해줌.

8.1.2 예외의 종류

예외상황은 네 가지 종류로 구분할 수 있다. 인터럽트, 트랩, fault, abort

인터럽트는 프로세서 외부에 있는 입출력 디바이스로부터의 시그널의 결과로 비동기적으로 발생한다. 하드웨어 인터럽트는 비동기적이며, 즉 특정 인스트럭션을 실행해서 발생한 것이 아니라는 의미에서 그렇다. 하드웨어 인터럽트를 위한 예외 핸들러는 종종 인터럽트 핸들러라고 부른다. 네트워크 어댑터, 디스크 컨트롤러, 타이머 칩 같은 입출력 디바이스들은 프로세서 칩의 핀에 시그널을 보내서 인터럽트를 발생시키고, 디바이스를 식별하는 예외 번호를 시스템 버스에 보냄.

프로세서는 인터럽트 핀이 high로 올라갔다는 것을 발견하고 시스템 버스에서 예외번호를 읽으며, 적절한 인터럽트 핸들러를 호출. 핸들러가 리턴할 때, 제어를 다음 인스터럭션으로 돌려줌. 프로그램이 마치 인터럽트가 발생하지 않았던 것처럼 계속 실행. 나머지 예외는 동기적으로 일어남 이런 것들은 오류 인스트럭션.

트랩과 시스템 콜 

트랩은 의도적인 예외상황으로, 어떤 인스트럭션을 실행한 결과로 발생함. 인터럽트 핸들러와 마찬가지로 트랩 핸들러는 제어를 다음 인스트럭션으로 리턴. 트랩의 가장 중요한 사용은 시스템 콜이라고 알려진 사용자 프로그램과 커널 사이의 프로시저와 유사한 인터페이스를 제공하는 것이다. 이러한 커널 서비스의 제한된 접근을 하기 위해서 프로세서는 특별한 'n' 인스트럭션을 제공하며, 이들은 사용자 프로그램이 서비스 n을 요청하고자 할 때 사용자 프로그램이 사용할 수 있는 인스트럭션이다. syscall 인스트럭션을 실행하면 트랩이 인자들을 해독하고 적절한 커널 루틴을 호출하는 예외 핸들러로 가게 한다. 보통 함수와 비슷해 보이지만 보통 함수는 사용자 모드에서 돌아가며, 이 때문에 이들이 실행할 수 있는 인스트럭션은 제한적이며, 이들은 호출하는 함수와 동일한 스택을 사용함. 시스템 콜은 커널 모드에서 돌아가며, 이로 인해 커널 내에서 정의된 스택에 접근함. 

오류는 핸들러가 정정할 수 있을 가능성이 있는 에러 조건으로부터 발생함. 오류가 발생하면 프로세서는 제어를 오류 핸들러로 이동해줌. 핸들러가 에러 조건을 정정할 수 있다면, control을 오류를 발생시킨 명령어로 돌려주어서 다시 실행. 그렇지 않으면 abort루틴 리턴해서 종료함. 오류의 대표적인 게 페이지 오류로 가상 메모리 테이블을 참조했을 때 대응되는 실제 메모리 page가 존재하지 않아 디스크에서 가져와야 될 때이다. 핸들러는 디스크에서 페이지 로드 후에 다시 명령어로 제어권 넘김. 그리고 다시 실행한다.

중단 은 DRAM이나 SRAM이 고장 날 때 발생하는 패리티 에러와 하드웨어 같은 치명적인 에러에서 발생 다시 응용프로그램으로 제어권 안 넘김.

8.2  프로세스

프로그램을 실행할 때 마치 프로그램이 단 한 개의 시스템에서 돌아가고 있는 것처럼 보인다. 우리의 프로그램이 프로세서와 메모리를 독점해서 사용하는 것처럼 보인다. 프로세서는 프로그램 내의 인스트럭션들을 차례대로 중단됨 없이 실행하는 것처럼 보인다. 또한 프로그램의 코드와 데이터는 시스템 메모리 상의 유일한 객체인 것처럼 보인다. 이러한 착각은 프로세스라는 개념에 의해서 이루어진다. 프로세스의 고전적인 정의는 실행 프로그램의 인스턴스이다. 시스템 내의 각 프로그램은 어떤 프로세스의 문맥에서 돌아간다. 이 상태는 메모리에 저장된 프로그램의 코드와 데이터, 스택, 범용 레지스터의 내용, 프로그램 카운터, 환경변수, 열려 있는 파일의 식별자를 포함

프로세스가 application에 제공하는 주요 추상화에 집중

  • 프로그램이 프로세서를 혼자서 사용한다는 착각을 제공하는 독립적 논리 제어 흐름
  • 프로그램이 혼자서 메모리 시스템을 가진다는 착각을 제공하는 사적 주소 공간

우리의 실행 목적 파일 내에 들어 있거나 프로그램과 동적으로 런타임에 링크된 공유 객체 내의 인스트럭션들에게 일련의 프로그램 카운터 값들이 대응된다는 것을 관찰할 수 있다. 이러한 PC값들의 배열을 논리적 제어 흐름 논리 흐름이라고 한다.  3개의 프로세스를 실행하는 시스템에 대해서 생각해 보면, 이 프로세서의 하나의 물리적 제어 흐름은 각 프로세스에 대해서 한 개씩 세 개의 논리 흐름으로 나누어진다. 각 수직선은 어떤 프로세스에 대한 논리적인 프름의 일부를 나타낸다. 요점은 하나의 프로세서를 사용해서 여러 프로세스들이 교대로 돌아간다는 점이다. 

8.2.2 동시성 흐름

논리 흐름은 컴퓨터 시스템 내에서 여러 가지 다른 형태를 갖는다. 예외 핸들러, 프로세스, 시그널 핸들러, 스레드, 자바 프로세스는 모두 논리 흐름의 예다.

자신의 실행시간이 다른 흐름과 겹치는 논리 흐름을 동시성 흐름이라고 부르며, 이 두 흐름은 동시에 실행한다고 말한다. 

논리적 제어흐름

A와 B 같은 경우를 동시에 돌아간다고 한다. 공동으로 실행되는 흐름의 일반적인 현상이 동시성이라고 알려져 있다. 또한 프로세스가 다른 프로세스들과 교대로 실행된다는 개념은 멀티태스킹이라고 알려져 있다. 한 프로세스가 자신의 흐름 일부를 실행하는 매 시간 주기를 타임 슬라이스라고 부른다. 그래서 멀티태스킹은 타임 슬라이싱이라고도 부른다. 위의 그림에서 프로세스 A에 대한 흐름은 두 개의 타임 슬라이스로 구성됨.

동시적 흐름에 대한 개념은 흐름들이 돌아가는 프로세서 코어나 컴퓨터의 개수와는 무관함. 만일 두 흐름이 시간상으로 중첩되면, 비록 이들이 동일한 프로세서에서 돌아가고 있더라도 이들은 동시적이다. 그렇지만 병렬 흐름이라고 알려진 동시성의 부분집합 개념을 구별하는 것은 유용함. 만일 두 개의 흐름이 서로 다른 프로세서 코어나 컴퓨터에서 동시에 돌아가고 있다면, 이것은 병렬 흐름(parallel flow)이다.

8.2.3 사적 주소 공간

프로세스는 각 프로그램에 자신이 시스템의 주소 공간을 혼자서 사용한다는 착각을 불러 일으킨다. 프로세스는 각 프로그램에 자신만의 사적 주소공간을 제공한다. 이 공간의 특정 주소에 연결된 메모리의 한 개의 바이트가 일반적으로 다른 프로세스에 의해서 읽히거나 쓰일 수 없다는 의미로 이 공간은 사적이다. 사적 주소 공간에 연결된 메모리의 내용이 일반적으로 서로 다를지라도 각각의 이런 공간은 동일한 일반적인 구조를 갖는다. 주소 공간의 윗 부분은 커널을 위해 예약 되어 있다. 주소공간의 이 부분은 커널이 프로세스를 대신해서 명령어를 실행할 때 사용하는 코드, 데이터, 스택을 포함함.

8.2.4 사용자 및 커널 모드

운영체제가 완벽한 프로세스 추상화를 제공하기 위해서 프로세서는 응용프로그램이 접근할 수 있는 주소공간 부분뿐만 아니라 응용프로그램이 실행할 수 있는 인스트럭션들을 제한 하는 메커니즘을 제공해야함. 프로세서는 이러한 작업을 지원하기 위해 일부 제어 레지스터로 모드 비트를 제공, 모드 비트가 설정되면 프로세스는 커널 모드로 동작한다. 커널 모드에서 돌고 있는 프로세스는 명령어 집합의 어떤 명령어도 실행 할 수 있으며, 시스템 내의 어떤 메모리 위치도 접근 할 수 있다. application 코드를 실행하는 프로세스는 처음에 사용자 모드에 있는데, 커널 모드로 진입하려면 인터럽트, 오류 , 트랩 시스템 콜 같은 예외를 통해서 이다. 

8.2.5 문맥 전환

커널은 문맥 전환이라고 알려진 예외적인 제어 흐름의 상위 수준 형태를 사용해서 멀티태스킹을 구현함. 커널은 프로세스마다 콘텍스트를  유지한다. 콘텍스트는 커널이 선점된 프로세스를 다시 시작하기 위해서 필요로 하는 상태임. 여러 가지 범용 레지스터 , 부동소수점 레지스터 등등, 커널 자료구조 같은 -주소 공간을 규정하는 페이지 테이블, 현재 프로세스에 관한 정보를 가지고 있는 프로세스, 테이블 , 프로세스가 오픈한 파일에 관한 정보를 저장하는 파일 테이블 같은- 객체들의 값들로 구성.  문맥 전환의 수행 절차는 현재 프로세스의 콘텍스트를 저장하고, 이전에 선점된 프로세스의 저장된 컨텍스트를 복원하며, 제어를 새롭게 복원된 프로세스로 전달. context switching 은 커널이 사용자를 대신해서 시스템 콜을 실행하고 있을 때 일어날 수 있다. 만일 시스템 콜이 어떤 이벤트의 발생을 기다리기 때문에 블록 된다면 커널은 현재 프로세스를 sleep 시키고 다른 프로세스로 전환한다. 예를 들자면 read시스템 콜이 디스크 접근을 요구하면 커널은 문맥 전환을 할 수 있다.

 

'컴퓨터 시스템 이야기' 카테고리의 다른 글

시그널 핸들러 작성하기  (0) 2021.09.24
네트워크 프로그래밍 (수정중)  (0) 2021.09.17
copy on write  (0) 2021.09.16
Memory system  (0) 2021.09.15
Chapter 1 컴퓨터 시스템으로의 여행(1.8 ~ 끝)  (2) 2021.08.22