'OS concept'에 해당되는 글 12건

  1. 2005.02.12 [세마포어] [뮤텍스]
  2. 2005.01.19 인터럽트에 대해서

[세마포어] [뮤텍스]

동기화에 필요한 기술들

데드락 걸렸을 때 사용하는 게 아니고 세마포어나 뮤텍스에 의해 데드락이 걸릴 수도 있습니다. 따라서 꼭 필요한 부분에만 제대로 사용해야합니다.


뮤텍스란 MUTual EXclusion으로 우리말로 해석하면 '상호 배제'라고 합니다. 말 그대로 상호 배제해서 실행하는 겁니다. 임계 구역을 가진 스레드들이 동시에 실행되지 않고 서로 배제되어 실행되게 하는 기술입니다. 여기서 임계구역(Critical Section)란 프로그램 상에서 동시에 실행될 경우 문제을 일으킬 수 있는 부분을 말합니다. 만약 어느 스레드에서 임계구역을 실행하고 있으면 다른 스레드들은 그 임계 구역에 접근할 수 없고 앞의 스레드가 임계 구역을 벗어나기를 기다려야합니다. 이런 방법이 뮤텍스입니다.

그리고 세마포어란 임계구역에 접근 하기 위한 열쇠 같은거라고 생각하면 됩니다. 예를 들어 화장실이 있다고 합시다. 이 화장실에 들어가기 위해서는 반드시 열쇠가 있어야 하고 이 열쇠는 단 하나 밖에 없으며 화장실에는 한명씩만 들어갈 수 있습니다. 이럴 때 여러명의 사람이 화장실을 이용하려면 일단 한명이 열쇠를 가지고 화장실을 이용하고 나오면 그 사람에게 열쇠를 받아 다음 사람이 이용하고 이렇게 한사람씩 돌아가면서 화장실을 쓸 수 있습니다. 이때 화장실은 임계구역이 되고 열쇠는 세마포어가 되는겁니다. 그리고 누군가 열쇠를 가지고 도망가버린다면 아무도 화장실을 사용하지 못하는 상태가 되어버립니다. 이런게 바로 데드락입니다. 데드락을 CPU 스케쥴링에서는 교착상태라고합니다.

 

세마포어(SEMAPHORE)

프로그래밍, 특히 유닉스시스템의 프로그래밍에서 세마포어는 운영체계의 자원을 경쟁적으로 사용하는 다중 프로세스에서, 행동을 조정하거나 또는 동기화 시키는 기술이다. 세마포어는 운영체계 또는 커널의 한 지정된 저장장치 내 값으로서, 각 프로세스는 이를 확인하고 변경할 수 있다. 확인되는 세마포어의 값에 따라, 그 프로세스가 즉시 자원을 사용할 수 있거나, 또는 이미 다른 프로세스에 의해 사용 중이라는 사실을 알게되면 재시도하기 전에 일정 시간을 기다려야만 한다. 세마포어는 이진수 (0 또는 1)를 사용하거나, 또는 추가적인 값을 가질 수도 있다. 세마포어를 사용하는 프로세스는 으레 그 값을 확인하고, 자원을 사용하는 동안에는 그 값을 변경함으로써 다른 세마포어 사용자들이 기다리도록 해야한다.

세마포어들은 일반적으로 메모리 공간을 공유하거나, 또는 파일들을 공유 액세스하기 위한, 두 가지 정도의 목적을 위해 사용된다. 세마포어들은 프로세스간 통신(IPC)을 위한 기술 중하나이다. C 프로그래밍 언어는 세마포어들을 관리하기 위한 일련의 인터페이스 또는 함수들을 제공한다.

이 세마포어를 가지고 경쟁관계에 있는 자원들을 충돌없이 잘 사용하기 위함입니다. 공유자원의 경우 다른 프로세스가 동시에 접근을 한다면, 어느 프로세스에 의해 변경된 자료가 나중에 수정된 자료인지 모르게 되는 문제가 생기며, 이로 인해 데이터의 각종 오류가 산재하게 됩니다. 그래서 세마포어 라는 일종의 변수를 두어 제어를 하게 됩니다.

 

세마포어

- 동기화의 일반적인 방법인 세마포어 방법은 세마포어라는 정수 변수(integer variable), 프로세스 대기열(process waiting queue), P와 V의 두 명령으로 구성된다. 초기 상태의 변수값은 자원의 수와 같으며 대기열은 비어 있다. P명령은 변수의 값을 하나 줄인 후, 변수의 값이 0보다 작으면 프로세스를 대기열로 집어 넣는다. 반대로 0보다 크면 그 프로세스는 계속 진행된다. V명령은 변수의 값을 하나 증가시킨다. 그 결과가 0보다 크면 프로세스는 계속되며 0보다 작으면 대기열의 프로세스 하나를 준비 상태로 만들고, 프로세스의 수행은 계속된다.  결국 변수의 값은 음수일 경우는 대기 중인 프로세스의 수를 나타내며, 양수이면 사용 가능한 자원의 수를 가리킨다. 위에서 동기화란 프로세스의 실행을 시간에 따라 순서적으로 처리하는 것을 동기화라 한다.

- 세마포어는 다익스트라(E.J.Dijkstra)가 제안한 동시에 정보를 공유하여 수행되는 두 개 이상의 프로그램이나 프로세스에서 활동 (activity)의 위치(coordination)를 설정해 주는 데 사용되는 동기화를 위한 기본 조작. 이는 두개 이상의 프로세스에 의해 공유되는 고유변수로 정의되는데, 보통의 방법으로는 다룰 수 없고 항상 P와 V라는 연산을 통해서만 액세스할 수 있다. 세마포어 sem이란 다음과 같은 연산이 허용된 정수형 변수를 말한다. (P와 V란 이름은 wait와 signal이란 말의 네덜란드어에서 나온것으로 이때 signal이란 물론 UNIX의 signal호출과는 다르다.) 두 연산은 모두 원자화되어야 한다. 즉 sem을 변경할수 있는 프로세스는 한 순간에 오직 하나 뿐이다.

 

컴퓨터가 여러 프로그램을 동시에 수행하는 다중 프로그래밍 시스템에서는 프로세스들간의 상호 배제와 동기화를 위한 기본적인 연산이 필요하다. 세마포어는 다익스트라가 제안한 프로세스 동기화를 위한 구조로, 이는 여러 프로세스들에 의해 공유되는 변수로 정의된다. 그런데 이 변수는 보통의 방법으로는 액세스할 수 없고 오직 P와 V라는 연산으로만 다룰 수 있다. P와 V연산의 정의는 다음과 같다. 

 

