ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [WEEK08] Pintos PROJECT1 : THREADS
    SW Jungle/TIL (Today I Learned) 2022. 11. 17. 06:41

    SW정글 8주차 과정인 "Pintos PROJECT1 : THREADS"를 수행하며, 배운 내용을 정리하려 한다.

     

    Project 1 : Threads에서 구현하는 기능은 다음과 같다.

      - Alarm Clock

      - Priority Scheduling

     

    이 글에서는 Alarm Clock만 다룰 예정이다.

     

     

    Alarm Clock

    알람시계라고 해서 이런 알람시계를 생각하면 안된다... 하지만 하는 짓은 비슷하긴 하다.

    /* 처음에 제공된 timer_sleep() 함수 */
    
    /* Suspends execution for approximately TICKS timer ticks. */
    void
    timer_sleep (int64_t ticks) {
    	int64_t start = timer_ticks ();
    
    	ASSERT (intr_get_level () == INTR_ON);
    	while (timer_elapsed (start) < ticks)
    		thread_yield ();
    }
    구현 목표 : timer_sleep() 함수를 재구현한다.

    timer_sleep() 함수는 ticks만큼의 시간동안 프로그램의 진행을 정지시켜준다.
    C언어에서 <stdlib.h>가 지원하는 기본 Sleep() 함수와 기능이 유사하다.

    이 함수는 busy wait를 하는데, busy wait하지 않도록 재구현해주는 것이 목표이다.

     

     

     

    우선 busy wait가 뭘까?

     

     

     

    printf("I will wait...\n");
    timer_sleep(100);
    printf("Waiting done!\n");

    위의 예시 코드를 통해 설명하면,

    첫번째 라인의 printf를 실행하고 세번째 라인으로 넘어가기 전에

    두번째 라인에서 sleep을 하게 되는데,

    이 때 이 프로그램은 기다리는 것 말고는 딱히 하는 일이 없음에도 불구하고 CPU를 차지하고 있다.

     

    그래서 우리가 해야 할 일은, 기다리는 동안에 다른 프로그램이 실행될 수 있게끔 코드를 수정해 주는 것이다.

     

     

     

    /* 
     timer.c
     */
    
    /* Suspends execution for approximately TICKS timer ticks. */
    void
    timer_sleep (int64_t ticks) {
    	int64_t start = timer_ticks ();
    	ASSERT (intr_get_level () == INTR_ON);
    	thread_sleep(start + ticks);
    }
    
    /* Timer interrupt handler. */
    static void
    timer_interrupt (struct intr_frame *args UNUSED) {
    	ticks++;
    	thread_tick ();
    	thread_wakeup (timer_ticks());
    }
    
    
    /* 
     thread.c
     */
    
    void
    thread_sleep (int64_t ticks) {
    	struct thread *curr = thread_current ();
    	enum intr_level old_level;
    	ASSERT (!intr_context ());
    
    	old_level = intr_disable ();
    	if (curr != idle_thread) {
    		curr->time_to_wake_up = ticks;
    		list_push_back(&sleep_list, &curr->elem);
    		thread_block();
    	}
    	intr_set_level (old_level);
    }
    
    void
    thread_wakeup (int64_t ticks) {
    	struct list_elem *node = list_begin(&sleep_list);
    	struct list_elem *end_node = list_end(&sleep_list);
    	struct list_elem *tmp;
    	struct thread *t;
    
    	ASSERT(intr_context());
    
    	while (node != end_node) {
    		tmp = node;
    		node = list_next(tmp);
    
    		t = list_entry(tmp, struct thread, elem);
    		if (t->time_to_wake_up <= ticks) {
    			list_remove(tmp);
    			thread_unblock(t);
    		}
    	}
    }

    그래서 나는 thread_sleep() 과 thread_wakeup() 이라는 함수를 따로 만들었다.

    자세한 구현을 설명하려면 너무 길어서, 논리 위주로 설명하겠다.

     

     

     

    기본적으로 pintos에서는 tick 단위로 시간이 흘러간다.

    그리고 1초에 100ticks가 올라갈 수 있도록 하기 위해서 timer_interrupt() 라는 핸들러 함수가 주기적으로 호출된다.

     

    그래서 이 함수를 이용하여 특정 시간이 되었을 때 깨워주는 thread_wakeup()을 호출해 줄 것이다.

     

    그러면 어떻게 재우고 어떻게 깨우는가?

    자고 있는 thread들을 보관하는 sleep_list를 만들어 주고

    재울때는 thread_sleep()으로 여기에 넣어준다.

    깨울때는 thread_wakeup()으로 여기서 깨워준다.

     

    재울 때 thread 구조체 안에 time_to_wake_up이라는 시간값 (ticks)을 같이 넣어주기 때문에,

    시간이 흘러갈 때마다 호출되는 thread_wakeup()은 호출될 때 마다 sleep_list를 순회하며 깨울 시간이 된 thread를 깨워주게 된다.

     

    한 가지 주의할 점은, 적절한 타이밍에 interrupt를 걸어주고 해제해줘서

    중요한 데이터가 수정되어야 하는 구간, 즉 원자성이 보존되어야 하는 코드 구간에서 context-switching이 일어나지 않게 함으로써 원자성이 보존되도록 신경써줘야 한다는 것이다.

     

     

     

     

    pintos에서, 정말 운영체제가 이런 일을 한다는 걸 배우는 것도 중요하지만

    pintos의 과제를 구현하는 데 필요한 설계 능력과 구현 능력... 복잡한 구조를 직접 구현해봄으로써 나의 능력을 키우는 것이 참 중요하다는 것을 느꼈다.

    댓글

Copyright 2022. ProdYou All rights reserved.