본문 바로가기
OS/Pintos P.J_1

[Project_1_THREADS]_ Alarm Clock

by Success T.H.I.E.F 2021. 12. 27.

들어가기전 , 핀토스에서는 프로세스와 스레드를 동일시 하고있음을 알아두자 

 

 

과제의 목표

수정전 timer_sleep()

/* devices/timer.c */
void timer_sleep (int64_t ticks) {
  int64_t start = timer_ticks ();
  while (timer_elapsed (start) < ticks) 
    thread_yield ();
}
timer_elapsed(start)는 timer_sleep이 호출된 시점에서 tick이 얼마나 지났는지 반환하는 함수이다.
tick(틱)은 컴퓨터가 켜지고 1ms 에 1씩 증가하는 값으로서 하드웨어에 달린 타이머에 의해 증가한다.
Idle()은 레디리스트에 아무스레드도 없는경우 불러오는 함수로 스레드로 CPU가 얼마나 쉬고 있는지를 보여주기 위해 있는 함수.

 

핀토스에서 timer_sleep함수는 현재의 스레드를 ticks시간동안 잠재우는 함수이다.

그런데 위 함수를 보면 스레드를 잠재우기 위해 thread_yield()를 호출해 일어나면 계속 ready리스트로 다시 데려다 놓는 busy waiting 방식으로 구현이 되어있다.

따라서 스레드가 들어왔을떄 tick시간있다 깨워줘 라고 하면 이 시간동안 스레드를 블럭시켜 별도의 리스트에 저장해 두었다가 잠을 깨울시간이 되면 unblock하여 레디리스트에 저장해 주는 방식으로CPU를 쉬도록 해준다.

 

source ./active
cd threads/
make
cd build/
pintos -- -q run alarm-multiple

수정전 실행 결과

수정전에는 idle이 한번도 안불렸다 이 말은 ready리스트가 빈적이 없어 CPU에서 항상 스레드가 돌아갔음을 의미 즉 쉬지 못했음을 보여준다.

 

우선  thread구조체에 스레드가 일어날 시간을 저장한 변수를 추가해준다.

/* /include/threads/thread.h */

struct thread {
	/* Owned by thread.c. */
	tid_t tid;                          /* Thread identifier. */
	enum thread_status status;          /* Thread state. */
	char name[16];                      /* Name (for debugging purposes). */
	int priority;                       /* Priority. */

	/* Shared between thread.c and synch.c. */
	struct list_elem elem;              /* List element. */
    
    /* PROJECT1: THREADS - Alarm Clock */
	int64_t	wakeup;						/* time to wake up */

스레드가 자러가려면 잘 수 있는 공간이 필요하다 

스레드가 잘 공간을 리스트로 선언해준다. 

/* PROJECT1: THREADS - Alarm Clock */
static struct list sleep_list;

 

이제 스레드를 실제로 잠재우는 함수인 thread_sleap함 수를 만든다.

/* /threads/thread.c */

/* PROJECT1: THREADS - Alarm Clock */
/* Make the running thread sleep. */
void thread_sleep(int64_t ticks) { //스레드 재움
	struct thread *cur;
	enum intr_level old_level;

	old_level = intr_disable(); 				/* turn off interupt */
	cur = thread_current();
	
	ASSERT(cur != idle_thread); //idle 스레드는 유지한다.

	cur -> wakeup = ticks;						/* set time to wake up */
	list_push_back(&sleep_list, &cur->elem);	/* push to sleep_list */
	thread_block();								/* make thread blocked */

	intr_set_level(old_level);					/* turn on interupt */
}

먼저 현재의 스레드를 담을 스레드 구조체(cur)를 선언한다

잠들러 들어갈동안은 interrupt가 발생하지 않게 하기 위해 inr_disable()함수로 interupt를 꺼준다.

 

또한가지 중요한점은 우리는 CPU가 쉬고있다는 것을 idle()을 호출해 idle ticks로 확인한다 따라서 Idle 스레드는 재우지 않도록 ASSERT로 막는다.

 

얼마나 잘지 기록해 주어야 하기때문에  아까 일어날 시간을 저장하기위해 만든 변수 wakeup에 얼마나 잘지 저장한다. 

 

잠을 잘 공간 sleep_list로 스레드의 상태를 보낸다.

 

스레드가 자는상태가 되었으니 thread_block()으로 스레드를 블럭한다.

 

이제 재웠으니 깨워줄때 방해할 수 있도록 아까 꺼두었던 interrupt를 다시 켜준다.

 

/* /devices/timer.c */

/* PROJECT1: THREADS - Alarm Clock */
void
timer_sleep (int64_t ticks) {
	int64_t start = timer_ticks ();

	ASSERT (intr_get_level () == INTR_ON);
	// while (timer_elapsed (start) < ticks)
	// 	thread_yield ();   //busy wait
	thread_sleep(start + ticks);
}

timer sleep함수가 호출되면 이제 진행중이던 스레드는 ready로 들어가는대신 블락 상태가 될것이고 이 스레드들은 sleep_list내에 있을것이다.

 

이제 깨우는 과정이 필요하다. 

/* /threads/thread.c */

/* PROJECT1: THREADS - Alarm Clock */
/* Makes the sleeping thread wake up. */
void thread_awake(int64_t ticks) {
	struct list_elem *e = list_begin(&sleep_list);
	while (e != list_end (&sleep_list)) { 
		struct thread *t = list_entry(e, struct thread, elem);
		if (t -> wakeup <= ticks) { //검사한 스레드의 깨워달라는 시간이 현재와 같거나 이전이면? 깨워야한다.!
			e = list_remove(e); //슬립 리스트에서 현재 스레드의 상태를 지워준다.
			thread_unblock(t); //현재 스레드를 깨운다.
		}
		else 
			e = list_next(e); //다음 스레드를 검사한다
	}
}

슬립 리스트가 비워질 떄까지 자는상태에 있는 스레드들을 현재시간과 깨워달라고 했던 시간들을 비교한다

 

타이머의 인터럽트는 ticks를 증가시키며 thread_ticks를 증가시킨다. 여기에 깨우는 함수를 추가한다.

/* devices/timer.c */


/* Timer interrupt handler. */
static void timer_interrupt (struct intr_frame *args UNUSED) {
	ticks++;
	thread_tick ();
	thread_awake(ticks); /* PROJECT1: THREADS - Alarm Clock */
}

이제 이 함수들을 모두 스레드 헤더에 담아주어 tread.c에서 사용되도록 한다.

/* /threads/thread.c */

/* PROJECT 1 : Threads - Alarm Clock */
void thread_sleep(int64_t ticks);
void thread_awake(int64_t ticks);

수정후 실행결과

코드를 수정하니 idle()이 550번 호출되었다. 이만큼 CPU는 ready리스트가 비어있었다고 볼 수있다 

'OS > Pintos P.J_1' 카테고리의 다른 글

[Project_1_THREADS]_Priority Scheduling_2  (0) 2021.12.30
[Project_1_THREADS]_Priority Scheduling_1  (0) 2021.12.28