procedure P(S)   --> 최초 S값은 1임

    while S=0 do wait  --> S가 0면 1이 될때까지 기다려야 함

    S := S-1   --> S를 0로 만들어 다른 프로세스가 들어 오지 못하도록 함

end P

 

procedure V(S) --> 현재상태는 S가 0임

    S := S+1   --> S를 1로 원위치시켜 해제하는 과정. 이제는 다른 프로세스가 end V 들어 올수 있음

 

P와 V는 쪼갤수 없는 단일 연산이다. 즉 한 프로세스가 P나 V를 수행하고 있는 동안에는 프로세스가 인터럽트를 당하지 않는다. 이제 P와 V를 사용하면 다음과 같이 위험지역(cirtical section)에 대한 상호배제를 구현할수 있다. 

 

    P(S);

    -----------------
    | 위 험 지 역       |
    ----------------- 

    V(S);                                                                           |

최초에 S의 값은 1이다. 위와 같은 위험지역을 포함하는 두개의 프로세스 A와 B가 있다고 하자. A와 B는 서로 독립적으로 수행되지만, 두 프로세스가 동시에 위험 지역으로 들어가서는 안된다. 위와 같이 세마포어를 사용하면 P(S)를 먼저 수행하는 프로세스가 S를 0으로 해놓고 위험지역에 들어가므로 나중에 도착하는 프로세스는 P에서 더이상 진행되지 못하고 기다리게 된다. 먼저 들어갔던 프로세스가 V(S)를 해주어야 비로서 P(S)에서 기다리던 프로세스가 위험지역에 들어갈 수 있고 따라서 상호배제가  실현된다. 위의 예는 이진 세마포어 (binary semaphore)로, 단지 하나의 프로세스만이 위험지역에 들어갈 수 있도록 한다. 한편 S의 초기값을 N으로 하면 최대 N개의 프로세스가 P(S)를 통과할 수 있게 되는데 이러한 경우에는 계수 세마포어 (counting semaphore)라 하며 자원 할당에 사용한다. 

UNIX 시스템 V에서 구현된 세마포어는 이러한 개념에 기초했지만 보다 일반적인(그리고 아마도 보다 복잡한)기능을 제공한다. 

우선 semget과 semctl을 살펴보자.

        <사용법>
         #include
         #include
         #include
   
         key_t key;
         int sem_id, nsems, permflags, command;
         int retval, sem_num;
         union semun

         {
               int val;
               struct semid_ds *stat;
               ushort *array;
          } ctl_arg;
                .
                .
                .
         sem_id = semget(key, nsems, perflags);
         retval = semctl(sem_id, sem_num, command, ctl_arg);

 

semget 호출은 msgget(get message queue)과 유사하다. 인수 nsems는 세마포어 집합에 필요한 세마포어의 갯수를 나타낸다. 따라서 UNIX 세마포어 연산은 세마포어 하나만이 아니라 한 집합 전체를 다루게 된다. 이로 인해서 나머지 세마포어 루틴에 대한 인터페이스가 복잡해진다. semget호출이 성공하면 세마포어 집합 식별자라는 메시지 큐 식별자와 유사한 역할을 하는 것이 돌아온다. C 언어의 관습을 따라서 세마포어 집합에 대한 첨자는 0부터 nsems-1까지 있을 수 있다. 집합 내의 각 세마포어는 다음과 같은 값들을 갖게 된다. 

       . semval : 세마포어의 값으로서 항상 양수가 지정된다.
                  여기에 값을 새로 지정하려면 반드시 세마포어 시스템 호출을 통해야하며 프로그램에서 일반 자료형의 변수와 같이 직접
                  접근 할 수는 없다.
       . sempid : 세마포어에 접근했던 최근의 프로세스의 프로세스 식별번호이다.
       . semncnt : 세마포어의 값이 현재보다 증가하기를 기다리는 프로세스의 갯수
       . semzcnt : 세마포어의 값이 0으로 되기까지 기다리는 프로세스의 갯수


정의에서 알 수 있듯이 함수 semctl은 msgctl보다 훨씬 복잡하다. sem_id는 유효한 세마포어 식별자라야 한다. command는 msgctl에서와 같이 수행해야 할 정확한 기능을 명시한다. 이 기능에는 세가지 유형이 있다. IPC_STAT와 같은 표준 IPC기능, 단일 세마포어만을 다루는 기능, 세마포어의 전체를 다루는 기능이 그것이다. 

 

가능한 모든 기능을 다음과 같이 정리하였다.
                   
                   semctl(semaphore control operations) 기능 코드
   -----------------------------------------------------------------------------
         표준 IPC기능 (semid_ds구조는 sem.h에 정의되어 있다.)
   -----------------------------------------------------------------------------
         IPC_STAT  상태정보를 ctl_arg.stat에 저장한다.
         IPC_SET   ctl_arg.stat에 저장된 형태로 소유권과 사용 허가권을 지정한다.
         IPC_RMID  시스템에서 해당 세마포어의 집합을 삭제한다.
   -----------------------------------------------------------------------------
         단일 세마포어 연산 (이들은 retval에게 넘어온 값 sem_unm을 사용한다.)
   -----------------------------------------------------------------------------
         GETVAL    세마포어의 값 semval을 돌려준다.
         SETVAL    세마포어 값을 ctl_arg.val로 지정한다.
         GETPID    sempid의 값을 돌려준다.
         GETNCNT   semncnt를 돌려준다.
         GETZCNT   semzcnt를 돌려준다.
   -----------------------------------------------------------------------------
         전체 세마포어 연산
   -----------------------------------------------------------------------------
         GETALL    모든 senvals의 값을 ctl_arg.array에 저장한다.
         SETALL    ctl_arg.array의 값을 사용하여 모든 semvals값을 지정한다.
   ----------------------------------------------------------------------------- 
   
