[Pintos] Threads: Alarm Clock 단계별 구현 및 테스트하기

2025. 5. 9. 19:19·크래프톤 정글/Code 정글(C언어)

Thread Alarm Clock 단계별 구현 및 테스트하기

이번 포스트에서는 PintOS 프로젝트 1의 첫 번째 과제인 Alarm Clock을 구현하는 과정을 단계별로 정리해보려고 합니다. Alarm Clock 기능은 busy waiting 없이 스레드를 sleep/wakeup 방식으로 재우고 깨우는 기능을 구현하는 것이 핵심이며, 이를 통해 스레드 동기화, 인터럽트 기반 이벤트 처리 등을 체험할 수 있습니다.

 

주요 목표

  • timer_sleep(int64_t ticks) 호출 시, 해당 스레드를 지정된 tick 동안 잠들게 하고 정확한 시점에 깨우기
  • 기존 busy-waiting 방식을 제거하고 sleep list 기반의 효율적인 대기 방식 구현
  • 커널 내에서의 동기화 원리, 스레드 상태 전이, 인터럽트 처리 흐름을 직접 다뤄보기

 

학습 목적

  • 스레드 상태 전이 구조 학습
    • THREAD_RUNNING → THREAD_BLOCKED → THREAD_READY → THREAD_RUNNING의 순환을 직접 구현하면서, 커널 스케줄링 흐름을 체득하기
  • 동기화 원리의 기초 체득
    • 인터럽트를 통한 비동기 이벤트 처리 구조와 함께, 임계 구역 보호를 위해 intr_disable()과 intr_set_level()를 사용하는 이유를 명확히 이해하기
  • sleep/wakeup 기반의 리소스 절약형 대기 구현
    • busy waiting 대신 sleep_list를 활용해, CPU 점유 없이 필요한 시점에만 스레드를 깨우는 구조를 설계하기

 

(참고) 수정해야 할 파일

  • threads/thread.*
  • devices/timer.*

 

Alarm Clock 단계별로 구현하기

1단계: 문제 정의하기  – Busy Waiting 제거

  • 기존 구현은 timer_sleep() 호출 시, while 루프로 tick 값을 계속 확인하는 방식으로, CPU 자원을 낭비하고 있음
  • 목표는 tick만큼 잠든 후 정확한 시점에 깨우되, 그동안 CPU를 점유하지 않도록 하는 것
  • 따라서, 스레드를 BLOCKED 상태로 만들어 스케줄러에서 제외시키고, 깨어날 시간이 되면 다시 READY 상태로 이동

 

2단계: 흐름 및 구조 설계하기  – Sleep List 기반 구조

핵심 아이디어

  • sleep_list라는 전역 리스트를 선언하고, 각 스레드의 깨어날 tick 값을 기준으로 정렬하여 삽입
  • 타이머 인터럽트가 발생할 때마다 현재 tick과 비교해, 깨울 시점이 된 스레드를 unblock함

스레드 흐름

Running → BLOCKED → READY → Running

핵심 동작 정리

  • timer_sleep(ticks) 호출 시
    • 현재 tick + ticks → wakeup_tick 계산
    • thread_sleep(wakeup_tick) 호출
  • thread_sleep()
    • 현재 스레드의 wakeup_tick 저장
    • sleep_list에 정렬 삽입
    • thread_block() 호출 → BLOCKED 상태 진입
  • 매 tick마다 timer_interrupt()
    • ticks++ 후, thread_awake(ticks) 호출
    • sleep_list 맨 앞부터 순회하며 wakeup_tick <= 현재 tick인 스레드들을 깨움

 

3단계: 함수 단위 설계 및 구현하기

각 함수가 어떤 책임을 가지고 어떤 동작을 해야하며, 어떤 자료구조를 다루는지 정리하는 단계입니다.

(0) 구현 전 준비 사항

  • struct thread에 필드 추가
    • int64_t wakeup_tick
    • struct list_elem wait_elem
  • 전역 sleep_list 선언 및 thread_init()에서 초기화
  • cmp_tick() 정렬용 비교 함수 정의 (sleep_list 삽입 시 사용)
