
핀토스에 영혼을 갈았더니 정작 중요한 개념 공부를 소흘히 해서 퀴즈에서 헷갈리는 게 많았음. 아래에서 정리해봅시다.

-mlfqs 들어가면 thread_mlfqs = true 처리되는건 이미 코드상 있음.
thread_mlfqs를 가지고 조건문 처리 할 일이 많음.원래는 priority가 0부터 63까지 존재하므로 64개의 큐를 준비해서 구현하는 게 정석이지만

ready_list)로 먼저 만들어 보고, 시간 남으면 여러 큐를 만들어 보기로 함.[구현 4-0] load_avg 전역변수 추가
[구현 4-1] struct thread에 구조체 멤버 추가
int nice, int recent_cpuinit_thread)[구현 4-2] mlfqs 사용 시 thread_set_priority 비활성화
if (thread_mlfqs) return; 으로 바로 반환하게 하면 됨[구현 4-3] mlfqs 사용 시 lock_acquire, lock_release의 priority donation 비활성화
[★★구현 4-4★★] priority 계산을 위한 함수 만들기
int에 저장해야 했음... 매크로 함수로 미리 필요한 부분을 정의하니까 조금 더 수월했음.
정수형과 정수형 간 계산은 기존처럼 해도 됨
하지만 정수형/부동소수점 or 부동소수점 간 계산은 아래와 같은 특이한 형태로 진행해야 함
x, y는 부동소수점, n은 정수형정수형이든 부동소수점이든 int에 저장함에 유의
1 << 14로 나누어 정수 형태로 바꾸어주어야 했음암튼 만들어야 하는 건
recent_cpu를 1 증가시키는 함수. (매 틱)load_avg를 재계산하는 함수. (매 초)load_avg = (59 / 60) * load_avg + (1 / 60) * ready_threadsrecent_cpu를 재계산하는 함수. (매 초)recent_cpu = (2 * load_avg)/(2 * load_avg + 1) * recent_cpu + nicepriority를 재계산하는 함수. (매 타임슬라이스)priority = PRI_MAX - (recent_cpu / 4) - (nice) * 2 후 인접정수로 내림.\thread_mlfqs가 참일 때만 실행.#define CALC_F (1 << 14)
#define FLOAT(n) ((n) * (CALC_F)) // 정수 n -> 부동소수점 반환
#define INT(x) ((x) / (CALC_F)) // 부동소수점 x -> 정수 반환
#define MUL_FLOATS(x, y) (((int64_t)(x)) * (y) / CALC_F) // 부동소수점 간 곱셈
#define DIV_FLOATS(x, y) (((int64_t)(x)) * CALC_F / (y)) // 부동소수점 간 나눗셈
void recent_cpu_up_one(void){
// `recent_cpu`를 1 증가시키는 함수.
// recent_cpu는 fixed-point.
if(thread_mlfqs){
(thread_current() -> recent_cpu) += 1 * (CALC_F);
}
}
void calc_load_avg(void){
// `load_avg`를 재계산하는 함수.
// load_avg는 fixed point, ready_threads는 integer
if(thread_mlfqs){
int ready_threads = list_size(&ready_list);
if (thread_current() != idle_thread){
ready_threads += 1;
}
//printf("ready_threads %d, load_avg %d\n", ready_threads, load_avg);
load_avg = (MUL_FLOATS(DIV_FLOATS(FLOAT(59), FLOAT(60)), load_avg) + DIV_FLOATS(FLOAT(1), FLOAT(60)) * ready_threads);
}
}
void calc_recent_cpu(struct thread *t){
// `recent_cpu`를 재계산하는 함수
if(thread_mlfqs){
// load_avg, recent_cpu, decay도 fixed point. nice는 integer.
int decay = DIV_FLOATS(2 * load_avg, 2 * load_avg + CALC_F);
int result = MUL_FLOATS(decay, t -> recent_cpu) + FLOAT(t -> nice);
t -> recent_cpu = result;
}
}
void calc_priority(struct thread *t){
// `priority`를 재계산하는 함수
if(thread_mlfqs){
// priority, nice는 integer. recent_cpu는 fixed point.
t -> priority = INT(FLOAT(PRI_MAX) - (t -> recent_cpu / 4) - FLOAT(t -> nice * 2));
}
}
[구현 4-4A] running, ready, blocked 상관없이 모든 쓰레드 대상으로 값을 갱신하는 함수 만들기
priority 재계산하는 함수.recent_cpu 재계산하는 함수.all_list를 만들어 사용struct list_elem으로는, donation 안 하면서 남아도는 d_elem을 그대로 사용함.thread_init 함수에서 main 쓰레드를 만든 뒤, all_list에 삽입thread_create에서 새로운 쓰레드가 만들어지면 삽입thread_exit에서 쓰레드를 없애는 경우 삭제all_list를 순회하면서 recent_cpu, priority를 갱신하자.[구현 4-5] set, get 함수 몇 개 만들기
void thread_set_nice(int nice UNUSED): 현재 쓰레드의 nice 설정int thread_get_nice(void): 현재 쓰레드의 nice 반환int thread_get_load_avg(void): load_avg * 100 반환. 이때 1 << 14로 나누어 정수형으로 변환해야 해야 테스트 케이스에서 잘 채점됨.int thread_get_recent_cpu(void): recent_cpu * 100 반환. 이때 1 << 14로 나누어 정수형으로 변환해야 해야 테스트 케이스에서 잘 채점됨.int thread_get_load_avg (void) {
return INT(load_avg * 100);
}
int
thread_get_recent_cpu (void) {
return INT(thread_current() -> recent_cpu * 100);
}
timer_interrupt 함수에서 멤버를 갱신하게끔 설정recent_cpu: 매 틱마다 1씩 더함.load_avg, recent_cpu: timer_ticks () % TIMER_FREQ == 0 (매 초) 일 때 재계산priority 재계산: timer_ticks () % 4 == 0 (매 4 * 틱)일 때 재계산/* [구현] Timer interrupt handler. */
static void
timer_interrupt (struct intr_frame *args UNUSED) {
ticks++;
// [구현 4-7] timer_interrupt마다 갱신할 멤버들 생신
//enum intr_level old_level;
//old_level = intr_disable();
if(timer_ticks() % 4 == 0){
// 모든 쓰레드의priority 재계산
calc_all_priority();
}
recent_cpu_up_one();
if(timer_ticks() % TIMER_FREQ == 0){
calc_load_avg();
calc_all_recent_cpu();
}
thread_tick ();
// 깨울 쓰레드 찾고, 실제로 깨우기
wake_up(ticks);
}