sem_num 인수는 semctl의 단일 세마포어 기능에서 특정 세마포어를 지정해준다. 마지막 인수인 ctl_arg는 세가지 구성 요소의 결합이다. 이들 구성 요소들은 각각 semctl의 세가지 기능에 대응한다. semctl은 세마포어의 초기값을 지정할 때 요긴하게 사용된다. 이 기능은 semget에는 없음에 주의하라. 따라서 semget과 semctl은 두개 모두 있어야 한다. 다음에 예로서 제시하는 함수는 프로그램이 단일 세마포어를 생성할때나 이에 대한 세마포어 집합 식별자를 얻고자 할 때 사용될 수 있다. 세마포어가 생성되는 경우에는 semctl을 사용하여 초기값을 부여하게 된다.

 

   /* initsem  -- semaphore initialization */
    #include "pv.h"
   

    initsem(semkey)   

    key_t semkey;
    {
          int status = 0, semid ;
          if ((semid = semget(semkey, 1, SEMPERM|IPC_CREAT|IPC_EXCL)) == -1)

          {
               if (errno == EEXIST)
                   semid = semget(semkey, 1, 0);
          }

          else   /* if created... */
               status = semctl(semid, 0, SETVAL, 1);

        if ((semid == -1 || status == -1)

        {
             perror("initsem failed");
             return (-1);
        }

        else
             return semid;    /* all okay  */
     }

 

  include 화일 pv.h는 다음과 같다.

    /* semaphore example header file */
    #include
    #include
    #include
    #include

    extern int errno;

    #define SEMPERM 0600
    #define TRUE 1
    #define FALSE 0

 

세마포어 연산 : semop 호출
semop은 기본적인 세마포어 연산을 실제로 수행하는 시스템호출이다. 이때 semop은 메뉴얼의 항목이 아니라 실제 함수 이름이다.


   사용법
      #include
      #include
      #include
      int retval, sem_id;
      struct sembuf op_array[SOMEVALUE];
               .
               .
      retval = semop(sem_id, op_array, SOMEVALUE);

 

sem_id는 세마포어 집합 식별자로서 이전에 semget호출을 통해 값이 지정되어야 한다. op_array는 sembuf구조의 배열로서 sembuf구조는 sem.h에 정의되어 있다. SOMEVALUE는 임의의 정수형 상수이다. 각각의 sembuf구조 변수는 세마포어에 대해 수행할 연산을 지정한다. 다시 강조하거니와 semop함수가 세마포어 집합에 대해 수행하는 일련의 연산들은 모두 원자화 되어야 한다. 즉, 그중의 한 연산이라도 수행할 수 없다면 전체 연산이 모두 수행되지 말아야 한다. 이 경우에는 특별히 명시되지 않는 한, 모든 연산이 한번에 수행될수 있을때까지 프로세스의 수행이 중지된다. sembuf구조를 좀더 자세히 보면 다음과 같이 구성되어 있다.
 
      --------------------------------------------------------------------
      |     struct sembuf {                                                                         |
      |     short   sem_num;        /* semaphore # */                                    |
      |     short   sem_op;         /* semaphore operation */                          |
      |     short   sem_flg;        /* operation flags */                                    |

      |     }                                                                                             |
      --------------------------------------------------------------------

 

sem_num는 집합 내의 세마포어에 대한 첨자를 저장한다. 만약 집합의 원소가 하나뿐이라면 sem_num의 값은 0이어야 한다.  sem_op는 함수 semop이 수행해야 하는 기능을 정수로서 나타낸다. 

여기에는 세가지 경우가 있다.

 

경우 1 : sem_op가 음수일때

이 경우에는 앞서 소개했듯이 일반적인 세마포어 명령 P()와 같이 수행된다. 이를 의사 코드(pseudo-code)로 나타내면 다음과 같다.(ABS는 변수의 절대값을 나타낸다)

 

        if (semval >= ABS(sem_op))

        {
             set semval to semval-ABS(sem_op)
        }

        else

        {
             if ((sem_flg&IPC_NOWAIT))
                     return-1 immediately
             else

             {
                    wait until semval reaches or exceeds
                    ABS(sem_op), then subtract
                    ABS(sem_op) as above
              }
        }

기본 개념은 함수 semop에서 sem_num이 가리키는 세마포어의 값 semval을 조사하는 것이다. semval의 값이 충분히 크다면 즉시 하나 감소시킨다. 아니면 semval이 충분히 커질 때까지 프로세스의 수행을 중단시킨다. 그러나 sem_flg의 IPC_NOWAIT 플래그의 값이 1로 되어 있으면 sem_op은 즉시 -1을 되돌려주고 errono값을 EAGAIN으로 한다.

 

경우 2 : sem_op이 양수일 때

이때는 보통의 V()연산과 유사하다. 즉 sem_op의 값을 해당 semval에 더해준다. 이때 해당 세마포어의 값이 증가하기를 기다리는 프로세스들이 깨어나게 된다.
   
경우 3 : sem_op이 0일때

이 경우는 semval을 변환시키는 것이 아니라 값이 0이 될때까지 기다린다. semval이 0이 아니고 sem_flg의 IPC_NOWAIT가 1인 경우에 semop는 즉시 오류 값을 돌려주게 된다.
        
SEM_UNDO 플래그

이것은 sembuf구조의 구성요소 sem_flg에 있는 플래그의 하나이다. 이는 프로세스의 수행이 끝났을 때 시스템이 수행된 연산을 자동적으로 취소하도록 지시한다. 수행된 일련의 연산을 추적하기 위하여 시스템은 세마포어에 semadj라는 정수를 대응시킨다. 이때 semadj변수는 프로세스마다 할당되어야 함에 주의해라. 따라서 서로 다른 프로세서는 동일한 세마포어에 대해 독립적인 semadj값을 유지하게 된다. SEM_UNDO의 값을 1로 하고서 semop연산을 수행하면 semadj값에서 sem_num값을 뺀다. 이때 sem_num의 부호가 중요한데 이는 sem_num의 값이 양수인가 음수인가에 따라 semadj의 값이 감소하거나 증가하기 때문이다. 프로세스의 수행이 끝나면 시스템은 semadj값을 해당 세마포어에 더해줌으로써 지금까지의 모든 semop호출 효과를 상쇄시킨다. 일반적으로 볼때 프로세스가 지정한 값이  해당 프로세스의 종료 후에도 효력을 갖지 않는다면 SEM_UNDO가 사용되어야만 한다.

 

세마포어의 예

이제 initsem루틴으로 시작한 예를 완성해 보자. 여기서는 전통적인 세마포어 연산을 P()와 V()로 구현하여 이를 중심으로 삼았다. 우선 P()를 보자.

       /* pc -- semaphore p operation */
       #include "pv.h"
       p(semid)
       int semid;
       {
           struct sembuf p_buf;
           p_buf.sem_num = 0;
           p_buf.sem_op = -1;
           p_buf.sem_flg = SEM_UNDO;
         
           if (semop(semid, &p_buf, 1) == -1)

           {
               perror("p(semid) failed");
               exit(1);
           }

           else
               return(0);
        }

 

이때 SEM_UNDO를 사용했음에 주의하라. V()는 다음과 같다.
     
        /* v.c -- semaphore v operation */
        #include "pv.h"
        v(semid)
        int semid;
        {
            struct sembuf v_buf;
            v_buf.sem_num = 0;
            v_buf.sem_op = 1;
            v_buf.sem_flg = SEM_UNDO;
 
            if(semop(semid, &v_buf, 1) == -1)

            {
                perror("v(semid) failed");
                exit(1);
            }

            else
                return(0);
         }

 

이제 비교적 간단한 이들 루틴으로 상호 배제를 구현해보자. 다음의 프로그램을 살펴보자.

 

         /* testsem -- test semaphore routines */
         #include "pv.h"
        
         main()
         {
              key_t semkey = 0x200;
             
              if(fork() == 0)
                 handlesem(semkey);

              if(fork() == 0)
                 handlesem(semkey);

              if(fork() == 0)
                 handlesem(semkey);
          }
            
          handlesem(skey)
          key_t skey;
          {
              int semid, pid = getpid();

              if((semid = initsem(skey)) < 0)
                  exit(1);
              printf("\nprocess %d before critical section\n", pid);
       
              p(semid);
              printf("process %d in critical section\n", pid);
            
              /* in real life do something interesting */
              sleep(10);
              printf("process %d leaving critical section\n", pid);

              v(semid);
              printf("process %d exiting\n", pid);
              exit(0);
           }

testsem은 세개의 자식프로세스를 생성하고 , 이들은 p()와 v()를 사용하여 임계 영역에는 한 순간에 둘 이상이 들어있지 못하도록 한다. 한 컴퓨터에서 testsem을 수행시킨 결과는 다음과 같다.

 

              process 799 before critical section
              process 800 before critical section
              process 801 before critical section

              process 799 in critical section
              process 799 leaving critical section
              process 799 exiting
              process 801 in critical section
              process 801 leaving critical section
              process 801 exiting
              process 800 in critical section
              process 800 leaving critical section
              process 800 exiting

 

o Critical secion 란 ?

다중 프로그래밍 운영체제에서 여러 프로세스가 데이타를 공유하면서 수행될 때 각 프로세스에서 공유 데이타를 액세스하는 프로그램 코드 부분을 가리키는 말. 공유데이타를 여러 프로세스가 동시에 액세스하면 시간적인 차이 때문에 잘못된 결과를 만들어 낼 수 있기 때문에 한 프로세스가 위험 부분을 수행하고 있을 때, 즉 공유 데이타를 액세스하고 있을 때는 다른 프로세스들은 절대로 그 데이타를 액세스하지 못하도록 하여야 한다.

o Mutual exclusion (상호 배제)란(1) ?

- 프로세스의 상호 교신에 대한 기본적인 조치는 공용 부분을 여러 프로세스가 동시에 사용하는 것을 배제하는 것이었다. 시스템의 어떠한 자원을 한 시점에서 한개의 프로세스만이 사용할 수 있도록 하는 것을 상호배제라 한다. 또한, 프로그램에서 이러한 자원을 사용하거나 혹은 그 내용을 변경하는 부분을 위험부분 (critical section)이라 하며, 둘 이상의 프로그램에서 이 위험 부분이 동시에 수행되지 않도록 하는 것이 상호 배제의 기능이다. 

- 다중 프로그래밍 시스템에서 여러 프로세스가 하나의 공유 데이타를 액세스하면서 작업을 할 때, 한 프로세스가 그 데이타를 액세스할 때는 다른 프로세스들은 그것을 사용하지 못하도록 하는 운영체제의 기능. 예를 들어 한 프로세스가 어떤 화일에 데이타를 쓰고 있을 때 다른 프로세스가 그 화일을 지원버린다면 많은 문제가 발생할 것이다. 상호 배제는 한번에 한 프로세스만이 공유 데이타를 액세스할 수 있도록 해 주는 것으로, 다중프로그래밍 시스템의 운영체제가 꼭 갖추어야 할 기능이다.

Posted by '김용환'
,
제 목 : 인터럽트에 대해서
작성자: 캠퍼스 C
출 처 : 나우누리
U R L : http://explore.kwangwoon.ac.kr/~k98el560
기 타 : 여기에 게재되는 모든 글들은 이미 작성자의 동의를 얻은 것이기
        때문에 상업적 목적이 없는 한 출처(이 홈페이지)를 밝히고 다른
        곳에 게재할 수 있습니다.
======================================================================
*******************************************************************
<중급>                  < 캠퍼스 C >                            4회
*************************<  목 차  >*******************************
<1> 인터럽트(interrupt)의 개념
<2> 인터럽트(interrupt)와 폴링(polling)
<3> 인터럽트 벡터 테이블(vector table)을 확인하자
<4> 인터럽트의 종류
<5> 인터럽트를 위한 하드웨어의 구현(IBM-PC 에서)
<6> 하드웨어 인터럽트의 순서.
<7> C 에서 제공하는 인터럽트 함수 (turbo C 에서)
<8> 인터럽트의 작동을 눈으로 확인해 보자.
<9> 인터럽트를 이용하여 시계를 만들어 보자
************************< 내용 시작 >******************************
<1> 인터럽트(interrupt)의 개념
-------------------------------------------------------------------
        영어로 "inter" 라는 의미는 "중간에, 사이에서" 라는 뜻이다.
그래서 잘아시다시피, 농구에서 패스하는 공을  가로채는 것을 "인터셉트
(intercept)" 라고 한다.
        마찬가지로 지금 우리가 공부하려는 "인터럽트(interrupt)"도
"중간(inter)에서 탁 깨뜨려서(rupt) 흐름을 바꿔 놓는다"는 뜻이다.
        지금 이 강좌를  듣는 분 중에는 나름대로 C의  기초를 공부하고
온 분들이겠지만 컴퓨터안에 "흐름"이 있다는 것을 모두  알고 있는지
궁금하다.
        그 흐름이란 80x86  CPU내부에 있는 "IP" 라는  레지스터의 값의
변화를 말한다.
        IP 하니까 생각나는게  있다. 천리안,하이텔  같은  회사에 근무
하는 분들은 이 IP란 용어를 참 많이 사용한다.
        "저 사람 IP 야"
글쎄 "JP" 라면 누군지 금새 알 것도 같은데 "IP"는 누군지 잘모르겠다.
          여기서 얘기하는 "IP"란  (Information Provider)란 뜻으로 "
정보를 제공하는 사람"이라는 뜻이다. 이를테면  사주팔자 같은 데이타베
이스를 천리안,나우콤같은 업체에 제공하는 사람들이 "IP"인 것이다.
        그러나 지금  우리가 얘기하는, (똑같은 이름의)  "레지스터 IP"
는 (내가 보기엔) 그것보다 훨씬 더 중요한 의미를 가지고 있다. 여러분이
소위 "프로그램을 배우겠다"는 사람이면 이 IP의 흐름을 손바닥 보듯이
훤하게 보고 있어야만 한다. 그것은 마치 "혈액 순환계"를 공부하는 의사
들이, "심장에서 나온 피가 어디로 돌고 있는지"를 훤하게 알고 있어야 하는
것과 마찬가지이다.
        IP 는 (Instruction Pointer)의  준말이다. C 기초 강좌에서
"포인터"를 공부한  사람이라면 이 레지스터의 내부에 들어갈  값 (예를
들면 0x1234 따위)가 무엇을 의미하는지 짐작이 가실 것이다.
        이 IP의 역할은,  "다음번에 수행해야 할 명령어의  번지수를 가
리키는것"이다. 이 강좌는 그래도 "중급강좌"인데  이런 자질구레한 얘기
를 쓸 필요가 있는지 잘 모르겠지만, 그래도 혹시나 하는 마음에 적어본다
        이  IP의 기능을 생생하게 느끼실려면 "국민은행"에 가보시면 잘
알 수 있다.  지방 국민은행에도 이런 시스템이  되있는지 잘  모르겠지만
국민은행에서 일처리를 하려면  들어가자마자 무조건 "대기번호" 표를 뽑고
전광판에 내 번호가 나타나기를 "멍하니" 서서(혹은 앉아서) 기다려야 된다.
        수시로  "띵동" 하는  소리와  함께  전광판의 숫자는  39,  40,
41,42, 하는 식으로  증가한다. 두말할 것도 없이 "39번  손님 앞으로.."
하는 안내이다.
        IP의 역할도 전광판과 똑같다. IP가  현재 "0x0039"를 가지고 있
으면 그 주소에 있는 코드를 읽어다가 수행을  한다는 뜻이다. 그리고 이
연산이  끝나면 IP의  값은 역시  "자동적"으로 증가  된다. 즉  0x0039,
0x0041 하는식이다.(여기서는 꼭 1씩 증가 하지는 않음).
        인터럽트의 개념은  이렇다. 지금 전광판이 38번을  가리키고 있
다. 그리고 다음이 내 번호인 39가  될 차례이다. 그런데 갑자기  번호가
999번으로 바뀐다. 그러더니  뒤에 앉은 더 높은 은행원이  "999번" 표를
흔들며 나타나서 먼저 돈을 찾아간다.  "아니 뭐  저런 x가 다있어 ? "하
고 항의를 하려는 순간, 이제서야 전광판에 내 번호인 39번이 나타난다.
        아시다시피 이것은  일종의 새치기에  해당한다. 그러나  이것이
실제로 컴퓨터  내부에서 일어나고 있는  인터럽트의 기본 개념이다.  왜
정직한 컴퓨터가 이런 새치기를 허용하는지,  의문을  갖는 분도 있을 것
이다.
      그러나 알고 봤더니 999번의 은행원이  찾아간 돈은 급한 응급환자
의 수술비 라고 한다...
        이런 기본적인  인터럽트의 개념 외에  반드시 알아야 할  것은,
"인터럽트는  nesting(중첩)도 허용된다"는 사실이다.  이런 설명을 위해
서 아래에 (중급 독자를 실망시킬 지 모르는) 유치한 예를 들었다.
        예를 들면, 지금  "이 팔육" 이라는 학생이 고등학교 2  학년 이
라고 치고, 같은  반 친구와 "야구 얘기"를 나누고 있다고  하자. 그런데
갑자기 3 학년 학생이 말을 걸어오면 "이  팔육"은 일단 친구와의 얘기를
잠시 중단하고 선배인 3학년하고 얘기를 시작 해야 한다.
        그러는 도중에 갑자기  선생님이 말을 걸어 온다면  여러분은 어
떻게 할 것인가  ? 정신 나간 학생이 아닌 다음에야  당연히 하던 얘기를
멈추고 선생님과 얘기를 시작 할 것이다.
<문제>  그러다 이번엔 교장 선생님이 말을 걸어오면 ?
        위의 문제는  "완전히" 정신 나간  문제인것 같다. 이런  당연한
문제는 낼 필요가 없는 것 같은데, 나온김에 하나만 더 내보자.
<문제> 교장 선생님과  하던 얘기가 끝이났다. 이번엔 누구와  다시 얘기
를 나누어야 하나 ?
        역시 이 문제도 정신이 나간  문제인데, 당연히 선생님과 얘기를
한다. 그러다 또 선생님과 얘기가 끝나면 다시  3 학년 학생과 얘기를 한
다. 그리고 또  3학년 학생과 얘기가 끝나면 이제 비로소  2 학년 친구와
하던 "야구 얘기"를 계속 할 수 있다.
        하두 인터럽트가 많이  걸려서 "이 팔육"은 잠시  생각을 해봐야
할 것이다. "몇회전 까지 공을 던졌던가 ?"를...
        나중에도 자세히  나오겠지만, 이런 "문맥(context) 전환"을  위한
노력은 실제로 컴퓨터 안에서도 똑같이 일어나고 있다.
        그런데 이 순간, 갑자기 1 학년 후배가  "잠깐만 !"을 외치며 말
을 걸어 온다면 여러분은 어떻게 할 것인가 ?
        이런 때 그  사람의 인간성이 드러날 것 같은데,  나같으면 대꾸
없이 주먹을 한방 날릴 것 같다. "조용히 해, 너마저 인터럽트냐 ? "
        그러나 컴퓨터의  CPU "이 팔육(80286)"은  (착하게도) 때리지는
않지만 아무런 대꾸를 하지 않은 채 , 친구와 하던 얘기를 계속한다.
        이런 예가 "인터럽트의 nesting(중첩)"에  대한 실제적인 개념이
다.   여기서, 위 등장  인물들의 "인터럽트 우선순위"를 높은  순서대로
적어보면 다음과 같다.
   교장 선생님 --> 선생님--> 3학년   --> 2학년 친구  --> 1학년
        컴퓨터 내부에서도 이런 우선 순위를  가지고 있는 것은 똑같다.
그럼 이제 문제를 하나 더 내보자.
<문제>  "이 팔육" 학생이 "하드 디스크 C"와  얘기를 나누고 있다. 그런
데 갑자기  "프린터" 가 얘기를 걸어  온다면, "이 팔육"은  주먹을 날릴
것인가 ?  아니면 하던 얘기를 멈추고 프린터와 애기를 시작할 것인가 ?
        이 문제를 틀린 사람도 역시 정신 나간  사람에 해당 하는데, 여
러분 중에 과연 몇 명이나 정신이 멀쩡한지 모르겠다.
*******************************************************************
<2> 인터럽트(interrupt)와 폴링(polling)
*******************************************************************
        인터럽트의 나머지 내용을  더 공부하기 전에 먼저  상식으로 알
아 두어야 할 것이 있다.
        컴퓨터 용어로 "폴링(polling)" 이라는 용어가  있다. 이 단어는
굳이 프로그래머가 알아야  할 필요가 있는 것은 아니다.  주로 하드웨어
를 디자인 하는  사람들이 여러가지 칩들을 가지고 사용할 때  자주 등장
하는 용어이기 때문이다.
        그럼에도 불구하고 굳이 이 용어에  대해서 이야기를 하는 것은,
마치 "홀쭉이와 뚱뚱이"란 용어가 서로의 개념을  더욱 명확하게 보여 주
듯이, "인터럽트 와  폴링"이 서로의 개념의 차이를 더욱  분명히 보여주
기 때문이다.
        poll이란 용어는, 아시다시피 "투표,  투표하다"라는 뜻이다. 그
러나 전산 용어가  많이 그렇듯이 이런 용어로는 정확한 뜻을  알기 어렵
다. 내가 가지고  있는 영어사전은 오래되서 그런지 [전산]에  관한 의미
는 아예 없는데, 다행히 아래한글 2.5에  속한 영어사전에서는 그 의미가
실려있다. 그런데...
        적어도 유명한 사전에  실린 해설이니까  엉터리  일리는 없는데
나로서는 무슨 의도로 이렇게 적어놨는지 도저히 알기가 어렵다.
poll : (신호, 스위치) 장치로 단말기기에 송신하도록 작용하다.
polling :  특정 단말을 지정하고  그 국(한자로 방송국 할때의  "국")이
          송신하도록 권유하는 과정
        컴퓨터 내부에서는  CPU가 "인터럽트 방식"으로  일을 한다.  즉
CPU의 역할은, 어느  한가지의 주변 기기만 상대하는 것이  아니다. 마치
대통령의 임무와 비슷하게,  일이 없다면 그냥 놀고 있다가 갑자기   "누
가 키보드를 눌렀습니다"(하드웨어 인터럽트에  해당함)라고 인터럽트 전
령이 연락을 하면, "음 읽어봐라" 하고 처리해준다.
        또 놀다가 "프린터가 데이타  보내랍니다"(역시 하드웨어 인터럽
트에 해당) 하면 역시 "음 보내줘라" 하고 처리를 하는 식이다.
        이에 비해 "폴링"이란  것은 마치 대통령이 길거리를  걸어갈 때
호위하는 경호원들의  역할과 비슷하다. 즉  한시도 감시를 늦추지  않고
주위를 두리번 거리며 살핀다(이것이 polling(폴링)임).  따라서 혹시 괴
한이 총을 뽑는 경우가 있으면, 경호원은 누구의  연락도 받지 않고 즉각
자기가 알 수 있다.
        하나 더 예를  들면, 영화에서 흔히 보는 것 같이,  교도소 담벽
에 누가  붙은 놈이 없나하고 왔다갔다  하는  "서치 라이트"  의 불빛과
같다. 즉  한시도 쉬지 않고 "한  가지 임무"만을 위해서 일을  하는 것,
이것이 폴링이라는 개념이다.
        이미 여러분이  짐작하시다시피 폴링  기능을 하는 것은  컴퓨터
중에서도 "변방"의 단순 기능을 하는 장치들에 흔히 있다.
        그중에서도 가장 대표적인  것이 키보드 내부에 있는 8048 이라는
 "원 칩 (one chip) 마이크로 프로세서"이다.  원 칩 마이크로 프로세서는
간단히 얘기해서, "CPU +  RAM + ROM"을 하나의 칩 안에  전부 갖고 있는
것을 얘기한다.
        아마 세월이 좀 지나면 지금  우리가 쓰는 386컴퓨터, 486컴퓨터
정도의 기능을 하는 one chip 마이크로프로세서가  나올 것이다. 그 정도
쯤 되면  아마 "터미네이터(T2)"의 몸속에  넣어주고 쓸 만할 정도가  될
지도 모른다.  하지만 현재는 그렇게 막강한  기능을 갖는 것은  없는 것
같고 거의 장난감 수준이다. 하지만,  그래도  냉장고, 세탁기 정도의 자
동화를 위해서는 충분히 쓰일 수 있는 정도이다.
        이런 원칩  마이크로 프로세서가  우리의 키보드 안쪽에  장착이
되있는 것이다.(앞으로 기회가 되면 그림 파일로  보여드리겠고) 그리고
그 칩은 항상  폴링을 하고 있다. "인간이 키 자판을  누르나 안누르나를
항상 살피면서..."
        ---------------------------------------------------------
<참조>  (앞으로 이런 곳에서 같이 보내는 "그림 파일"을 참조하세요)
        ----------------------------------------------------------
        이와같이 "인터럽트 와 폴링"이라는 개념을  비교하면서 이해 하
시기를 바란다.
<숙제> 자신 있는 사람은 키보드를 뜯어보고  눈으로 직접 8042라는 칩을
        확인하세요 (반드시 전원을 내리고)
<숙제> 컴퓨터를 켠 상태에서 여러분 눈앞에  있는 키보드를 보고 8042가
        "폴링"하고 있는 것을 느껴 보세요.
        (서치 라이트 불빛이 왔다갔다 하듯이...)
*******************************************************************
<3> 인터럽트 벡터 테이블(vector table)을 알자
*******************************************************************
        현재 IBM-PC 에서는 256개의 인터럽트를  허용하고 있다. 그리고
각각의 인터럽트가 걸리면, 그에 해당하는 일을  하는 루틴이 있는데, 우
리는 그것을 "인터럽트 핸들러(interrupt handler)"라고 부르고 있다.
즉, 인터럽트가  걸리면 그  일을 처리(handle)하는 프로그램을  말한다.
이 인터럽트 핸들러라는  프로그램은 ROM BIOS 안에 어셈블러로  짜여 있
기도하고, DOS 안에도 있고, 우리가 직접 짤 수도 있다.
        이 각각의 인터럽트 핸들러의 시작 번지를  모아 놓은 것을 우리
는 "인터럽트  벡터 테이블(interrupt  vector table)"이라고 부르고  있
다.
        이 인터럽트 벡터  테이블을 직접 살펴보기 전에  상식으로 알아
야 할 것은 다음과 같다.
        1. 각 핸들러의 주소는 총 4바이트로 이루어져 있다.
            -> 세그먼트(2바이트) + 오프셋(2바이트) = 4 바이트
                예) 0x1234 : 0x5678
        2. IBM-PC 에서는, 메모리에 각 주소가 뒤집어져서 들어간다
             -> 이걸 "역 word" 방식이라고 부르며,  x1234  : 0x5678인
               값이  0번지부터 메모리에 들어가는 모습은 다음과 같다.
                78     56     34     12
                --     --     --     --
                0번지  1번지  2번지  3 번지
        3. "인터럽트 벡터 테이블"은 메모리의 절대번지
                --------------------------------
                0x0000 : 0x0000부터 할당되 있다.
                --------------------------------
        4. 벡터 테이블의 총 크기는 1K의 메모리 size이다.
                총 인터럽트 벡터 수 :   256개
                크기                :     4 바이트
                ---------------------------------------
                                        1024 바이트
