기존 Pintos의 알람 기능은 Busy Waiting
으로 구현되어있는데, 프로세스(pintos에서 스레드와 프로세스는 동일한 개념이다)가 잠을 잘때, 일정한 시간마다 일어나서 아침이 됐는지 확인하는 방식이다. 따로 block 시키지 않고 yield하여 ready list의 맨 뒤로 보내는 것이다.
이 방식은 cpu가 busy하게 매 tick마다 확인하기 때문에, 하려고 하는 일을 못하여 많은 시스템 자원을 낭비하는 비효율적인 방법이므로
Alarm System Call
과제를 통해 시스템 자원 낭비를 최소화 하고자 한다.
Busy Waiting
살펴보기/* device/timer.c */
void timer_sleep (int64_t ticks) {
int64_t start = timer_ticks (); // start : 시작시 시간(tick)
ASSERT (intr_get_level () == INTR_ON); //인터럽트가 들어왔을때만 실행
while (timer_elapsed (start) < ticks) //start로부터 tick만큼 시간이 지나기 전까지
thread_yield (); //cpu를 양보한다.
}
tick
= 컴퓨터가 켜지고 10ms에 1씩 증가하는 값
timer_elapsed(start)
= timer_sleep이 호출된 시점에서 몇 tick이 지났는지 반환함
이 함수의 반환값이 timer_sleep의 인자인 ticks값보다 작으면 thread_yield()
를 호출하여 ready list에 있는 다른 스레드를 위해 CPU를 반환하고 ready list 가장 뒤로 이동.
이 과정은 ticks동안 반복되고 이러한 방식으로 핀토스는 timer_sleep을 구현
자야 할 프로세스들을 READY상태로 두어 while문 반복하는것임
잠을 자야할 프로세스들을 Block
상태로 만들어 list에 넣어둔 뒤, 깰 시간이 되면 다시 Ready
상태로 바꿔주는 것.
Sleep-Awake
간단하게 설명하자면, 자는 thread를 한 list에 모아놓고, 인터럽트(인터럽트가 발생할때마다 1 tick이 올라간다) 때마다 깨어나야할 thread를 찾아서 깨워준다.
sleep_list
를 만들고 일어날 시간을 thread 구조체 안에 기록한다./* thread/thread.h */
struct thread{
...
int64_t wakeup; // 깨어나야 하는 ticks 값
...
}
/* thread/thread.c */
static struct list sleep_list;
void thread_init(void){
...
list_init(&ready_list);
list_init(&all_list);
list_init(&sleep_list);
...
}
/* thread를 ticks시각까지 깨우는 함수 */
void thread_sleep(int64_t ticks){
// 인터럽트를 금지하고 이전 인터럽트 레벨을 저장함.
enum intr_level old_level = intr_disable();
//cur은 idle이 아니어야 하는데, idle은 sleep 하지말아야 한다 (sleep되면 cpu가 꺼져버림)
struct thread *cur = thread_current();
ASSERT(cur != idle_thread);
//awake함수가 실행되어야할 tick값을 update
update_next_tick_to_wake(cur->wakeup = ticks);
list_push_back(&sleep_list, &cur->elem);
//이 thread를 block하고 다시 scheedule 될때 까지 blocked상태로 wait
thread_block(); //interrupt를 꺼줘야만 작동함
// 인터럽트를 다시 받아들이도록 수정
intr_set_level(old_level);
}
sleep list
를 탐색하여 일어날 thread
를 찾는 함수를 만든다./* thread/thread.c */
/* 푹 자고 있는 thread 중에 깨어날 시각이 ticks 시각이 지난 애들을 모조리 깨우는 함수 */
void thread_awake(int64_t wakeup_tick){
struct list_elem *e = list_begin(&sleep_list);
enum intr_level old_level = intr_disable();
next_tick_to_awake = INT64_MAX;
while (e!=list_end(&sleep_list)){
struct thread *t = list_entry(e, struct thread, elem);
if(wakeup_tick >= t->wakeup){ //thread가 일어날 시간이 되었는지 확인
e = list_remove(&t->elem); //sleep list에서 제거
thread_unblock(t); //스레드 unblock
}else{
e = list_next(e);
}
}
intr_set_level(old_level);
}
/* devices/timer.c */
static void
timer_interrupt (struct intr_frame *args UNUSED)
{
ticks++;
thread_tick ();
thread_awake (ticks); // ticks 가 증가할때마다 awake 작업 수행
}