기본 개념
Alarm Clock : 스레드가 특정 시간 동안 잠들어 있다가 정해진 시간이 되면 자동으로 깨어나는 기능
thread.h
wakeup_tick
해당 스레드가 깨워져야하는 시점을 나타내는 값(= 스레드가 sleep 상태로 들어갈 때, 몇 번째 tick(인터럽트 타이머 기준)이 되었을 때 깨워야 하는 지를 저장)
struct thread {
// 이미 존재하는 코드...
int64_t wakeup_tick; /* 스레드를 깨우기 위한 틱 수 */
// 이미 존재하는 코드...
};
C
복사
시스템의 tick 카운터와 비교하여 지정된 tick이 되면 sleep_list에서 해당 스레드를 깨워 ready 상태로 전환하는 데 사용
sleep_list
현재 sleep(대기) 상태에 있는 스레드들을 담는 리스트
extern struct list sleep_list; // sleep 상태인 스레드들을 담는 리스트
C
복사
extern 키워드를 사용해 해당 변수가 다른 파일에서 정의되어 있음을 표현 → thread.c 파일에서는 선언만 하고, 실제 메모리 할당 및 초기화는 다른 소스 파일에서 진행(여러 파일에서 sleep_list를 사용 가능하도록)
각 스레드는 자신의 wakeup_tick 값에 따라 sleep_list 에 저장됨. 스레드가 특정 시간(tick)까지 잠들어 있어야할 때 해당 스레드를 이 리스트에 삽입. 타이머 인터럽트가 발생할 때마다 sleep_list 를 확인하여 깨워야할 tick이 된 스레드를 깨워 ready 상태로 전환시킴
thread.c
전역 변수(sleep_list)
struct list sleep_list; // timer.c 파일에서 사용 : sleep 상태인 스레드들을 담는 리스트
C
복사
thread_init()
void
thread_init (void) {
// 동일한 코드...
list_init (&sleep_list);
// 동일한 코드...
}
C
복사
timer.c
timer_sleep()
void
timer_sleep (int64_t ticks) {
struct thread* current_thread;
enum intr_level old_level;
ASSERT(intr_get_level() == INTR_ON);
// negative, zero 테스트에 대한 처리(예외 처리)
if(ticks <= 0)
{
return;
}
// 먼저 인터럽트 끄기(핸들러와의 레이스 컨디션 방지)
old_level = intr_disable();
current_thread = thread_current();
current_thread->wakeup_tick = (timer_ticks()) + ticks; // ticks만큼 후에 깨우기
list_insert_ordered(&sleep_list, ¤t_thread->elem, wakeup_tick_compare, NULL); // sleep_list에 현재 스레드 추가(오름차순 정렬)
thread_block(); // 현재 스레드를 block 상태로 바꾸기
intr_set_level(old_level); // 인터럽트 다시 켜주기
}
C
복사
wakeup_tick_compare()
/* insert_ordered 함수에 사용할 비교 함수 */
static bool wakeup_tick_compare(const struct list_elem* a, const struct list_elem* b) {
// 두 스레드의 wakeup_tick 값을 비교해 더 작은 값을 가진 스레드를 앞에 오도록 하기
struct thread* thread_a = list_entry(a, struct thread, elem);
struct thread* thread_b = list_entry(b, struct thread, elem);
return thread_a->wakeup_tick < thread_b->wakeup_tick;
}
C
복사
timer_interrupt()
static void
timer_interrupt (struct intr_frame *args UNUSED) {
ticks++;
thread_tick ();
/*
* sleep_list에 있는 스레드들 중에서 깨워야 할 스레드가 있는지 확인
* 타이머 인터럽트 : 1초에 100번 발생 -> 매 인터럽트마다 sleep_list에서 깨울 스레드가 있는지 확인
*/
struct list_elem* elem = list_begin(&sleep_list);
while(elem != list_end(&sleep_list))
{
struct thread* td = list_entry(elem, struct thread, elem);
if(td->wakeup_tick <= ticks)
{
elem = list_remove(elem); // 깨울 스레드는 리스트에서 제거하고 다음 엘리먼트로 이동
thread_unblock(td); // 스레드 깨우기
}
// 오름차순 정렬 상태에서 ticks보다 큰 wakeup_tick을 가진 스레드부터는 볼 이유 없음
else
{
break;
}
}
}
C
복사