<숙제> 디버거(debugger)의  덤프 명령(d)를 이용하여 인터럽트  벡터 테
        이블을 프린트하고 책상앞에 붙여 놓으세요.(총 1K 만큼)
<숙제> 디버거(debugger)를  이용하여 실제  메모리 00번지 부터  dump로
살펴보세요. 이 때  [525쪽] 에서 인터럽트 번호 9h의 실제  인터럽트 서
비스 루틴"의 주소를 "세그먼트 : 오프셋" 형태로 적어보세요.
*******************************************************************
<4> 인터럽트의 종류
*******************************************************************
        인터럽트의 갯수는 총 256개 (한바이트  크기분 )가 있다고 했지
만, 이것도 크게 다음과 같이 나누어 볼 수가 있다.
        1. 소프트웨어 인터럽트
        2. 하드웨어 인터럽트
        먼저 교재를 가지신  분은 [524쪽]에 나와 있는 표를  지금 펴보
자.(교재가 없는 분들은 각자 가지고 있는  인터럽트 벡터 표를 참조해주
세요)  이 표를 보는 방법은 다음과 같다.
<첫째>
        제일 왼쪽  칸이 "인터럽트 번호"  인데, 이게 아파트  당첨순서
처럼 "0 순위"에 가까울수록 우선순위가 높은  것이다. 따라서 제일 처음
에 나오는 "0" 번 인 "devide by zero  error"가 교장 선생님 처럼  컴퓨
터 내부에서는 가장 우선순위가 높은것이다. 숫자  뒤에 붙은 h는 16진법
을 의미한다. 즉  1h --> 0x01 과 같은 의미이다.
<둘째>
        가운데 "Address"  칸이 있는데, 이  칸은 전부 4바이트  크기의
