본문 바로가기
연구하기/Computer Engineering

Interrupt 와 Exception

by 썰렁황제 2008. 11. 29.
  • Interrupt

    • 로우 레벨 시스템에서, 현재 진행중인 프로그램의 실행 흐름을 잠시 중지시키고 다른 것을 수행하게 하는 것
    • 일반적으로 현재 흐름이 suspend 되고, 인터럽트 부분에 기술된 주소로 점프하여 그 부분의 코드를 실행시킨다.
    • 인터럽트는 2가지 용도로 쓴다

      • 역방향 처리 흐름

        • 컴퓨터의 주변장치로부터 데이터를 읽고 쓰기 위해서 CPU 가 매번마다 일일이 주변 장치의 상태를 점검할 수는 없다.

          • 예를 들어 인터럽트 없이 키보드 입력을 처리하려 한다면 CPU 는 매 주기마다 일일이 키보드의 버튼 눌림 상태를 감지해야만 한다
        • 따라서 이럴 떄에는 CPU 가 상태를 점검하는 것이 아니라, 주변기기 쪽에서 어떤 상태가 발생했을 때 그것을 알리는 방식으로 가는 쪽이 여러모로 훨씬 유리하다
        • 이런 방식은 실행 흐름의 주도권을 주변기기가 가지므로 CPU 쪽이 주도권을 가지는 일반 처리와는 반대의 방향이라고 생각할 수 있다.
      • 예외

        • 예외의 좋은 예는 divide by zero 이다.
        • 왜 divide by zero 에 대해서 별도의 결과를 주지 않고 Interrupt 를 발생시켰을까?

          • divide by zero 라는 상황을 반환할 방법이 없었기 때문이다.

            • 결과 레지스터가 나타낼 수 있는 모든 수치는 모든 연산의 결과와 동일한 집합을 가진다. 따라서 divide by zero 를 나타낼 수 있는 범위가 존재하지 않는다.
            • 만약 무한대를 나타낼 수 있는 방법이 있다면 가능했겠지만, 실수라면 몰라도 정수형에서는 마땅한 표현방법이 없다.
            • 별도 레지스터를 통해 이를 보고한다고 한다면, 매 나눗셈 연산마다 이 값을 체크해서 확인해야 하므로 나눗셈 연산에 오버헤드를 발생시키는 결과를 가져온다.
          • 따라서 divide by zero 를 나눗셈 연산의 예외로 취급하여 아예 별도의 실행을 수행하게 만든 것이다.
        • 예외와 Interrupt 간의 관계는 개념과 구현수단의 차이이다.

          • 예외와 Interrupt 는 근본적인 부분에서는 다른 개념이다.
          • 하지만 기본 흐름과는 다른 상황을 처리한다는 점에서 접점이 존재하고, 그래서 Interrupt 는 예외를 구현하기 위한 수단이 될 수 있다.
          • 즉, 예외와 Interrupt 는 서로간에 교집합을 가지는 서로 다른 범위를 가지는 집합이라 볼 수 있다.

  • Exception : 예외

    • 프로그램 작성 시 예측하지 못했던 상황을 표현하기 위한 개념
    • 예측하지 못했던 상황을 어느 수준까지 만들어두느냐가 예외를 기술하는데에 있어서 중요한 부분이다.

      • 왜냐하면, if 를 통하여 예외라고 생각할 수 있는 상황들의 대부분을 원래의 흐름에서 제어할 수 있기 때문이다.

        • 예를 들면 숫자만 입력받는 곳에서 문자가 들어왔을 경우, 파싱 시에 미리 이를 알아내고 분기처리를 수행할 수 있다.
      • 실제로 c 의 경우, 근본적으로 예외는 사용자가 직접 기술하는 것으로만 발동하며, 즉 이는 코드 작성이라는 측면에서는 사실상 동일한 수준을 가진다.
    • 그럼 왜 예외를 쓰는가?

      • 일반적으로 어떤 구문을 처리하는 도중 여러가지 분기가 존재할 수 있는데, 특히 이 중에서 특정 블럭의 코드 흐름에 많이 벗어나는 상황이 존재할 수 있다.

        • 숫자 2개를 입력받아 덧셈 연산을 하는 프로그램을 생각해 보자. 입력 시 문자가 들어왔다면, 덧셈 연산 부분은 아무런 쓸모가 없다.
      • 예제로 든 것과 같이 작은 코드 블럭이거나, 혹은 큰 코드 블럭이라도 단일 계층이라면 if 로 분기가 가능하므로 어렵지 않다.
      • 하지만, 우리가 흔히 사용하고 있는 동적 프로시저 호출 방식을 사용하여 상당한 양의 스택이 쌓였을 때, 발생했던 상황이 이렇게 적층된 프로시저들의 흐름과 다르다면, 즉 그 뒷부분의 처리코드들을 전혀 사용할 수 없어, 원래의 흐름으로 복귀할 수 없는 경우 어떻게 처리해야 할까?

        • 고전적인 방식이라면, 각 호출 프로시저에 대해서 일일이 잘못된 상황이 발생했음을 알리는 메시지를 전달할 수단 (일반적으로 반환값의 일부 영역) 을 만들고 이 반환값을 전달하는 방식으로 처리하게 된다.
        • 하지만 이 방식은 너무나 번거로우며, 모든 범위를 예측해 기술하기에는 인간의 능력상으로도 한계가 있고 프로시저가 메시지를 전달할 수단이 일반적인 프로시저 구조의 언어에서는 너무 부족하다.

          • 반환값의 일부 영역, 전역공간이나 call by reference, call by result 등을 사용한 전달이 그 방법 중 하나이나, 어느 것이나 프로시저를 너저분하게 만든다.
        • 게다가 만약 상위 호출 프로시저가 이미 그러한 부분을 고려하지 않고 만든 프로시저인데, 호출당하는 프로시저는 자신이 작성한 코드이고 예외 상황이 발생할 것이 확실하다면 어떻게 해야 할까?
        • 즉 이러한 예외 상황을 기술하는 것은 프로시저의 기본 구조와는 독립되어 기술되어야 할 필요성이 있음을 알 수 있다.
      • 이러한 이유로 만들어진 것이 바로 예외이다.
    • 어디까지 예외로 취급해야 하는가?

      • 많은 경우 예외는 코드 작성자의 기술에 의해 이루어지므로, 결국 어느 수준까지를 예외로 처리해야 하느냐 하는 고민을 해야만 한다.
      • 이에 대한 명쾌한 해답은 존재하지 않지만, 다음과 같은 상황은 반드시 고려대상에 넣어두어야만 한다. 사실상 대부분이 위에 설명한 상황이다.

        • 기 작성된 코드에 의해서 호출되는 코드를 작성해야 하는데, 기 작성된 코드에 별도의 예외 시스템이 존재하지 않고, 지신은 다시 이러한 예외 상황을 핸들링해야 하는 경우, 이를 처리할 수 있는 유일한 수단은 예외 뿐이다

          • 예를 들면, GUI 이벤트 핸들링과 같은 경우가 아주 좋은 예이다.
        • 발생한 상황이 현재의 코드 흐름과 너무나 벗어나 이후 처리해야 하는 코드들의 수행을 대부분 포기해야 하는 경우, 이 코드들이 많은 프로시저 호출에 겹쳐 걸쳐 있었을 때에는 단순 분기나 반환값 할당 방식으로 처리하기에는 너무 어려우므로 예외를 발생시켜 예외 핸들링 코드로 일거에 탈출하게 한다.
      • 시스템에서 발생시키는 예외 (로우레벨에서 divide by zero, JVM 에서 제공하는 기본적인 시스템 예외 등) 들은 당연히 예외로 처리하는 것이 좋다.

이 글은 스프링노트에서 작성되었습니다.

  스프링노트에서 작성한 건데 영 호환성이 쩝...
  예외와 인터럽트에 대해서는 좀 더 정리해야 할 필요성이 있다고 본다. 아직 내가 잘못 이해하거나 제대로 이해하지 못한 부분이 많기도 하고...
  더불어 이전에 썼던 예외 관련 글도 수정해야 할 필요성이 있고, 너무 글만으로 이루어져 있어서 인식에도 문제가 있다.

  프로그래밍에 관련된 개념들 대부분은 글보다 그림이 훨씬 직관적인 경우가 많다. 아무래도 앞으로는 그런 방향으로 글을 써 봐야 할 듯.

반응형