PintOS PJT1 - Threads

김수환·2024년 11월 5일

PintOS

목록 보기
2/15

멀티스레드 환경 구축 시, 고려해야할 점들과 실제 구현 예제를 알아보자.

멀티스레드 사용 시 여러개의 스레드들은 실행 순서에 따라 실행되거나 대기하거나 sleep(block)될 수 있다.

레드는 크게 위와 같은 4가지 상태로 존재할 수 있다.

  • Ready Queue에서 실행을 대기 중인 스레드 list
  • CPU를 점유하며 실행 중인 스레드
  • 일정 시간 동안 sleep 되어 block 상태인 스레드
  • 사용이 끝난 스레드

현재 코드 상황과 개선해야할 점.

개선된 Alarm Clock 코드 개요

기존 busy-waiting 방식의 문제점:

기존 timer_sleep은 특정 시간이 될 때까지 while 문을 반복하며 thread_yield()를 호출하여 CPU를 낭비했습니다. 이를 해결하기 위해, 스레드를 block 상태로 전환하여, 스케줄링에서 제외시키는 방식으로 개선합니다.

개선 아이디어:

Block 상태로 변경: 스레드를 block 상태로 전환하여 CPU 사용을 줄입니다.
Sleep 리스트 추가: 깨어날 시간이 있는 스레드를 sleep_list에 저장하여, 각 스레드의 깨어날 시간을 관리합니다.
Timer Interrupt 활용: timer_interrupt에서 1 tick마다 깨어날 시간이 된 스레드를 ready state로 전환합니다.

기존 "Busy waiting" 방식을 -> "Blocking (sleeping)" 방식으로 변경하는 것이 핵심

Blocking:

자원이 준비될 때까지 프로세스나 스레드가 block 상태로 들어가며, 이 상태에서는 CPU를 전혀 사용하지 않습니다. 자원이 준비되면 OS가 자동으로 wake-up 시켜 실행 가능 상태로 전환합니다.

Sleeping:

주로 스레드나 프로세스를 지정된 시간 동안 대기시키며, 이 시간 동안 CPU를 사용하지 않는 상태로 둡니다. 지정된 시간이 지나면 스레드는 자동으로 깨어납니다.

개선된 코드 구현

Project1의 알람 기능 개선을 위해서
기본 소스 코드 상에서
thread.h
thread.c
timer.c
를 수정할 필요가 있다.

1. Thread 구조체에 wakeup 시간 필드 추가

스레드가 깨어날 시간을 저장하는 wakeup 필드를 추가합니다.

/* thread/thread.h */
struct thread {
    ...
    int64_t wakeup; // 깨어나야 할 tick 시간
    ...
};

2. Sleep 리스트 추가 및 초기화

새로운 sleep_list를 추가하여, block 상태인 스레드를 관리합니다.

/* thread/thread.c */
static struct list sleep_list;

void thread_init(void) {
    ...
    list_init(&sleep_list);  // sleep_list 초기화
    ...
}

3. thread_sleep 함수 구현

thread_sleep 함수는 현재 스레드를 sleep_list에 추가하고, block 상태로 변경합니다. 이 함수는 timer_sleep에서 호출됩니다.

/* thread/thread.c */
void thread_sleep(int64_t ticks) {
    struct thread *cur = thread_current();
    enum intr_level old_level;

    old_level = intr_disable();    // 인터럽트 비활성화
    ASSERT(cur != idle_thread);    // idle 스레드는 sleep 되지 않도록 예외 처리

    cur->wakeup = ticks;           // 깨어날 시간 설정
    list_push_back(&sleep_list, &cur->elem); // sleep_list에 추가
    thread_block();                // 스레드를 block 상태로 전환

    intr_set_level(old_level);     // 인터럽트 활성화
}

4. timer_sleep 함수 개선

timer_sleep은 thread_sleep을 호출하여 block 상태로 스레드를 전환하고, 깨어날 시간을 설정합니다.

/* devices/timer.c */
void timer_sleep(int64_t ticks) {
    int64_t start = timer_ticks();
    thread_sleep(start + ticks); // 깨어날 시간 지정
}

5. thread_awake 함수 구현

thread_awake 함수는 sleep_list의 모든 스레드를 확인하여 깨어날 시간이 된 스레드를 ready 상태로 전환합니다.

/* thread/thread.c */
void thread_awake(int64_t ticks) {
    struct list_elem *e = list_begin(&sleep_list);

    while (e != list_end(&sleep_list)) {
        struct thread *t = list_entry(e, struct thread, elem);
        if (t->wakeup <= ticks) {  // 깨어날 시간이 되면
            e = list_remove(e);    // sleep_list에서 제거
            thread_unblock(t);     // 스레드를 unblock 상태로 전환
        } else {
            e = list_next(e);
        }
    }
}

