거북이의 쉼터

(2021.10.23) Mlfqs - 기타 구현 및 디버깅 본문

코딩 삽질/KAIST PINTOS (CS330)

(2021.10.23) Mlfqs - 기타 구현 및 디버깅

onlim 2021. 10. 23. 14:17

1. 서론 및 필요 내용 설명

이제 얼마 안 남았다. synch.c와 thread.c에서 priority donation과 priority calculation이 thread_mlfqs 환경에서 실행되지 않도록만 하면 된다. 또한, 테스트 케이스에 사용되는 함수들을 구현하면 끝이다. 빨리 해치우자.

 


2. 구현해야 하는 것

  • priority_donate(), priority_calculate()가 실행되기 위한 조건 추가
  • 테스트 평가 함수 구현

 


3. 구현 과정

3-1. 조건 추가

priority_donate()는 lock_acquire()에서만 호출되므로 해당 함수를 다음과 같이 수정한다.

void
lock_acquire (struct lock *lock) {
	ASSERT (lock != NULL);
	ASSERT (!intr_context ());
	ASSERT (!lock_held_by_current_thread (lock));

	enum intr_level old_level;
	old_level = intr_disable();

	struct thread *cur = thread_current ();
	
	if (!thread_mlfqs && lock->holder)
	{
		cur->wait_on_lock = lock;
		priority_donate (cur, lock->holder);
	}

	sema_down (&lock->semaphore); // gets blocked when there is holder
	
	cur->wait_on_lock = NULL;
	list_push_back (&cur->lock_list, &lock->lock_elem);
	
	lock->holder = thread_current ();

	intr_set_level (old_level);
}

priority_calculate()는 sema_up()과 thread_set_priority()에 사용되므로 다음과 같이 정정한다.

/* synch.c */
void
sema_up (struct semaphore *sema) {
	enum intr_level old_level;

	ASSERT (sema != NULL);

	old_level = intr_disable ();
	if (!list_empty (&sema->waiters))
	{
		list_sort(&sema->waiters, cmp_priority, NULL);
		thread_unblock (list_entry (list_pop_front (&sema->waiters),
					struct thread, elem));
	}
	sema->value++;

	if (!thread_mlfqs)
		priority_calculate (thread_current());

	preemption ();
	intr_set_level (old_level);
}

/* thread.c */
void
thread_set_priority (int new_priority) {
	if (!thread_mlfqs)
	{
		thread_current ()->org_priority = new_priority;
		priority_calculate (thread_current ());
		preemption ();
	}
}

3-2. 테스트 케이스 함수 구현

테스트 케이스에서 호출되는 함수를 주석에서 요청하는 대로 구현한다. 

/* Sets the current thread's nice value to NICE. */
void
thread_set_nice (int nice UNUSED) {
	/* TODO: Your implementation goes here */
	thread_current ()->nice = nice;
	mlfqs_update_thread_priority (thread_current());
	preemption();
}

/* Returns the current thread's nice value. */
int
thread_get_nice (void) {
	/* TODO: Your implementation goes here */
	return thread_current ()->nice;
}

/* Returns 100 times the system load average. */
int
thread_get_load_avg (void) {
	/* TODO: Your implementation goes here */
	return fp_to_nearest(load_avg * 100);
}

/* Returns 100 times the current thread's recent_cpu value. */
int
thread_get_recent_cpu (void) {
	/* TODO: Your implementation goes here */
	return fp_to_nearest(thread_current ()->recent_cpu * 100);
}

thread_set_nice()에서 mlfqs_update_thread_priority()와 preemption()을 호출하는 이유는 매뉴얼에서 다음과 같이 언급하고 있기 때문이다.

Sets the current thread's nice value to new nice and recalculates the thread's priority based on the new value (see Calculating Priority). If the running thread no longer has the highest priority, yields.

 


4. 디버깅

여기까지 진행하고 make check를 했다.

 

앞선 프로젝트에는 영향이 없는 것을 확인할 수 있고, 4개의 테스트가 실패하는 것을 볼 수 있다. 문제 원인을 파악하기 위해 mlfqs-recent-1 테스트를 개별 실행했다.

 

recent_cpu가 0.00으로 너무 작다는 것을 확인할 수 있다. 코드에 어떤 문제가 있나 살펴봤는데 recent_cpu는 fp_t라서 매 tick마다 increment를 해 주는 함수에서 곧이곧대로 1을 더하면 안된다는 사실을 잊어버리고 있었다. 정수 1을 fp_t로 만들어준 뒤 더해야 하는데 변환을 하지 않았다. 바로 고쳐서 다시 make check를 해 주었다.

void
mlfqs_increment_recent_cpu (void)
{
	if (thread_current () != idle_thread)
		thread_current ()->recent_cpu += n_to_fp(1);
}

그 결과 깔끔하게 모두 통과한다.

 

 


5. 후기

Mlfqs는 처음하는 것치고는 꽤 간단한 프로젝트였다. 다음에 할 User Program에서도 이 기세를 이어나갔으면 좋겠다.

Comments