// thread의 wakeup_tick 값을 비교하는 비교 함수
// 이 함수는 리스트에 thread들을 'wakeup_tick' 순으로 정렬하기 위해 사용된다.
bool cmp_tick(const struct list_elem *a, const struct list_elem *b, void *aux UNUSED) {
    // 리스트 요소 a와 b를 각각 thread 구조체로 변환
    struct thread *t1 = list_entry(a, struct thread, wait_elem); // a가 포함된 thread 추출
    struct thread *t2 = list_entry(b, struct thread, wait_elem); // b가 포함된 thread 추출

    // 두 스레드의 wakeup_tick 값을 비교하여 t1이 먼저 깨워져야 한다면 true를 반환
    // 즉, t1이 t2보다 먼저 깨어나야 한다면 t1이 리스트 앞에 오게 된다.
    return t1->wakeup_tick < t2->wakeup_tick;
}

 

(1) thread_sleep() 함수 추가

스레드를 잠들게 하는 핵심적인 처리를 담당합니다.

  • 현재 스레드의 wakeup_tick을 설정하고
  • sleep_list에 정렬 삽입
  • 상태를 THREAD_BLOCKED로 변경
void thread_sleep(int64_t wakeup_tick) {
    // 현재 실행 중인 스레드의 포인터를 가져오기
    struct thread *cur = thread_current();

    // idle_thread는 시스템 유휴 상태에서 돌아가는 스레드이므로 잠재우면 안 됨
    if (cur == idle_thread) return;

    // 인터럽트를 비활성화하여 sleep_list에 접근할 때 다른 인터럽트가 끼어들지 못하게 함
    enum intr_level old_level = intr_disable();

    // 현재 스레드가 언제 깨어나야 할지를 설정 (timer_tick()과 비교할 대상)
    cur->wakeup_tick = wakeup_tick;

    // sleep_list에 현재 스레드를 삽입하되,
    // 깨어날 시점(wakeup_tick)을 기준으로 정렬해서 삽입
    list_insert_ordered(&sleep_list, &cur->wait_elem, cmp_tick, NULL);

    // 현재 스레드를 비활성화(blocked 상태)로 전환하여 CPU를 양보
    thread_block();

    // 인터럽트 상태를 이전 상태로 복구 (다시 인터럽트 허용)
    intr_set_level(old_level);
}

 

(2) timer_sleep() 함수 추가

유저 스레드가 호출하는 인터페이스로, 내부에서 thread_sleep()을 호출합니다.

void timer_sleep(int64_t ticks) {
    // 요청한 시간이 0 이하라면 굳이 재울 필요가 없으므로 함수 종료
    if (ticks <= 0) return;

    // 현재 시점에 ticks만큼 더한 시점을 계산하여,
    // 그 시점이 되면 스레드를 깨우도록 한다
    int64_t wakeup_tick = timer_ticks() + ticks;

    // 계산한 깨울 시점을 넘겨서 현재 스레드 재우기
    thread_sleep(wakeup_tick);
}

 

(3) thread_awake() 함수 추가

타이머 인터럽트가 발생할 때 호출되어, 깨어날 시간이 지난 스레드를 깨웁니다.

void thread_awake(int64_t now_tick) {
    // 인터럽트를 비활성화하여 sleep_list 접근 중 동시 수정 방지
    enum intr_level old_level = intr_disable();

    // sleep_list의 처음 요소부터 순회 시작
    struct list_elem *e = list_begin(&sleep_list);

    // sleep_list는 wakeup_tick 기준으로 오름차순 정렬되어 있으므로,
    // 깨울 스레드가 있는 앞쪽부터 순차적으로 검사하면 됨
    while (e != list_end(&sleep_list)) {
        // 리스트 요소 e를 thread 구조체 포인터로 변환
        struct thread *t = list_entry(e, struct thread, wait_elem);

        // 현재 시간이 해당 스레드의 깨울 시간 이상이면
        // → 이제 깨울 수 있으므로 리스트에서 제거하고 unblock
        if (t->wakeup_tick <= now_tick) {
            e = list_remove(e);     // 현재 요소를 제거한 다음 다음 요소로 이동
            thread_unblock(t);      // 깨워서 ready 상태로 전환
        } else {
            // 남은 스레드들은 아직 깰 시간이 안 됐으므로 검사 종료
            break;
        }
    }

    // 인터럽트 상태를 원래대로 복원
    intr_set_level(old_level);
}

 

