Published on

[ Pintos ] 0.Synchronization

Authors
  • avatar
    Name
    유사공대생
    Twitter

개요

하드웨어는 어느 순간이든 시스템 버스를 통해 CPU에 신호를 보내 인터럽트를 발생시킬 수 있다.(여기서 말하는 시스템 버스는 주요 구성요소 사이의 주요 통신 경로이다.) 인터럽트는 다른 많은 목적으로도 사용되며 운영체제와 하드웨어의 상호작용 방식의 핵심 부분이다.

CPU가 인터럽트되면, CPU는 하던 일을 중단하고 즉시 고정된 위치로 실행을 옮긴다. 이러한 고정된 위치는 일반적으로 인터럽트를 위한 서비스 루틴이 위치한 시작 주소를 가지고 있다.

그리고 인터럽트 서비스 루틴이 실행된다. 인터럽트 서비스 루틴(Interrupt Service Routine, ISR)은 컴퓨터 시스템에서 발생하는 인터럽트에 대한 처리를 담당하는 프로그램 또는 코드 블록이다. 인터럽트 서비스 루틴의 실행이 완료되면, CPU는 인터럽트되었던 연산을 재개한다.

image

인터럽트는 컴퓨터 구조의 중요한 부분이다. 각 컴퓨터 설계는 자신의 인터럽트 매커니즘을 가지고 있으며, 몇가지 기능은 공통적이다. 인터럽트는 적절한 서비스 루틴으로 제어를 전달한다. 이러한 전달을 관리하는 직선적인 방법은 인터럽트 정보를 조사하는 일반적인 루틴을 호출하는 방법이다. 이 루틴은 이어 인터럽트 고유의 핸들러(handler)를 호출한다.

하지만, 인터럽트는 매우 빈번하게 발생하기 때문에 빠르게 처리되어야 하고, 필요한 속도를 제공하기 위해서는 인터럽트 루틴에 대한 포인터들의 테이블을 이용하는 방법이 있다. 이 경우에는 중간 루틴을 둘 필요 없이, 테이블을 통하여 간접적으로 인터럽트 루틴이 호출될 수 있다. 일반적으로 포인터들의 테이블은 하위 메모리에 저장된다.(첫 100개정도의 위치) 이들 위치에는 장치에 대한 인터럽트 서비스 루틴의 주소가 들어 있다. 인터럽트가 요청되면, 인터럽트를 유발할 장치를 위한 인터럽트 서비스 루틴의 주소를 제공하기 위해 이 주소의 배열, 인터럽트 벡터가 인터럽트 요청과 함께 주어진 고유의 유일한 장치 번호호 색인된다.

인터럽트 구조는 또한 인터럽트된 모든 정보를 저장해야 인터럽트를 처리한 후 이 정보를 복원할 수 있다. 만약 인터럽트 루틴이 처리기의 상태를 변경할 필요(레지스터 값 변경 등)가 있다면, 인터럽트 루틴은 반드시 명시적으로 현재의 상태를 저장하여야 하며, 복귀하기 전에 상태를 복원해야 한다. 인터럽트를 서비스한 후, 저장되어 있던 복귀 주소를 프로그램 카운터에 적재하고, 인터럽트에 의해 중단되었던 연산이 언터럽트가 발생되지 않았던 것처럼 다시 시작된다.

기본 인터럽트 메커니즘

CPU 하드웨어에는 인터럽트 요청 라인(interrupt request line)이라는 선(wire)이 있는데, 이는 하나의 명령어의 실행을 완료할 때마다 CPU가 이 선(wire)을 감지한다. CPU가 컨트롤러가 인터럽트 요청 라인에 신호를 보낸 것을 감지하면, 인터럽트 번호를 읽고 이 번호를 인터럽트 벡터의 인덱스로 사용하여 인터럽트 핸들러 루틴(interrupt-hanlder routine)으로 점프한다. 그런 다음 해당 인덱스와 관련된 주소에서 실행을 시작한다.

인터럽트 처리기는 작업 중에 변경될 상태를 저장하고, 인터럽트 원인을 확인하고, 필요한 처리를 수행하고, 상태 복원을 수행하고, return_from_interrupt 명령어를 실행하여 CPU를 인터럽트 전 실행 상태로 되돌린다.

image

