본문 바로가기
Bootup 중 Kernel로의 진입 - main() 함수 -

Bootup중 Kernel로의 진입은 어떻게 진행되는 걸까요. 보통 Kernel로의 진입은 main함수에서 이루어 져요. 우리 Software Entry가 main() 함수라는 사실을 이미 들여다 본 적이 있었지요. 우리 Software쟁이들은 일단 Software를 들여다 볼 때는 main()함수를 찾아서 들여다 보는 버릇이 있으니까, 여간 편리한 게 아니지요.
 
main()함수는 이미 boot loader를 지나서 (기본적인 Hardware setup -CPU init, MCU init, clock init, memory init 등이 끝난 상태라고 봐야겠네요) 순수하게 Software가 동작 가능한 상태가 되었을 때, 호출 되는 부분이니까 여기에서 Kernel을 시작하는 Entry가 보통 자리하게 되어요.
 
어떻게 하느냐. 맨 처음에 kernel_init 함수를 통해서 IDLE task를 살리는 일을 하게 되는데, IDLE task라는 건 task가 아무것도 ready이거나 running이 아닌 경우에 kernel을 점유할 task를 하나 넣어주는 거죠. 여기에서 IDLE이라고 부른다고 해서 꼭 IDLE이라고 부를 이유는 전혀 없어요. Kernel마다 그 이름은 다 다르니까, 헷갈리시면 안되어요.
 
dword idle_stack [IDLE_STACK_SIZ];     /* Stack for MC task.     */
 
int main (void)
{
  kernel_init( 
            &idle_task_tcb,             /* IDLE task TCB */
            (void *)idle_stack,        /* IDLE task stack */
            IDLE_PRI,                    /* IDLE task PRIORITY */
            idle_task,                    /* IDLE task 본체 */
            "IDLE_task")
   return 0;    /* Never Returns : 여기로는 다시는 안돌아와요 */
}
 
자, 이런 식으로 필요한 것들이 4개 정도 있겠네요. TCB, stack, PRIORITY, Task 본체. 요 네 개가 왜 필요한지에 대한 의문점이 생기겠죠. idle_task_tcb는 왜 필요한가! 하면, 기본적으로 Kernel은 TCB를 가지고 모든 장난을 치니까 필요하죠. idle_task_tcb를 Task tcb list (Double Linked List)에 등록을 하고요. IDLE task를 실행해 주는 일을 하지요. 일단 이전에 나왔던 TCB의 구조체를 한번 다시 볼까요. 
 
typedef struct {
   struct task_tcb_struct *next_ptr;
   struct task_tcb_struct *prev_ptr;
task_tcb_link_type ;
 
typedef struct task_tcb_struct {
   char                        task_name[200];
   void                         *sp;                                  /* 스택 포인터 */
   uint32               receive_siganl ;                          /* 받은 Signal */
   uint32               wait_signal ;                             /* 기다리는 Signal   */
   uint32               pri;                                          /* Task의 Priority */
   task_tcb_link_type    link;                                   /* for TCB list */
task_tcb_type ;
 
TCB에는 init하면서 넣어줘야 할 information들이 즐비 하네요. 만약에 kernel_init()을 다음과 같이 정의 해 보자구요. 그래야 좀 보기 편하겠네요.
 
typedef void (*task_func_type)();
 
void kernel_init ( 
  tcb_type      ptcb, 
  unsigned char*   pstack,
  dworkd         pri,        
   task_func_type   ftask,          
  char           *ptaskname,       
)
 
두 번째로 idle_stack이 왜 필요한가! 라면, tcb에 IDLE task가 사용하게 될 stack을알려주는 거에요. kernel_init()함수 안에는 &idle_task_tcb와 idle_stack을 각각ptcb와 pstack으로 받아서 아래처럼 연결해 주는 코드가 있겠죠.
 
      ptcb->sp               =      pstack ;
 
이제부터 IDLE task가 사용할 stack은 idle_stack이다 라고 알려주는 거고요. 세 번째로는 Priority를 알려줘야죠.
 
     ptcb->pri               =   pri ;
 
그리고 네 번째로는 taskname도 넣어주면 금상첨화 겠네요.
 
           while ( (ptcb->task_name[loop] = ptaskname[loop] ) &&
            ( index++ <  200  ) );;
           ptcb->task_name[199] = '\0'
 
으로 복사해 넣어주시고요.
 
이렇게 하면 받은 정보들을 Kernel이 참조 가능하도록 TCB에 차고차곡 잘 쌓아 넣어주신게죠. 마지막으로 IDLE_task가 실행되게 하면 되니까
 
     ftask();
 
을 호출해 주면 idle_task()가 실행 되겠죠. 오호~ IDLE task가 이제 실행 됩니다요. IDLE task만 실행해 주면 끝나느냐 하면, 그건 또 다른 얘기에요. IDLE task는 또 하나의 임무가 있어요. 그게 뭐냐면, IDLE task 이외의 task들을kernel API를 통해서 살려내야 하는 임무를 가지고 있지요. 일단 idle_task()에 진입 하면, task들을 define해 주고, start 해주는 routine을 가지게 되는 거에요.
 
void idle_task()
{
 
   define_tasks();
   start_tasks();
 
   while (1)
   {
        wait_signal (WORK);
   }
}
 
idle_task()에게 순서가 오는 건 Kernel이 다 뒤져봐도 이제 idle 너밖에 없다고 판단했을 때 idle_task()에게 WORK signal을 날려 주는 구조고요. 아무것도 안하고 loop 한 바퀴 돌면 또 WORK을 기다리는 거죠. 그 사이에 또 아무 것도 없으면 또 idle에게 순서가 오고 뭐 이런 식인 거에요. 결국에는 이런 모양새가 완성되는 거죠.

define_tasks() 들에는 다른 task들을 최초에 idle task 살리듯이 TCB, Stack, Priority, Task 본체, Task name등을 가지고 TCB 장난을 치고 Task 본체를 실행하게 되는 거에요. sleep_task()라는 걸 예를 들어서 보지요. 
 
void sleep_task()
{
 
   sleep_task_init();
 
   wait_signal (START);  /* ⓐ define하게 되면 sleep_task()가 실행되며 여기에서 기다림 */
 
   while (1)
   {
        wait_signal (WORK); /* ⓑ task의 무한 loop 내부 */
   }
}
 
define을 하게 되면 define된 모든 task들은 ⓐ 지점에서 START signal을 기다리고 있는 거지요. 이 시점에서 idle_task()가 start_tasks() 함수를 만나게 되면 define했던 모든 task에 순서대로 START signal을 날리는 거에요. 이렇게 되면 모든 task는 while(1)문 안으로 진입하게 되어 WORK signal을 기다리게 되는 거에요. 간단하지요? 헉헉.
 
이런 식으로 처음에 System이 Power up 되어 Kernel에 진입하게 되는 거랍니다. 전체적인 Story는 이렇고요, Idea도 거의 비슷한 거에요. Kernel 종류마다 약간의 차이는 있지만, 이걸 알고 있으면 조금 더 편하게 접근 가능할 거라 생각하고 있습니다.
 

 IDLE task에게 순서가 가는 일은 거의 없사옵니다. 왜냐하면 Battery Saving을 위하여, Task들이 일을 안 할 때는 Power Saving mode로 System을 Shutdown 시킨 답니다. 그래서 IDLE보다는 sleep_task()가 거의 끝자락에 자리잡고 있다고 해도 과언이 아닙니다요.

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



댓글





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