본문 바로가기
Task 상태, Task는 Service단위

Service는 누군가에게 일을 해주는 걸 service라고 하죠. 보통 Task는 하나의 일 단위로 쪼개는 게 보통 Task를 나누는 관례에요. 보통 이런 Task들을 다룬 서적들을 보면 어려운 말로 상태를 나눠 놓았던데, 별거 아닙니다. Task는 누군가가 일을 시키면 일을 하는 상태하고, 누구라도 저에게 일을 시켜주십시오 기다리고 있겠습니다 라는 상태, 그리고 어 누군가가 나한테 일을 시켰네? 하는 상태 등이 있겠지요.

이런 상태를 State Transition Diagram이라는 유식한 그림으로 나타내면

이런 식인 거에요. 이런 그림 많이 봤죠. 그런데, 이런 그림으로 그린다고 해서, Task가 나 이런 상태요.. 하고 자기 state를 누구한테 보여주거나, 관련된 Data를 저장하고 있는 건 아니에요. 그냥 이런 일을 하는 단계들이 있더라 정도에요. 다만, 이런 State는 Task Level에서 관리 되는 게 아니라, Kernel Level에서 관리 되고요. 이런 건 TCB라는 거에 들어 있어요. 하지만, 대충 Task의 구조만 봐도 대충의 Task의 상태를 알 수 있으니 그걸 확인해 보도록 하시죠.
이제까지 보아 오던 Task의 기본 구조하고 이런 State를 비교해서 한번 보도록 하시죠.

void waiter_task()
{
    
     waiter_task_init();   /* 여기까지 ①번 init state구요, 여기를 지나자 마자 ②번 wait state가 된답니다. */
                                   
     while (1)
     {
          wait_signal (SRV_SIGNAL);   /* wait을 만나자 마자 ②번 state가 되는거지요.  */
                                        /* 누군가가 물 갖다주고, 주문도 받아 달라고 SRV_SIGNAL을 날려 주시면 
                                            ③ Ready State가 됩니다요. Ready State라는 건
                                            Scheduler는 이 Task가 누군가로 부터 일 해달라고 요청 받았어라는 걸
                                            알아 챌 수 있는 단계고요, 다음 번에 Scheduling할 때 순서를 줘야 하는
                                            task라는 걸 인식 하는 단계죠. 그러니까, Signal을 받았다고
                                            무작정 실행되는 건 아니에요 */
                                       /* ④ wait_signal을 벗어나  여기로 진입하면 Scheduler가 waiter_task로 Context Switching 시켜서
                                           일할 수 있게 만들어 준 단계죠. 현재 waiter_task가 일을 하고 있으니까,  
                                           Running state라고 불러요. */
          clear_signal (SRV_SIGNAL); /* SRV_SIGNAL을 다음번에도 받을 수 있도록 초기화 하자 */
                                          
          어서옵셔. 인사하고;      
          물도 갖다주고;         
          주문도 받아주고;    
      }
}

뭐, 별거 아니죠. 그러니까, 무한 loop를 돌면서 wait → ready → running → wait 뭐 이런 순서를 무한히 반복하는 거죠. 자 기본적인 Task의 state와 구조는 이렇고요. 그러면 signal을 날릴 수도 있어야겠죠. 자, 눈치 채셨는지 모르겠지만, 이제 우리는 식당 하나를 꾸려나가 볼 꺼에요. waiter도 있어야 하고 cook도 있어야겠죠. waiter_task()가 할 일이 어서옵셔 인사하고, 물도 갖다 주고, 주문도 받아주고, 그 다음에 cook_task()라는 넘한테 요리를 시켜야 겠죠. 혼자서 다 할 순 없으니까. 이런 경우에 send_signal(해당task, 시킬 일) 이라는 가상의 함수를 정의 해 볼까요?

void waiter_task()
{    
     waiter_task_init();   
                                   
     while (1)
     {
          wait_siganl (SRV_SIGNAL);
 
          clear_signal (SRV_SIGNAL);
                                   
          어서옵셔. 인사하고;      
          물도 갖다주고;               
          주문도 받아주고;   
 
          send_signal ( cook_task, COOK_SIGNAL);          
 
      }
}

send_signal ( cook_task, COOK_SIGNAL); 을 요렇게 해주면, Wait State였던 cook_task()는 Ready state가 되고요, Scheduler가 순서를 잘 배분하여 cook_task()에게 실행 순서를 주면, cook_task()는 Running state가 되어 '요리'를 한 후에 다시 wait state로 돌아가겠죠. 요렇게만 하면 또 재미 없으니까, 약간 task에 변형을 주어 볼까요? task끼리 서로 일을 시키기도 하고, 한 task가 여러 가지 일을 할 수 있으면 좋겠죠. 자, 그러면 waiter_task()를 조금 더 변형 해 볼게요. 한가지! signal은 어떤 word type의 전역변수에 각 bit 별로 할 일들이 약속되어 있다고 해보시죠.