장치 컨트롤러는 인터럽트 요청 라인에 신호를 할당하여 인터럽트를 발생(raise) 시킨다. CPU는 인터럽트를 포착(catch)하여 인터럽트 핸들러로 디스패치(dispatch)하고 핸들러는 장치를 서비스하여 인터럽트를 지운다(clear).



대부분의 CPU에는 2개의 인터럽트 요청 라인이 있다.

마스크 불가능 인터럽트(nonmaskable interrupt)

마스크 불가능 인터럽트는 CPU가 무시할 수 없는 중요한 이벤트를 나타낸다. 예를 들어, 하드웨어 장치의 심각한 오류나 전원 공급 장애와 같은 상황에서 발생할 수 있다. 이러한 인터럽트는 CPU의 현재 작업을 중단하고 즉시 처리되어야 한다. 따라서 이러한 인터럽트는 프로그래머나 운영 체제가 무시하거나 마스크 처리할 수 없다. 대부분의 CPU 아키텍처에서는 NMI (Non-Maskable Interrupt) 라고도 불린다.

마스크 가능 인터럽트(maskable interrupt)

마스크 가능 인터럽트는 CPU가 인터럽트 요청을 무시하거나 나중에 처리할 수 있는 경우를 나타낸다. 이러한 인터럽트는 운영 체제나 프로그래머에 의해 마스크 처리될 수 있어서 CPU가 중요한 작업을 수행 중에 이러한 인터럽트를 무시하고 나중에 처리하도록 할 수 있다. 이러한 인터럽트는 주로 입출력 장치나 타이머와 같은 외부 장치 또는 프로그램 실행 중에 특정 조건이 충족되었을 때 발생할 수 있다.

마스크 가능 인터럽트는 우선 순위를 지정하여 여러 개의 인터럽트 중 어떤 것을 먼저 처리할지 결정할 수 있다. 또한 인터럽트 컨트롤러(IC)라고도 불리는 하드웨어 장치를 통해 여러 장치 간의 인터럽트를 관리하고 CPU에 전달할 수 있다.

스레드간 자원 공유를 조심히하지 않고 통제되지 않은 방식으로 하게 되면 난리가 난다. 특히 운영체제 커널에서 잘못하면 하드웨어 자체를 못쓰게 될 수도 있다.

인터럽트 비활성화(Disabling Interrupts)

가장 원시적인 동기화 방법은 인터럽트를 비활성화시키는 것이다. 다시 말해, CPU가 인터럽트에 응답하지 못하도록 일시적으로 막는 것이다. 인터럽트가 비활성화된 경우, 실행중인 스레드를 제외하고는 다른 스레드가 미리 예약되지 않으며, 스레드 미리 예약은 타이머 인터럽트에 의해 제어된다. 인터럽트가 활성화된 경우, 일반적으로 실행중인 스레드는 언제든지 중단될 수 있으며, 두 개의 C 문장 사이 또는 한 문장의 실행중에 중단될 수 있다.

부가적으로, 이것은 Pintos가 "preemptible kernel"이라는 것을 의미한다. 즉, 커널 스레드는 언제든지 중단될 수 있다. 전통적인 Unix 시스템에서는 "nonpreemptible"이며, 커널 스레드는 스케줄러에 명시젓으로 호출되는 지점에서만 중단될 수 있다.(사용자 프로그램은 두 모델 모두에서 언제든지 중단될 수 있다.) 예상했듯이, preemptible kernel은 더 명시적인 동기화가 필요하다.

일반적으로는 인터럽트 상태를 직접 설정할 필요는 거의 없다. 대부분의 경우 다음 섹션에서 설명할 다른 동기화 기본 요소를 사용해야 한다. 인터럽트를 비활성화하는 주된 이유는 커널 스레드를 외부 인터럽트 핸들러와 동기화하기 위한 것이다. 이러한 외부 인터럽트 핸들러는 슬립(sleep)할 수 없고, 대부분의 다른 동기화 형태를 사용할 수 없다.

일부 외부 인터럽트는 인터럽트를 비활성화해도 지연될 수 없다. 이러한 인터럽트는 "마스크 불가능 인터럽트(NMIs, Non-Maskable Interrupts)"라고 하며 주로 비상상황(예: 컴퓨터가 불타고 있는 경우)에서만 사용되어야 한다. Pintos는 마스크 불가능 인터럽트를 다루지 않는다.