운영체제(OS)/with PintOS

(Project1-Threads) - Alarm Clock

스탠딩 2023. 12. 26. 21:15

** Pintos 구현의 모든 순서는 pintos-kaist git book을 따른다.

** 코드 구현에 대한 설명은 주석을 참고!

Alarm Clock

`devices/timer.c`에 정의된 `timer_sleep()` 함수를 다시 구현하세요.
현재 제공된 구현은 시간이 충분히 경과할 때까지 현재 시간을 확인하고 `thread_yield()`를 호출하는 루프에서 busy wait(바쁜 대기)하는 방식입니다. busy wait를 피하도록 다시 구현하세요.

void timer_sleep (int64_t ticks);

호출한 스레드의 실행을 일시 중단하고 적어도 x 타이머 틱이 경과할 때까지 기다립니다. 시스템이 그 외에 유휴 상태가 아닌 한 스레드는 정확히 x 틱 후에 깨어나지 않아도 됩니다. 올바른 시간만큼 기다린 후에는 해당 스레드를 준비 큐(ready queue)에 넣습니다.

struct sleeping_thread { // 구조체 생성, 기존에 있는 스레드 구조체에 멤버를 추가해도 됨.
    struct thread *t;
    int64_t wake_tick;
    struct list_elem elem;
};
static struct list sleep_list; // sleep_list 생성, thread_init할 때 초기화
void timer_sleep(int64_t ticks) {
    ASSERT(intr_get_level() == INTR_ON);
    thread_sleep(ticks); // 매번 체크하는 busy_waiting 대신 주어진 ticks만큼 sleep하는 함수
}
void thread_sleep(int64_t ticks) {
    struct thread *curr = thread_current();
    enum intr_level old_level;

    ASSERT(!intr_context());
    old_level = intr_disable(); // 원자성을 위해 인터럽트 비활성화

    struct sleeping_thread st;
    st.t = thread_current(); // thread_sleep을 호출한 현재 스레드
    st.wake_tick = timer_ticks() + ticks; // 현재 tick + 주어진 ticks = 깨어날 tick

    if (curr != idle_thread) { // 현재 스레드가 idle_thread가 아니면
        list_push_back(&sleep_list, &(st.elem)); // sleep_list에 st.elem(sleep_thread_elem)을 push
    }
    do_schedule(THREAD_BLOCKED); // 스케쥴링
    intr_set_level(old_level); // 인터럽트 활성화
}
void thread_wake(void) {
    /* Wake sleeping thread with past time */
    struct list_elem *tmp;
    struct sleeping_thread *tmp_thread;
    if (!list_empty(&sleep_list)) { // sleep_list를 순회하면서
        for (tmp = list_begin(&sleep_list); tmp != list_end(&sleep_list); tmp = list_next(tmp)) { 
            tmp_thread = list_entry(tmp, struct sleeping_thread, elem);
            if (tmp_thread->wake_tick <= timer_ticks()) { // 깰 시간인 스레드는
                list_remove(tmp); // 리스트에서 제거 후
                thread_unblock((tmp_thread->t)); // unblock
            }
        }
    }
}

`timer_sleep()` 함수는 실시간 작업을 수행하는 스레드에 유용합니다. 예를 들어 초당 한 번 깜빡이는 커서 등이 있습니다. `timer_sleep()` 함수의 인수는 타이머 틱 단위로 표현되며, 밀리초 또는 다른 단위로 표현되지 않습니다. 초당 TIMER_FREQ 타이머 틱이 있으며, TIMER_FREQ는 devices/timer.h에 정의된 매크로입니다. 기본값은 100입니다. 이 값을 변경하지 않는 것이 좋습니다. 변경하면 많은 테스트가 실패할 가능성이 있기 때문입니다.
특정 시간(밀리초, 마이크로초, 나노초) 동안 슬립하는 데 사용되는 별도의 함수인 `timer_msleep()`, `timer_usleep()`, `timer_nsleep()` 함수도 있습니다. 그러나 필요할 때 자동으로 `timer_sleep()`를 호출합니다. 이러한 함수들을 수정할 필요는 없습니다. 알람 클록 구현은 이후 프로젝트에서 필요하지 않지만, 프로젝트 4에서 유용할 수 있습니다.