주소이다. 주소가 4바이트인 이유는 "세그먼트  : 오프셋" 형식으로 먼거
리 포인터(far pointer)를 이용하기 때문이다.
1> 하드웨어 인터럽트
----------------------
        이 중에서도  일단 하드웨어 인터럽트를 구분해보자.  각기 색이
다른 형광펜으로 다음의 번호에 표시를 해두자.
   * 인터럽트 번호 : 8h 부터  fh 까지
       <메모>   - 하드웨어 인터럽트
                - 8259 인터럽트 콘트롤러 1번에서 발생한다.
   * 인터럽트 번호 : 70h 부터 77h 까지
        <메모>  - 하드웨어 인터럽트
                - 8259 인터럽트 콘트롤러 2번에서 발생한다.
   * 인터럽트 번호 : 0h  부터 04h 까지
        <메모> - CPU 에서 발생하는 인터럽트
2> 소프트웨어  인터럽트
------------------------
        위에서 표시한 하드웨어 인터럽트를 제외한  모든 것이 소프트웨
어 인터럽트라고 생각하면 된다. 이것도 크게  BIOS 인터럽트와 DOS 인터
럽트로 구별할  수 있다. 먼저   DOS 인터럽트의 구별을  위해서, 표에서
다음과 같은 줄에 "확실히"표시를 해두기로 하자.
        * 인터럽트 번호 -->  21h    84-87    (Dos function call)
        이 DOS function  call을 호출하면 (메모리에 올라와  있는) DOS
