거북이의 쉼터

(2021.10.05) Alarm Clock - timer_interrupt() 수정 본문

코딩 삽질/KAIST PINTOS (CS330)

(2021.10.05) Alarm Clock - timer_interrupt() 수정

onlim 2021. 10. 5. 13:28

1. 서론 및 필요 내용 설명

지난 포스팅에서 timer_sleep()까지는 전부 코딩했다. 이제 thread를 재우는 것 까지는 루틴이 완료됐으니, 이제 thread를 깨울 차례이다. thread를 깨우는 타이밍은 매 tick마다 타이머 인터럽트로 인해 호출되는 timer_interrupt() 함수에서이며, 현재의 tick과 sleep_list에 있는 thread의 wakeup_tick을 비교해 thread를 깨울 것이다.

 

이번 구현에서는 별로 설명할 것이 없기에 바로 구현해야 할 것을 짚고 구현해보자.

 


2. 구현해야 하는 것

  • timer_interrupt()에서 현재의 tick과 sleep_list 내의 thread의 wakeup_tick을 비교하는 것
  • 비교 후 깨울 thread의 Unblock 루틴

 


3. 구현 과정

우선 기존 timer_interrupt()의 코드는 다음과 같은데,

/* Timer interrupt handler. */
static void
timer_interrupt (struct intr_frame *args UNUSED) {
	ticks++;
	thread_tick ();
}

ticks를 증가시키고, 현재 돌아가고 있는 thread가 무엇인지 판단해 kernel thread, user thread, idle tick을 증가시킨다. 여기서 ticks 증가 이후에 sleep_list를 참조해주면 된다. 이전 포스팅에서 sleep_list는 wakeup_tick이 증가하는 순으로 정렬을 시켰기 때문에, 일정 기점 이후의 thread는 조사하지 않고 리턴하면 된다. 물론, sleep_list는 여전히 thread.c에서만 참조할 수 있기에, 보조 함수를 하나 생성해 호출하는 방식으로 sleep_list를 참조한다.

/* Timer interrupt handler. */
static void
timer_interrupt (struct intr_frame *args UNUSED) {
	ticks++;
	thread_wakeup (ticks);
	thread_tick ();
}

이제 thread_wakeup을 구현한다. sleep_list에서 한 개씩 맨 앞에 놓여있는 thread의 wakeup_tick을 보며, 깨워야 할 thread라면, sleep_list에서 먼저 제거한 뒤, thread_unblock을 호출하여 깨워준다. 이대로 구현한 것이 아래 코드이다.

void
thread_wakeup (int64_t cur_tick) {
	struct list_elem *e;
	struct thread *t;

	while (!list_empty (&sleep_list))
	{
		e = list_front (&sleep_list);
		t = list_entry (e, struct thread, elem);
		
		if (cur_tick < t->wakeup_tick)
			break;

		list_pop_front(&sleep_list);
		thread_unblock(t);
	}
}

이제 다 된 것 같으니 컴파일 후 실행을 해 보자.

 


4. 디버깅

우선 여기까지 구현했을 때, 제대로 동작해야 할 테스트 케이스는 아래와 같다.

 

  • test_alarm_single
  • test_alarm_multiple
  • test_alarm_simultaneous
  • test_alarm_zero
  • test_alarm_negative

제목이 직관적이라 뭘 하는지는 코드를 보면 쉽게 이해할 수 있으므로 구체적인 설명은 생략하고, 개별 코드 실행을 어떻게 해야 하는지만 짚고 넘어가자. 우선 make를 한 뒤 build 바깥에서 pintos를 호출하면

os.dsk cannot be temporal.

을 뱉으며 그냥 죽어버린다. utils/pintos 파일을 보면 os.dsk 경로를 못 찾아서 생기는 현상이므로, os.dsk가 존재하는 build에서 실행해주면 문제가 없을 것이다. 구체적인 개별 실행 명령은 make check를 했을 때 나오는 명령어를 참조하였다.

pintos -v -k -T 60 -m 20   -- -q   run alarm-single
pintos -v -k -T 60 -m 20   -- -q   run alarm-multiple
pintos -v -k -T 60 -m 20   -- -q   run alarm-simultaneous
pintos -v -k -T 60 -m 20   -- -q   run alarm-zero
pintos -v -k -T 60 -m 20   -- -q   run alarm-negative

이제 개별적으로 실행을 한 뒤, idle 시간이 0이 아니면 성공한 것이다. busy wait이 발생할 경우, 실행된 tick이 모두 kernel tick으로 집계되기 때문에 기존 Pintos 구현에서는 다음과 같은 결과가 나온다.

$ pintos -v -k -T 60 -m 20   -- -q   run alarm-single
...
Execution of 'alarm-single' complete.
Timer: 294 ticks
Thread: 0 idle ticks, 294 kernel ticks, 0 user ticks
...

그리고 수정 결과 이렇게 idle tick이 늘어난 것을 확인할 수 있다.

$ pintos -v -k -T 60 -m 20   -- -q   run alarm-single
...
Execution of 'alarm-single' complete.
Timer: 294 ticks
Thread: 250 idle ticks, 44 kernel ticks, 0 user ticks
...

make check를 잠시 진행했을 때, 나머지 테스트 케이스에서도 통과하는 것을 보아, 제대로 구현이 된 것을 알 수 있다.

 


5. 후기

Pintos 첫 코딩을 마치며 소감은 이런 식으로 일지를 남기는 것이 정말 어렵다는 것이다. 내 생각을 정리된 글로 풀어쓴다는 것이 쉬운 일이 아니고, 특히 이걸 더 복잡한 앞으로의 과제에서 해야 한다는 것이 내 의욕을 갉아먹는다. 그래도... 해야지 뭘...

Comments