6. timer_interrupt 함수 수정

매 tick마다 thread_awake 함수를 호출하여 깨어날 시간이 된 스레드를 찾아 깨웁니다.

/* devices/timer.c */
static void timer_interrupt(struct intr_frame *args UNUSED) {
    ticks++;
    thread_tick();
    thread_awake(ticks); // 매 tick마다 awake 작업 수행
}

PintOS - thread 기능의 안정화, 효율화를 위해 추가적으로 체크해 볼 부분.

1. interrupt.c (인터럽트 관리)

수정 이유:
스레드 상태 관리에서 interrupt를 비활성화하고 활성화하는 작업이 빈번히 필요합니다. 특히, sleep 상태의 스레드를 깨어나게 하는 timer_interrupt와 관련이 깊습니다.
수정 방법:
인터럽트 비활성화 및 활성화가 자주 호출되는 코드에서 성능 최적화가 가능한지 확인하고, 인터럽트 핸들링 중 타이머 관련 기능을 더 정밀하게 관리할 수 있도록 수정할 수 있습니다.

2. synch.h 및 synch.c (동기화 관련)

수정 이유:
스레드 간 동기화 문제를 해결하기 위한 파일로, 세마포어, 락, 조건 변수 등의 구조체와 관련 함수들이 정의되어 있습니다. 특히, 스레드가 잠드는 동안 공유 자원을 안전하게 관리하려면 동기화가 필요합니다.
수정 방법:
스레드가 sleep 상태에서 ready 상태로 전환될 때 사용될 락이나 조건 변수를 최적화하여 데드락 방지 및 성능 개선을 도모할 수 있습니다. 또한, 스레드가 깨울 때 발생할 수 있는 경쟁 상태를 해결하기 위한 동기화 로직을 추가할 수 있습니다.

3. list.h 및 list.c (리스트 관련)

수정 이유:
Pintos의 리스트 자료구조는 스레드 관리에 중요한 역할을 합니다. 예를 들어, sleep_list, ready_list 등이 모두 리스트로 관리되므로 효율적인 리스트 조작은 성능에 큰 영향을 미칩니다.
수정 방법:
sleep_list에서 깨워야 하는 스레드를 효율적으로 찾기 위해 리스트를 정렬된 상태로 유지하거나, 이진 탐색과 같은 알고리즘을 적용하여 성능을 개선할 수 있습니다.

4. scheduler.c (스케줄러)

수정 이유:
스레드를 어떤 순서로 실행할지를 결정하는 스케줄링 정책을 다룹니다. 스레드가 sleep 상태에서 깨어났을 때의 우선순위 관리가 중요합니다.
수정 방법:
스레드를 깨울 때 우선순위를 기반으로 ready_list에 삽입하거나, 우선순위 역전을 방지하기 위해 Priority Inheritance와 같은 기능을 추가하여 우선순위 기반의 효율적인 스케줄링을 구현할 수 있습니다.

5. init.c (시스템 초기화 관련)

수정 이유:
Pintos 시스템 초기화와 관련된 설정 파일로, 스레드나 타이머의 초기 설정이 이루어집니다. 특히 main 함수에서 스레드 관련 모듈 초기화가 수행됩니다.
수정 방법:
시스템이 부팅될 때, sleep_list와 같은 추가 리스트를 초기화하거나, 타이머 주기를 조정하여 시스템 전체 성능에 맞게 최적화할 수 있습니다.

6. palloc.c (페이지 할당기)

수정 이유:
Pintos에서 페이지 단위로 메모리를 할당 및 해제하는 코드가 포함되어 있습니다. 스레드가 새로운 메모리를 필요로 할 때, 이 할당기를 통해 메모리를 요청하게 됩니다.
수정 방법:
스레드 관리 시 필요할 수 있는 메모리 할당을 최적화하거나, 메모리 누수가 발생하지 않도록 메모리 해제 루틴을 개선할 수 있습니다.

7. timer.h (타이머 관련 헤더)

수정 이유:
타이머 주기, 인터럽트 관련 변수들이 선언되어 있으며, timer_sleep의 인터페이스를 제공합니다.
수정 방법:
timer.h에 새로운 타이머 주기 상수나 매크로를 추가하여 시스템 설정을 보다 세밀하게 조정할 수 있습니다. 필요 시 thread.h에 정의된 타이머 관련 변수와 연동하여 효율성을 높일 수 있습니다.

profile
juniorDev

0개의 댓글