ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Pintos Project #1 : Pintos 환경 구축 (1/3)
    Computer Science/OS 2020. 5. 29. 14:43
    728x90

    1. 개요

    • Pintos의 소스 코드 분석, 전체적인 구조 이해하는 것

    2. 내용

    • Pintos 운영체제가 부팅을 시작하여, alarm-multiple 테스트를 수행하기까지의 과정에 대해 다음을 수행한다. 
      • 함수 분석
      • 자료구조 분석
      • 프로그램 실행 경로 분석

     

    • 배포한 가상 머신 상의 ~/pintos/src/threads 디렉토리 내의 소스 코드를 대상으로 분석한다.
    • 대상 소스 코드 중 조건부 컴파일을 위한 매크로 USERPROG와 FILESYS 해당 부분은 분석 대상에서 제외한다.
    • 프로그램 수행 경로를 따라 주요 함수를 분석하고, 관련 주요 자료구조에 대한 분석을 포함하여야 한다.

    3. 순서

    1. 함수의 분석
    2. 자료구조의 분석
    3. 프로그램 실행 경로의 분석

    3-1. 함수의 분석

    1. thread_init()

    1)     thread_init() : thread의 초기화를 의미한다. 자세히 말하자면 kernel 자기 자신을 하나의 thread로 만드는 과정이다. 이를 kernel 본체 thread라고 하겠다. 

     

     

    2)     ASSERT (intr _get_level () == INTR_OFF) : ASSERT()는 괄호안의 조건이 만족되어야만 한다는 뜻이다. 즉, 여기서는 interrupt가 off되어야만 진행한다는 의미이다. 외부interrupt의 off는 외부interrupt를 무시한다는 의미인데, 무시하지 않는다면 ready queue에서 다음 thread의 scheduling과정에서 interrupt가 발생되어 오류를 보일 수 있기 때문이다. 

     

     

    3)     list_init(&ready_list), list_init(&all_list) : ready_list는 ready queue를 의미한다. Ready queue는 linked-list형태로 되어있다. Ready queue의 초기화를 의미한다. Ready queue이외에 job queue, device queue등이 있다. 이를 초기화한다. 

     

     

    4)     Initial_thread=running_thread() : thread의 자료구조인 tcb의 시작주소를 thread의 시작주소로 하겠다는 의미이다. 

     

     

    5)     Init_thread(initial_thread, “main”, PRI_DEFAULT) :thread의 본격적인 초기화이다. Thread의 초기화는 thread의 tcb의 초기화를 의미한다. 

             (1)    Initial_thread : 초기화 대상

             (2)   “main” : 대상의 이름을 main으로 한다. 

             (3)   PRI_DEFAULT : thread의 우선순위이다.

     

     

    6)    Initial_thread->status=THREAD_RUNNING : thread의 상태를 running으로 바꾼다. 

     

     

    7)    Initial_thread->tid=allocate_tid() : thread의 고유 id인 tid를 할당한다. Allocate_tid()함수는 1씩 증가된 값을 return한다.


    2. palloc_init()

    1)      palloc_init() :  동적 메모리 할당 영역의 초기화를 하는 함수이다. 이외에 malloc_init(), paginig_init()함수가 있다. 

     

     

    2)     free_start=ptov(1024*1034) :  pintos에서 사용할 메모리 영역의 시작 주소를 free_start에 저장한다. 

     

     

    3)     Free_end=ptov(init_ram_pages*PGSIZE) : page의 갯수와 page의 크기(PGSIZE)를 곱하여 전체 영역을 구하고, 그 값을 free_end에 저장한다. 

     

     

    4)     Free_pages=(free_end-free_start)/PGSIZE : 사용 가능한 전체 메모리 영역의 페이지 갯수를 구하여 free_pages에 저장한다. 

     

     

    5)     User_pages=free_pages/2 : 전체 페이지의 절반을 user page로 한다. 

     

     

    6)     If문을 보면, 만약 user pages가 user pages의 최대크기(limit)보다 크면, user pages를 최대 크기와 동일하게 설정한다. 

     

     

    7)     Kernel_pages=free_pages-user_pages : user pages를 할당하고 남은 공간에 kernel pages를 할당한다. 

     

     

    8)     첫 번째 init_pool은 kernel_pool이라는 이름의 base address를 free_start로 하고, page갯수가 kernel pages와 같게 하여 memory pool을 생성한다. 

     

     

    9)     두 번째 user_pool은 init_pool은 kernel_pool이라는 이름의 base address를 (free_start+kernel pool의 크기)로 하고, page갯수가 user pages와 같게 하여 memory pool을 생성한다.

     

     

    10)   이 함수가 실행된 직후의 memory pool의 구조는 다음과 같다.

             (1)    Kernel pool과 user pool에 tcb의 크기인 4KB 단위로 동적 메모리가 할당된다.

             (2)   .text : 기본 명령어들이 있다. 

             (3)   .rodata : 값이 바뀌지 않는 전역변수가 있다. 

             (4)   .data : 일반 전역변수가 있다. 

             (5)    .bss : 초기값이 주어진 전역변수가 있다. 


    3. intr_init()

    1)      intr_init() : interrupt의 초기화를 의미한다. Interrupt의 초기화는 곧 idt의 초기화를 의미한다. Interrupt를 처리하기 위한 routine을 함수로 구현하고, 이 함수의 시작 주소를 IDT(Interrupt Descriptor Table)에 저장한다. 

     

     

    2)     pic_init() : pic의 초기화이다. PIC(Programmable Interrupt Contoller)는 cpu의 Interrupt 담당 비서라고 생각하면 된다. 즉 모든 interrupt는 pic에 연결되고, cpu는 pic와 연결되는 구조이다. (8259)

     

     

    3)     다음으로 나오는 for문은 idt의 초기화이다. 여기서 INTR_CNT는 256이다. Interrupt의 자료구조

     

     

    4)     Idtr_operand=make_idtr_operand(sizeof idt-1,idt) : Idt를 가리키는 포인터가 별도로 존재하는데 이것을 idtr이라고 한다. 여기서 idtr은 table의 시작주소를 가리키게 된다. 

     

     

    5)     다음으로 나오는 for문은 Interrupt의 이름을 unknown으로 한다. 

     

     

    6)     특정 interrupt만 이름을 지정한다. 


    4. IntrNN_stub:

    1)      Cpu가 interrupt를 받으면, 받음과 동시에 interrupt의 고유번호를 알게 된다. 또한 context를 총 3차례의 push를 하므로써 저장하는데 interrupt받자마자 context의 1/3을 stack에 저장(push)한다. 

     

     

    2)     고유번호를 통해 idt에서 intrNN_stub로 jump한다. 

     

     

    3)     intrNN_stub에서 context의 1/3을 stack에 저장한다. (총 2/3 저장 완료)

     

     

    4)     Jmp Intr_entry로 인해 intr_entry로 jump한다. 


    5. Intr_entry:

    1)      Intr_entry에서 context의 1/3을 저장한다. 이로써 모든 context가 stack에 저장되고 이를 intr_frame이라고 한다. 

     

     

    2)     Kernel의 환경을 설정한다. 

     

     

    3)     Call intr_handler를 통해 본격적으로 interrupt를 처리하는 함수로 진입한다. 


    6. Intr_exit:

    1)      3번의 push로 context를 저장하였지만, 마무리단계에서는 1번의 pop으로 context를 복원한다. 

     

     

    2)    Addl $12, $esp : intr_frame을 해산시킨다. 

     

     

    3)     Iret : interrupt return을 의미한다. 


    7. Intr_handler()

    1)      Intr_handler() : 모든 interrupt, fault, exception을 다루는 함수이다. 

     

     

    2)     Intr_frame *frame : intr_frame을 가리키는 포인터를 설정한다.  

     

     

    3)     External=frame->vec_no>=0x20&&frame->vec_no<0x30 : vec_no는 Interrupt의 고유번호이다. 고유번호가 이 범위 내에 있다면, 그 interrupt는 외부 interrupt이고 이를 external로 한다. 

     

     

    4)     External을 다룰때는 다른 interrupt를 off한다. 

     

     

    5)     In_external_intr=true : 외부 interrupt가 실행중인지 여부를 true로 한다. 

     

     

    6)     Yield_on_return=false : run인 thread로부터 time slice expired로 인해 cpu를 빼앗아도 되는지 여부를 false로 한다. 

     

     

    7)     Handler=intr_handlers[frame->vec_no] : table에서 해당 interrupt처리 함수를 찾아서 실행한다. 

     

     

    8)    In_external_intr=false : 외부 interrupt가 실행중인지 여부를 false로 한다. 

     

     

    9)    Pic_end_of_interrupt(frame->vec_no) : pic가 cpu에게 interrupt가 정상적으로 종료되었다는 ack(신호)을 보낸다.

     

     

    10)   If문을 보면, 만약 interrupt 처리 도중, time slice expired가 되어 yield_on_return이 true가 될 수 있다. 이 경우 thread_yield()함수를 실행시켜 run인 thread의 cpu를 빼앗는다. 


     8. thread_start()

    1)      thread_start() : idle thread를 만들고, thread들이 multiprogramming을 하도록 interrupt를 enable한다. 

     

     

    2)     Thread_create(“idle”,PRI_MIN,ide,&idle_started) : idle thread를 만드는 함수이다.         

              (1)    “idle” : 새로 만들 thread의 이름

              (2)   PRI_MIN : thread의 우선순위, min이므로 제일 낮은 우선순위를 가진다. 

              (3)   Idle : idle thread에서 실행할 idle code(함수)

              (4)   &idle_started : idle함수의 변수

     

     

    3)     Intr_enable() : interrupt의 enable(허용)을 나타낸다. Interrupt의 enable은 곧 multiprogramming을 하겠다라고 해석된다.


    9. Thread_create()

    1)      Thread_create() : thread를 생성하는 함수이다. 

     

     

    2)    T=palloc_Get_page(PAL_ZERO) : 새 thread의 4KB의 tcb를 생성한다. 

     

     

    3)     Init_thread(t,name,priority) : tid가 t이고 우선순위가 priority인 thread를 초기화한다. 

     

     

    4)     /*stack frame for kernel_thread()*/ ~ thread_unblock(t) : 일반적으로 context switch가 발생하면, 이전 context를 현재로 복원한다. 하지만 특별한 경우로 sheduling의 대상이 새로만든 thread라면, 이전 context가 의미없는 값일 수 있다. 따라서 처음 thread_create()로 인해 thread가 생성될 때,  fake stack frames을 만든 후 해당 thread를 ready상태로 바꾸어 ready queue에 넣는다. 

     

     

    5)     Thread는 새로 만들어지면 바로 run하지 않고 ready상태로 대기하였다가 차례가 되면 run한다. 


    10. Idle()

    1)      idle() : idle()함수로 만들어진 idle thread는 시스템이 할 일이 없을 때 실행되는 thread이다. 

     

     

    2)     For ( ; ; ) : 무한루프를 도는 for문을 통해 idle thread는 의미없는 실행을 계속한다.


    11. Thread_tick()

    1)      Thread_tick() : run하는 thread에게 cpu의 할당 시간을 부여하는 함수이다. 

     

     

    2)     첫 번째 if-else문을 보면, 현재 thread t가 idle thread인지, user thread인지, kernel thread인지 구별한다. 새 thread가 생성되면 thread_tick은 0으로 초기화 된다. 

     

     

    3)     두 번째 if문을 보면, TIME_SLICE는 일반적으로 4를 의미한다. 즉 TIME_SLICE보다 현재 thread의 cpu 소유시간이 크다면, cpu를 빼앗아야 한다. (time slice expired)

     

     

    4)     Intr_yield_on_return() :yield_on_return을 true(1)로 한다는 뜻이다. True가 되면 cpu를 빼앗는다. 


     

    728x90

    댓글

kxmjhwn@gmail.com