(4) timer_interrupt() 함수 수정

기존에 tick을 증가시키는 기능에 더해, thread_awake() 호출을 추가합니다.

static void timer_interrupt(struct intr_frame *args UNUSED) {
    ticks++;
    thread_tick();
    thread_awake(ticks);	// 이제 매 tick마다 깨울 스레드가 있는지 확인
}

 

 

Alarm Clock 테스트하기

Alarm clock의 구현이 끝났다면 이제 테스트를 해볼 차례인데요. 먼저 threads 디렉터리에서 make clean 후 make로 수정된 C코드를 실행파일로 만들어줍니다. 이후 build 디렉터리로 이동한 다음 make check를 통해 테스트를 해볼 수 있는데요. 모든 테스트가 전부 실행되기 때문에, alarm으로 시작하는 테스트가 다 끝난 뒤 중간에 종료하시는 걸 추천드립니다.

 

테스트 이후, build 디렉터리 안에 tests/threads 디렉터리가 새로 생기고, 이 안에 테스트 결과 파일들이 새롭게 생기게 되는데요. .errors에는 테스트 실행 중 표준 에러(stderr)에 찍힌 메시지, .output에는 테스트 실행 중 표준 출력(stdout)에 찍힌 내용, .result에는 테스트 통과 여부(PASS/FAIL) 및 간략한 로그가 포함되어 있습니다.

make check 후 생기는 테스트 파일들

저작자표시 비영리 변경금지 (새창열림)

'크래프톤 정글 > Code 정글(C언어)' 카테고리의 다른 글

[Pintos] Threads: Priority Scheduling - 동기화 Primitive 단계별로 수정하기  (0) 2025.05.11
[Pintos] Pintos 학습 프로세스 ver1.0  (0) 2025.05.10
[Pintos] Pintos를 시작하면서 - 협업에 대한 고민, AI의 활용 범위  (0) 2025.05.09
[WebProxy] 캐시 기능이 추가된 동시성 프록시 서버 단계별 구현 및 테스트하기  (0) 2025.05.07
[VSCode] 내가 쓰는 VSCode 유용한 단축키 모음  (0) 2025.05.06
'크래프톤 정글/Code 정글(C언어)' 카테고리의 다른 글
  • [Pintos] Threads: Priority Scheduling - 동기화 Primitive 단계별로 수정하기
  • [Pintos] Pintos 학습 프로세스 ver1.0
  • [Pintos] Pintos를 시작하면서 - 협업에 대한 고민, AI의 활용 범위
  • [WebProxy] 캐시 기능이 추가된 동시성 프록시 서버 단계별 구현 및 테스트하기
그냥사람_
그냥사람_
IT 관련 포스팅을 합니다. 크래프톤 정글 8기 정경호
  • 그냥사람_
    그냥코딩
    그냥사람_
  • 전체
    오늘
    어제
    • 글 전체보기 N
      • 크래프톤 정글 N
        • 로드 투 정글(입학시험)
        • CS기초(키워드, 개념정리) N
        • 컴퓨터구조(CSAPP)
        • Code 정글(C언어) N
        • 마이 정글(WIL, 에세이)
      • 자료구조&알고리즘
        • 자료구조
        • 알고리즘
      • 일상
  • 블로그 메뉴

    • 홈
  • 링크

    • Github
  • hELLO· Designed By정상우.v4.10.3
그냥사람_
[Pintos] Threads: Alarm Clock 단계별 구현 및 테스트하기
상단으로

티스토리툴바