의 일부분인 command.com 안의 루틴을 호출하게 된다.
        BIOS function  call을 호출하면  ROM-BIOS 안의 루틴을  호출한
다.
*******************************************************************
<5> 인터럽트를 위한 하드웨어의 구현(IBM-PC 에서)
*******************************************************************
        인터럽트를 발생시키는 기능은 8259라는  콘트롤러(칩) 2개가 이
용된다. 더 자세한 개념도는 나중에 그림 파일로 보여드리기로 하겠다.
*******************************************************************
<6> 하드웨어 인터럽트(8259에서 발생하는))의 순서.
*******************************************************************
        하드웨어 인터럽트에 대해서  생각해야 할 것은 다음과  같은 두
가지이다.
        -----------------------------------------------------------
        1. 인터럽트를 걸 수 있는 디바이스(장치)의 종류는 무엇인가 ?
        2. 그 우선순위는 어떻게 결정되어 있는가 ?
        -----------------------------------------------------------
        이런 문제는 컴퓨터를  만드는 회사에서 이미 결정이  된 것이기
때문에 , 일반 사용자들은 [525쪽] 표를 참조하기만 하면 된다.
        표에서 "하드웨어  인터럽트"라고 표시한 부분을  참조하고 다음
의 문제를 풀어 보기로 하자.
<문제> 키보드와  하드디스크 인터럽트가  동시에 걸렸다고 하자.  CPU는
어느 인터럽트를 먼저 실행하겠는가 ?
*******************************************************************
<7> C 에서 제공하는 인터럽트 함수  (Turbo C 에서)
*******************************************************************
        C에서 인터럽트를  사용하는 방법은 지극히 간단하다.  이미 C에