void waiter_task()
{
    
     waiter_task_init();   
                                   
     while (1)
     {
          wait_siganl (SRV_SIGNAL || DELIEVER_SIGNAL);
          sig = get_signal (); 
 
          if ((sig & SRV_SIGNAL)!=0)
          {
             clear_signal (SRV_SIGNAL);
              어서옵셔. 인사하고;      
              물도 갖다주고;               
              주문도 받아주고;   
              send_signal ( cook_task, COOK_SIGNAL);         
           }
 
          if ((sig & DELIEVER_SIGNAL)
          {
              clear_signal (DELIVER_SIGNAL);
              음식을 손님에게 가져다 주자 영차;
          }
      } /* while (1) */
}

자, 이렇게 되면 waiter_task()는 두 가지 일을 할 수 있는 task가 되었어요. 와웃! cook_task()는 COOK_SIGNAL을 받은 후, 요리를 열심히 한 후에, send_signal (waiter_task, DELIEVER_SIGANL)을 waiter_task()한테 날려주시면, waiter_task()는 마침 기다리던 DELIEVER_SIGANL을 처리해 주는 거죠. 쿄홋 간단하죠.

void cook _task()
{
    
     cook_task_init();   
                                   
     while (1)
     {
          wait_siganl (COOK_SIGNAL);
          sig = get_signal (); 
 
          if ((sig & COOK_SIGNAL)!=0)
          {
              clear_signal (COOK_SIGNAL);
              열심히 요리를 하자;  
              send_signal ( waiter_task, DELIVER_SIGNAL);         
           }
      } /* while (1) */
}

자, 이때, 세상에는 Task밖에 없느냐, 하면 Interrupt도 있겠죠. Interrupt는 Hardware로 부터 진짜 전기 신호가 전달 되고요. Interrupt는 비 정기적으로 (Asynchronous라는 유식한 말도 있죠) System에 input을 넣어주죠. 갑자기 그리고 뜬금없이. 느닷없이?
이럴 때, 느닷없는 Interrupt를 통해서 waiter에게 손님이 왔을 때 알려주면 어떨까요? 보통 Interrupt가 걸리면 ARM의 경우에는 IRQ Exception이 발생하는 것과 다름없고요, IRQ mode로 전환되면서, Interrupt를 처리하게 되는데, 이때 IRQ mode에서 Interrupt를 처리해 주는 routine을 Interrupt Service Routine, ISR이라고 불러요.

손님이 오는 걸 Interrupt라고 하고요, 손님이 오는 순간 IRQ mode로 바뀌면서, 실행되는 함수를 customer_isr(void) 라고 해 볼까요? C Level의 ISR을 만들려면, ARM에서는 __irq 예약어를 사용해서 만들어 줘요. 요건 IRQ_Handler 구현 할 때 좀더 자세히 보고요. 

 void  customer_isr (void)
{
    send_signal (waiter_task, SRV_SIGNAL);
    clear_interrupt_source (customer_coming);
}

요렇게 구현해 주면 어떤 일이 벌어질 까요? 그렇죠. 순님이 오는 순간 customer_coming Interrupt가 걸리면서 IRQ_Handler()가 불려지고, 이 ISR은 SRV_SIGNAL을 waiter_task()한테 날리겠죠. 또 재방송인데, 그러면 waiter_task()는 SRV_SIGANL을 받았으니, Ready 상태가 되고요, Context Switch 될 때, waiter_task()로 순서가 오기만을 기다리다가, 순서가 오면 어서옵셔, 인사하고, 물 갖다 주고, 주문 받고 하는 거죠. (물론 cook한테 일도 시키겠죠?)

마지막에 customer_coming interrupt를 clear해 주는 code가 있는데, 만일 costomer_comming interrupt를 clear안 해주면 어떻게 될까요? customer_isr을 처리하고 나서 봤더니, 아까 customer_coming interrupt가 또 있는 거죠. 에라이 또 customer_isr로 가자~  System은 이렇게 또 interrupt 처리를 하게 되는데, 이건 아까 손님 때문에 걸린 interrupt니까 지금 지워주지 않으면 무한히 바보 같은 짓을 계속 하게 되는 거지요.

뭐, 별거 있나요. 이렇게 System은 구성되는 거라고요.

 Signal은 어떤 모양?
보통 Signal은 bit로 설정 가능하게 되어 있어요. 32 bit signal로 설정되어 있다면, Task는 32가지의 signal을 받을 수 있는 것이지요. 그러니까, wait signal을 설정할 때 ||으로 설정할 수 있고요, wait_siganl (SRV_SIGNAL || DELIEVER_SIGNAL); 받은 signal을 확인할 때 if ((sig & SRV_SIGNAL)!= 0)  요런 식으로 확인 가능한 거에요.

 
 
Linked at 친절한 임베디드 시스템 개발자.. at 2009/09/20 19:13

... ⓓ Task의 상태, Task는 Service단위와 ISR ... more

Commented by 재여리 at 2009/08/12 11:17
글 잘보고 있습니다.^^
님께서 했던 것을 토대로 살짝 제 나름대로 정리도 제 홈페이지에 해본것도 있었는데..
이미지 살짝 무단(?)으로 퍼간것을 용서해 주시길 바랍니다..ㅡㅜ;
예전에 REX를 소스레벨로 분석해보았었는데..
님께서 올려주시는 자료를 토대로 새롭게 알게 되는 부분이 많이 있네요..
REX 분석할 때 signal에 대해 이해가 잘 안갔었는데 님의 자료를 보고 이제 좀 알것같습니다..^^
Commented by 히언 at 2009/08/12 20:40
ㅎㅎ 넹 감사합니다. 자주 오시고요,
다만, 대문에 말씀드렸듯이, 출처를 밝히는 조건으로 open입니다요!
이미지 출처를 표기해 주시면~ 아주아주 감사하겠사옵니다. ~
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.
친절한 임베디드 시스템 개발자 되기 강좌 글 전체 리스트 (링크) -



댓글





친절한 임베디드 개발자 되기 강좌 글 전체 리스트 (링크) -