임베디드 레시피

Egloos | Log-in





Interrupt VS. Polling

Interrupt를 얘기했으니, Polling을 얘기 안 할 수가 없겠네요. Interrupt는 하드웨어적으로 Hardware의 변화를 감지해서 외부로부터의 입력을 CPU가 알아 채는 방법이지요. 반면에 다른 입력을 감지하는 방법으로는 Polling이 있는데, Polling이라는 건 Hardware의 변화를 지속적으로 계속~ 읽어 들여서 그 변화를 알아채는 방법이라고 할 수 있는 것이에요.
 
Interrupt의 경우에는 원래 하던 일을 멈추고, 미리 약속된 ISR을 실행하여, 그 변화에 대한 응답을 하는 것이기 때문에, 각 CPU마다 Interrupt를 어떻게 처리해야 하는지가 다 다르지요. 그래서 System Software를 Design하는 Engineer는 이를 잘 알아야 하고, Kernel을 Design하거나, Porting하는 데에도 이런 처리 방법을 잘 이해해야 하지요. 하지만, Polling이라는 건 우리가 흔히 많이 사용하는 방법인 게지요. 특별하게 CPU에 Dependent하지도 않고, 그냥 무작정 계속 읽어 들이면 되는 거에요. 다 되었나요~? 하면서 상태를 계속 읽는 루프를 이용하면 되는 거에요.
 
do
{
  status = check_status();
} while (status == NOT_DONE);
 
뭐, 이런 식이죠.
무한정 이렇게 확인하고, status가 NOT_DONE이 아닌 경우 (아무래도 DONE이겠죠)에만 더 진행하는 거지요. 이런 걸 Interrupt로 하게 되면 어떤 모양새가 될까나요? 여기에서 위의 경우에 status가 DONE인 경우에 Interrupt가 발생할 때 실행하는 ISR을 status_alarm() 이라고 가정해 BoA요.
 
void dsp_task()
{
   ..................
register_isr (status_alarm);
wait_signal (OPERTION_DONE_SIG);
   ...................
}
 
 
void status_alarm()
{
    send_signal (dsp_task, OPERATION_DONE_SIG);
}
 
뭐 이런 식이 되겠죠. dsp_task에서 status_alarm() Call back 함수를 등록해 놓고 wait 상태로 진입한 후에, 다른 task들은 CPU를 점유할 수 있는 기회를 가질 수 있겠죠. status가 DONE이 된 경우에 status_alarm() 함수가 실행되면, dsp_task에게 OPERATION_DONE_SIG를 날려주시고, 그런 다음에 dsp_task는 Ready가 되고 때가 되면 Running이 되어 이어서 일을 할 수 있겠죠.
 
자, Polling과 Interrupt 차이를 알 수 있나요? Polling은 CPU가 다른 일은 하지 않지만, 즉각적으로 상태를 알아 낼 수 있고요, Interrupt는 CPU가 다른 일을 할 수 있는 상태이지만, 다시 해당 task로 순서가 돌아오려면 시간 차이가 생기죠. 이런 걸 Overhead라고 부르기도 해요. 사실 task level로 보려니까 이런 Overhead가 보이지만, 반응만 따지면 Interrupt가 훨씬 즉각적이에요.
 
Polling은 한 바퀴를 다 돌아야 그 상태 변화를 check가 가능합니다만, Interrupt는 실은 상태변화가 되자마자 status_alarm() 함수가 호출되는 것이나 마찬가지지요. Polling의 경우에는 Polling Loop가 길 수록 점점 더 늦게 반응할 가능성이 높아지지만, Interrupt의 경우에는 그렇지 만은 않은 셈인 거에요. 그러니까 task level로 다시 일을 시키지 않을 거면 Interrupt가 훨씬 빠른 거죠. 이런 차이를 잘 이해해야만 Interrupt와 Polling을 잘 선택할 수 있는 거지요. Interrupt로 구성할 것이냐, Polling으로 구성할 것이냐는 것은 순전히 사용자 손에 달렸다고 봐야 하거든요. Polling중에는 Interrupt를 이용한 Polling도 있을 수 있어요. Kernel Service중에서 Timer service를 이용하는 건데요, (Timer Service도 Clock tick Interrupt를 이용한 거죠) Interrupt로 처리할 수 없는 pin들의 변화 값을 관리하는 task를 만들어 놓고, 그 task에게 특정 주기 시간 주기로 pin을 monitoring하라고 일을 시키는 테크닉이에요.
 
뭐, 예를 들면, GPIO (General Purpose I/O)라는 pin들을 50mS마다 한번씩 check하고 싶다라고 하면, 이런 식의 구현이 될 수 있겠죠.
 
void pin_monitor_task()
{
    while (1)
    {
    set_timer (pin_monitor_task, WAKE_UP_SIG, 50); /* 50mS 마다 WAKE_UP_SIG 보내줘.. */
    wait_signal(WAKE_UP_SIG);   /* 기다릴 께... */
    clear_signal(WAKE_UP_SIG);  /* 오, 왔구나, 일단 WAKE_UP_SIG를 clear하고, */
    poll_gpio ();                        /* gpio를 polling해 보자 */
    }
}
 
void poll_gpio()
{
     if (read_gpio(1) == High)
        do_gpio1_work();
 
     if (read_gpio (2) == Low)
        do_gpio2_work();
 
      ............................
}
 
뭐 이런 식이 되겠지요?  이런 식으로 구성해주면 50mS마다 한번씩 gpio의 상태를 읽어서 그에 해당하는 일들을 할 수 있게 되는 거에요. 우후~
 
Interrupt를 쓰면 Processor가 다른 일을 할 수 있어서 효율적이지만, Interrupt를 실행하기 위한 Exception mode로의 전환, Context Switching (Register backup등) 이 Overhead가 될 수가 있음을 잘 기억해 두시고요. Polling과 Interrupt는 서로 차이가 좀 있으니까, 구현에 따라 장단점이 달라지니까, 잘 기억해 두시면 좋습니다요. 
 

by 히언 | 2009/08/25 20:36 | RTOS팩토리(커널) | 트랙백 | 핑백(1)

Linked at 친절한 임베디드 시스템 개발자.. at 2009/10/01 12:45

... ⓜ Interrupt 와 Polling ... more

◀ 이전 페이지          다음 페이지 ▶