임베디드 레시피

Egloos | Log-in





Preemptive (선점형) Multitasking 이란 도대체

선점형 (Preemptive) Multitasking란 도대체 뭔가에 대한 이야기.
 
뭐, 별건 없어요. 일단은 많은 사람들이 정의를 내려주었으니, 그 정의를 알아보면,
" 하나의 Task가 Scheduler로부터 CPU를 사용권을 할당 받았을 때,  Task가 자발적으로 CPU 사용권을 반납할 때까지 강제적으로 CPU의 제어권을 빼앗을 수 없는 Multitasking방식을 비선점형 (Non-preemptive) Multitasking이라 하고, 이와 반대로 Scheduler의 필요에 따라 Task로부터 CPU의 제어권을 강제적으로 빼앗아 다 Task가 실행될 수 있도록 Task의 실행 순서를 결정하는 Multitasking방식을 선점형 (Preemptive) Multitasking 한다. "
 그리고, 또 하나.Multitasking의 의미를 되새겨 본 적이 있나요? " Multitasking은 CPU하나에서 2개 이상의 작업을 동시에 처리하거나, 동시에 실행하는 것을 말한다."
 
여기에서 우리는 Multitasking에 대한 오해를 하나 풀고 지나가야 할 것 같아요. 뭐 오해라기 보다는 하도 argue가 많은 부분이라 어떻게 정의하고 어떻게 구현할 것인지에 대해서, 이야기 해보려 해요. 이게 이해 된다면 뭐, 좀 다른 것들이라도 조금씩 변형해서 이해할 수 있겠지요?
흔히들 Multitasking은 동시에 뭔가를 동시 다발적으로 일을 하는 거라는 Concept을 가지고 계실 텐데요, 이건 시분할 방식으로 한 개의 CPU에서 여러 가지 일을 동시에 하는 것처럼 보이게 하는 거지, Multitasking하고 좀 구분해야 하지 않을 까 싶어요. 일단은 Embedded System는 여러 개의 Task를 Ready 상태로 만들 수 있고, 이 Task들에게 따로 따로 일을 시킬 수 있는 System을 Multitasking이라고 불러야 할 듯 합니다요.
 
시분할 방식을 여기에 더 접합하여, 동시에 동시다발적으로 동작하는 것처럼 보이게 할 수 있는 거지, 이게 Multitasking이라고 보기에는 좀 무리가 있어 보이네요. 보통은 Non-preemptive Multitasking에서 이런 시분할 방식을 이용해서 웬만하면 모든 task들이 동시에 실행되는 것처럼 보이는 테크닉을 구사하죠.
 
어쨌거나 일단 그림으로 Non-preemptive하고, Preemptive를 비교해 보면, 확인 가능해질 거라 생각하며, 끄적여 보겠사옵니다.
 
Non-preemptive는 Task가 자발적으로 CPU 사용권을 반납할 때까지, 해당 Task가 CPU를 점유하고 있지요. 우리 Task의 구조에서 어떤 것이 Task가 자발적으로 CPU에게 사용권 반납을 하느냐 하면, wait_signal이 그런 형태죠. wait_signal()을 해주면, Task는 그 signal이 올 때 까지는 놀겠다는 의미에요. 그러니까, Non-preemptive는 Task가 wait_signal()을 해주기 전까지는 자기가 실행되겠다는 의미지요.

 
Task가 A, B, C, D 4개 있다고 치면, Non-preemptive 방식은 Task A가 일을 다 마치고 wait_signal()을 부를 때 까지 계속 실행되다가, wait_signa()이 호출되어야 Task B로 넘어가게 되는 거고요. 보통은 Task A,B,C,D가 서로 연관된 service를 주고 받을 경우에는 Task A가 B에게 일을 시키고, B는 C에게 일을 시키고 뭐 이런 식이거나, Task끼리 서로 연관성이 없는 일을 하는 System의 경우에는 ISR을 통하거나, 특별한 경로를 통해서 여러 가지 Task를 만들어 그때그때 실행 시키는 형식이에요. 이런 경우에는 Task는 무한 loop를 도는 형식이 아닌, 일반적인 Software 같은 모냥을 할 때가 많죠. 이건 극단적인 Non-preemptive 방식이고요. 이런 식으로 하면 아무래도 A가 끝날 때 까지는 B나 C가 실행이 안되니까, 이런 거야 말로 시분할로 Task A 1초, Task B 1초, 뭐 이런 식으로 순번을 돌리면서 실행하면 여러 개가 한꺼번에 진행되는 것처럼 보이겠죠. 그것이 시분할 방식의 Multitasking인 거에요. 자, 그러면 바햐흐로 Preemptive는 어떨까. Preemptive는 Task A가 일하던 중에 Task B도 치고 들어올 수 있고, Task C도 치고 들어올 수 있는 뭐 이런 System인데요, 보통 Task가 치고 들어올 수 있다고 함은 Task 마다 중요도(Priority)를 갖고 있어서, 더 높은 순위의 Priority를 가진 Task가 CPU 사용권을 치고 들어올 수 있다는 의미에요.
 

