-
[WEEK08] Pintos PROJECT1 : THREADSSW 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의 과제를 구현하는 데 필요한 설계 능력과 구현 능력... 복잡한 구조를 직접 구현해봄으로써 나의 능력을 키우는 것이 참 중요하다는 것을 느꼈다.
'SW Jungle > TIL (Today I Learned)' 카테고리의 다른 글
[WEEK11] 페이징은 왜 하는걸까 (Pintos PROJECT3 : VIRTUAL MEMORY) (0) 2022.12.06 [WEEK09~10] syscall wait()의 구현 - 구조 설계를 중점으로 (Pintos PROJECT2 : USER PROGRAMS) (0) 2022.11.29 [WEEK07] C언어에서 문자열 다루기. 근데 이제 포인터와 파싱(parsing)을 곁들인 (0) 2022.11.10 [WEEK06] Malloc Lab 설명서 번역 (CS:APP) (2) 2022.10.28 [WEEK05] C언어로 구현하는 레드-블랙 트리 (Red-Black Tree) (1) 2022.10.27