서 준비되  있는 함수를 불러다 쓰면  되기 때문이다. 그  종류는 다음과
같다.
int86()
int86x()
intdos()
intdosx()
        이 내용은 교재의 [440쪽] (라이브러리  함수)편에 잘나와 있다.
(교재가 없는  분은 각자 가지고  계신 "라이브러리 레퍼런스"를  참조해
주세요.) 이 강좌를 읽는 분들은 "함수읽는  방법"에 대해 잘 아시리라고
생각하지만, 혹시나 하는 마음에 하나만 설명해 보기로 한다.
        먼저  [440쪽]의 int86()  함수를  살펴보면, 반드시  "dos.h"를
포함하라고 되어있다. 이유는,  union REGS의 정의가 이 곳에  되있기 때
문이다. 인터럽트를 사용하기 위해서는 CPU의  레지스터를 직접적으로 이
용해야 하는데, 그 변수의 정의가 dos.h 안에 이미 되있는 것이다.
        그리고,  int86()함수의 파라메타로  사용되는 변수는  3개인데,
교재에 보다시피. 첫번째 값은 "인터럽트의 번호"이다.  이 번호가 이 강
좌의 첫부분에서 얘기했던 "인터럽트 번호"를 의미한다.
        그리고 두번째,  세번째의 파라메타는 아래와 같이  똑같은 형태