뭐, 보시다시피 가장 불쌍한 Task A가 실행되는 동안 더 높은 Priority의 Task B가 치고 들어 왔고요. Task B가 실행되는 동안 더 높은 Task C가 치고 들어 왔고요. Task C가 끝남과 동시에 Task D가 치고 들어왔네요. 점점 더 형님 먼저 아우는 굶어 죽던지 말던지. 뭐 그런 System인 거에요.
 
항상 궁금했던 건 Task A가 Running 중에, Task B가 어떻게 Ready 상태가 되고, Task C는 어떻게 Ready 상태가 되는 걸까. Task들은 자기가 알아서 생각하는 기능이 있는 걸까 하는 기묘한 생각에 빠져들게 되지요. 분명 Ready 상태는 Signal을 받아야 Ready 상태가 될 꺼고. 지 혼자서 Ready는 되지 않을게 분명하지요. 저기 Task A가 Running중에 Task B가 Ready가 되는 비밀은 Interrupt에요. 그림에서 숨겨진 내용을 다시 살펴 봐야 해요. 현미경 어디 갔어.


 
자, 잘 보면 가장 Priority가 높은 Interrupt가 중간중간 발생하는 거에요. Task A를 처리하다가, Priority가 가장 높은 Interrupt가 발생하여 Task A를 Ready상태로 만들어 놓고요, Interrupt가 처리해야 할 일을 ISR에서 처리를 하다가 보니, Task B에게 일을 시켜야겠다고 생각이 들면? 그때 send_signal()을 하는 거에요. send_signa()을 하니까, Task B는 Ready 상태가 된 거죠. ISR을 다 처리하고 나고서 Scheduler가 Context Switching을 하려고 보니까 자, Ready인 녀석들이 머머 있나.. 찾아 봤더니, 오호라, Task B가 Ready네? (이때 Task A 실행되다가 말았음 불구하고, Task B가 더 중요도가 높으니까, Task B를 선택하는 것이 Scheduler의 숙명이지요) 그러면 Scheduler는 Task B가 Running 상태가 되도록 만들어 줍니다요. 마찬가지로 Task B에서 Task C로도 마찬가지고요, Task C에서 Task D로도 마찬가지고요. Task D가 다 실행되고 난 다음에, Task D는 wait_signal()을 통해서 CPU 실행권을 놓게 되고, wait 상태가 되지요. CPU 실행권을 놓고 나니, Scheduler는 또 시름에 잠기죠. 가장 Priority가 높은 Ready상태의 Task는 누가 있느냐, Task C구나? 그러면 C 니가 Running해. Task C가 일을 다 마치고 또 wait_signal()을 통해서 CPU의 실행권을 놓았네요. Scheduler는 또 어느 녀석이 Priority가 높으면서 Ready인가.. 또 찾겠죠. Task B일쎄.. 뭐 이런 식의 Policy를 가지고 Context Switching을 하는 거에요. 어떤 Task가 실행 중에 다른 Task가 Ready가 되는 Mechanism은 이런 거에요. 그러니까, customer_isr과 waiter_task, cook_task도 그런 식으로 굴러 갈 수 있는 거죠. 예를 들어 customer_isr > waiter_task> cook_task 순으로 Priority를 만들어 주면 cook_task가 요리를 하고 있더라도, 손님이 오게 되면 customer_isr이 발생하게 되고, customer_isr은 waiter_task를 ready로 만들고 종료되고, 바로 다음 번에 waiter_task가 cook이 요리를 하던 중이라도, 먼저 손님에게 인사를 하고 다시 cook에게 또 주문이 왔다고 알려주는 거죠.  요리를 끝내고 waiter에게 음식 나르라고 DELIVER_SIGNAL을 날리고 wait_signal()을 통해서 wait 상태에 들어 가고, 다시 waiter가 Priority가 높으니까, 음식을 날라주시고, 다시 wait이 되고 wait이 되고 보니, cook이 또 요리 할 일이 있었구나 하고 알아 챌 테고, 또 요리를 열심히 한 후에, wiater에게 알려주겠죠. 뭐 이런 mechanism 인 게죠. 
 
 
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) */
}
 
 
 
이제부터의 구현사항에 대해서는 Priority base의 Preemptive Scheduling System에 대해서 집중적으로 예를 들어서 가려고 해요. 전 이게 재미있더라구요.
 

by 히언 | 2009/08/12 20:37 | RTOS팩토리(커널) | 트랙백 | 핑백(1) | 덧글(1)

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

... ⓔ Preemptive (선점형) Multitasking이란 도대체 ... more

Commented by 신입사원 at 2009/09/10 08:50
비선점형을 설명하는 첫번째 그림 바로 밑단락의 두번째 줄!!! wait_signa() <-- 오타 발견했습니다ㅎㅎ
※ 이 포스트는 더 이상 덧글을 남길 수 없습니다.

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