이다.
                union REGS *inregs
                union REGS *outregs
스트락춰 와 유니온의  표현 방법에 대해서 익숙치 않은 분들은  각자 연
습하기로 하고 ,  앞으로는 아래에 보이는 상투적인 표현을  그대로 따라
쓰도록 하자.
#include <dos.h>        <-- 반드시 포함
main()
{
 union REGS r;          <-- 변수 r을 임의로 지정.
 r.h.ah = 0x02;      <-- 인터럽트  call 하기전에 필요한 값 대입
 int86(0x1a,&r,&r);  <-- 인터럽트 호출 (현재는 0x1a번)
                         &r의 형태로 쓴 걸 주의할 것
                        (함수에서 주소를 쓰라고 정의되 있기때문)
 hour = r.h.ch;      <-- 레지스터에 저장된 결과를 이용
 min  = r.h.cl;
 sec  = r.h.dh;
}
        int86x() 함수는 똑같은 내용이지만,  특별히 세그먼트 레지스터
를 더 지정 할 수 있는 형태이고,  intdos() 형태는 "DOS 인터럽트"를 호
출하는 함수이므로 굳이 인터럽트 번호 0x21을  안써도 되는 형태일 뿐이
다.
*******************************************************************
<8> 인터럽트의 작동을 눈으로 확인해 보자.
*******************************************************************
        다음에 인터럽트가 직접 작동되는 모습을 살펴보기로 하자.
먼저 [524쪽] 인터럽트 번호표를 보면, 인터럽트  0번은 "divide by zero
error"이다. 이말은 짐작하시다시피, 프로그램 실행중  "0으로 나누는 경
우"가 있으면  발생되는 인터럽트이다. 0으로  나누면 안되는 이유야  잘
아실 것이고, 이 인터럽트는 성격상 CPU  내부에서 발생하는 것이고 우리
가 임의로 호출할 수 있는 것은 아니다.
        다음을 직접  실행시키고 인터럽트 0번이 실행되는  모습을 살펴
보기로 하자.
main()
{
        int i;
        i = 0;
        i = 100/i;
}
<문제> 인터럽트 0번의 인터럽트 핸들러  (인터럽트 서비스 루틴)의 실제
주소는 얼마인지 디버거를 이용하여 찾아보세요.  (00 - 03까지에 들어가
있는 주소)
        하나 더  예를 들어,  인터럽트 번호 1c  를 살펴보자. 이  것은
"사용자 정의 timer tick" 이라고 설명되 있다.  이 내용을 이미 알고 계
신 분도 많겠지만, 지금 우리가 쓰고  있는 IBM-PC에서는 1초에 18.2번의
인터럽트가 걸리고 있다. 더 근본적인  일련의 이야기는 다음번에 하나의
강좌로 다시 쓰기로  하고 지금은 이런 사실을 그대로 확인해  보기로 하
자.
<문제>  먼저 도스 상태에서 모니터를 쳐다보세요.  즉 다음과 같은 상태
입니다. 여기서는  지금 안보이지만 모니터상에서는 커서가  깜박이고 있
을 것입니다.
           c:\tc>
지금 컴퓨터는 무슨 일을 하고 있습니까 ?
        요즘 아주 인기있는  "문화 유산 답사기"에 관한  책을 읽어보면
"사람은 아는 만큼  느낀다"는 아주 근사한 말이 나온다.  우리가 지금하
고 있는  기계적인 컴퓨터 공부에서도  이런 말을 쓰는 것이  어울리지는
잘 모르겠지만,  여러분이 바로  위의 문제를 풀면서(모니터를  쳐다보고
있으면서) 무엇을 느끼실 지 궁금하다.
        평상시에 커서는 (DOS 상태에서), 사람이  키보드를 치기를 기다
리며 한가롭게  껌벅이고 있는 것  같지만, 사실은 이 상태에서도  1초에
18.2번씩 인터럽트가 걸리고 있다는 걸 알아야 한다.
        이 인터럽트가 걸리면 ( 인터럽트 1c 번이므로)  70 - 73 번지에
적혀 있는  주소로 가서 일을 하고  온다. 그러나 이 곳에  가보면 (보통
상태에서는) 아무 일도  안시키고 "그냥 돌아가" 하는 쓸쓸한  대답만 듣
고 돌아온다. 따라서  외관상으로는 마치 아무것도 안하는 것  같이 보인
다.
        따라서 우리는 이제 아주 간단항 일을  하나 시킴으로서 정말 인
터럽트가 1초에 18.2번씩 걸리는 지 확인해 보기로 하자.
        시키는 일은 간단하다.  그냥 기념으로 '0' 이라는  숫자를 하나
찍고 돌아가라는  것이다. 먼저 프로그램을 보기로 하자
#include <dos.h>
interrupt intr_exam();
main()
{
        unsigned segment, offset;
        segment = FP_SEG(intr_exam);
        offset  = FP_OFF(intr_exam);
        poke( 0x0000, 0x0070, offset);
        poke( 0x0000, 0x0072, segment);
}
interrupt intr_exam()
{
        putch('0');
}
        위  프로그램은  보시다시피   두개의  함수로  이루어져  있다.
main() 함수에서  하는 일은 intr_exam()  이라는 함수의 주소를  읽어서
0000 : 0070, 번지와 0000 : 0072 번지에 넣어 주는 일만 한다.
        현재 변수로 쓰인 segment, offset 두개는 int 타입(2 바이트)
에 해당하는 것을 기억하자. 따라서 이 값들은  70 - 73까지의( 인터럽트
1c의 서비스 루틴 주소) 값을 변경하고 있는 것이다.
        이 작업은  짐작하시다시피, "일 안시키는 현재의  서비스 루틴"
으로 가지 말고 intr_exam() 이라는 함수로 오라는 뜻이다.
        현재 intr_exam() 함수는 '0' 을  찍으라는 일을 시키고 있다.그
리고 이 함수는 보통 프로그램의 서브  루틴이 아니고, 특별히 "인터럽트
서비스 루틴" 이라는 의미로 함수명 앞에  "interrupt" 라는 휘장을 달아
준 걸 눈여겨 보자.
        이제 이 프로그램을 그대로 잘라내서  실행시켜 보기로 하자. 이
프로그램은 원래의 서비스 루틴으로 되돌리는  프로그램이 없으므로 평상
시와 같이 DOS를 쓰고 싶으면 커퓨터를 껐다가  켜야 한다는 것을 기억하
자.  그리고 아래에 적혀 있는 숙제를 메모해 두고 직접해 보기로 하자.
Posted by '김용환'
,