ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [PintOS] WIL (1) alarm-clock 위주 (추후 보완 예정)
    SW 정글/운영체제 2024. 10. 1. 01:22

     

    현대의 우리 컴퓨터는 멀티스레딩 환경을 가지고 있다.

    멀티스레드 전에는 스레드가 하나일 때가 있지 않았을까?

    우리의 귀여운 PintOS는 하나의 스레드만을 가지고 있다.

    단일 스레드를 가진 프로세스는 어떻게 효율적으로 사용해야 하는지 고민하는 게 이번 과제라고 생각했다.

     

     

    기존 코드의 문제 - Polling 방식?

    눌러? 아니네, 눌러? 아니네, 눌? 아, ㄱ? ㄴㄴ

     

    //*============ SLEEP ===================================
    // 지정된 타이머 틱 수만큼 실행을 일시 중단하는 함수입니다.
    void timer_sleep (int64_t ticks) {
    	//timer_sleep 함수 호출 시 가장 먼저 timer_ticks()를 호출하여 현재 타이머 틱의 수(start)를 저장한다.
        int64_t start = timer_ticks ();  // 시작 시점의 타이머 틱 수를 저장합니다.
    
        ASSERT (intr_get_level () == INTR_ON);  // 인터럽트가 활성화된 상태를 확인합니다.
        while (timer_elapsed (start) < ticks)  // 경과 시간이 지정된 시간보다 작은 동안 반복합니다.
            thread_yield ();  // 다른 스레드에게 CPU를 양보한다.
    		//!위의 스레드는 계속해서 실행 큐에 남아있고, 조건을 평가하기 위해 CPU 시간을 소모한다.
    }

     

    타이머가 설정된 시간이 있는데, 그전까지 계속 서로서로 본인 차례가 왔는지 검사(poll)하는 방식이다

    이는 CPU를 점유한 채 지속적으로 체크해서 다른 작업을 할 수 없게 만든다.

    CPU 자원을 필요이상으로 사용하므로 자원 관리에도 비효율적인 방식이다.

     

     

    👩‍🌾timer, tick, interrupt ?

     

    타이머 (Timer) & 틱 (Tick)

    타이머는 시간의 흐름을 측정하고, 정해진 시간이 지나면 특정 작업을 실행하도록 하는 장치이다.

    컴퓨터에서는 ""이라고 하는 일정한 시간 간격으로, CPU에 신호(인터럽트)를 보내어,

    시간 기반의 작업을 관리할 수 있게 한다.

    *틱은 운영 체제가 시간을 어떻게 관리하는지를 결정하는 기본 단위로, 보통 밀리초 단위로 측정한다.

    초침이 tick, tick 움직인다.

     

    인터럽트 (Interrupt)

    인터럽트는 프로그램의 정상적인 흐름을 잠시 중단하고, 긴급하게 처리해야 할 작업(ex:입력 장치로부터의 데이터 처리, 타이머 신호 등)을 우선적으로 수행하도록 한다.

    인터럽트 발생 시, CPU는 현재 실행 중인 작업을 일시 중지하고 인터럽트 처리 루틴을 먼저 실행한 후,

    중지했던 작업을 다시 재개한다.

     

    아래는 팀원의 스케치를 내 방식대로 재해석하여 도식화한 것이다. (잘못된 부분은 댓글로 부탁드립니다)

    Q. 누구를 깨우지?에 대한 물음은 priority schedule로 이어진다.

     

     

    이 때 run이 끝나면 어떻게 되는 걸까?

     

    그 다음 처리 과정은 운영 체제의 스케줄러와 스레드의 상태에 따라 다양한 시나리오가 나올 수 있다.

     

    1. 컨텍스트 스위칭 (Context Switching)

    스레드의 실행 시간 할당(타임 슬라이스)이 끝나거나 더 높은 우선순위의 스레드가 실행 준비가 되었을 때, 운영 체제는 현재 스레드를 중단하고 다른 스레드로 전환한다. 이 과정에서 현재 스레드의 상태는 ready 상태로 바뀌며, 다시 실행될 준비를 한다.

     

    2. 블로킹 (Blocking)

    스레드가 입출력 작업이나 데이터를 기다리는 등의 이유로 블록 상태가 필요할 때, 스레드는 자발적으로 CPU를 양보하고 blocked 상태로 전환된다. 이 상태에서 스레드는 필요한 조건이 충족될 때까지 대기하며, 조건이 만족되면 ready 상태로 전환되어 다시 실행될 수 있다.

     

    3. 종료 (Termination)

    스레드가 자신의 실행을 완료했을 때(예를 들어, 실행할 코드가 더 이상 없거나 exit 함수를 호출했을 때), 스레드는 종료된다. 이 경우, 스레드의 모든 자원이 해제되고, 스레드는 시스템에서 제거된다. 이는 terminated 상태라고도 부르며, 스레드의 생명주기가 완전히 끝나는 시점이다.

     

    4.수면 (Sleeping)

    특정 시간 동안 실행을 중단해야 할 필요가 있을 때(예: sleep 함수 사용), 스레드는 지정된 시간 동안 sleeping 상태가 된다. 이 상태는 blocked 상태의 한 형태로, 시간이 경과한 후에 스레드는 자동으로 ready 상태로 복귀하게 된다.


    코드 구현

     

    ✅timer.c 에서 polling -> interrupt 방식으로 변경

     

     

     

    thread.c

    void thread_sleep(int64_t ticks) {
        struct thread *t = thread_current();
    
        enum intr_level old_level = intr_disable(); // 인터럽트를 비활성화하여 스레드 리스트 조작 중 발생할 수 있는 인터럽트를 방지
    
        if (t != idle_thread) { // 현재 스레드가 idle 스레드가 아닌 경우만 처리
            t->wakeup_tick = ticks;// 스레드가 깨어나야 할 시간 설정
            list_push_back(&sleep_list, &t->elem);// 스레드를 sleep_list에 추가
            do_schedule(THREAD_BLOCKED);// 스레드의 상태를 BLOCKED로 변경하고 스케줄러를 호출
        }
        intr_set_level(old_level);// 원래의 인터럽트 레벨로 복원
    }

     

    ▶️ list_push_back(&sleep_list, &t->elem);

    Sleep List 추가 로직: PintOS의 thread_sleep 함수는 실행 중인 스레드를 일정 시간 동안 잠자게 합니다. 

    • 현재 스레드 획득: thread_current() 함수를 호출하여 현재 실행 중인 스레드의 참조를 가져온다.
    • 인터럽트 비활성화: intr_disable()를 호출하여 현재 스레드가 sleep_list에 안전하게 추가될 수 있도록 인터럽트를 일시 중지한다. 이는 스레드의 상태 변경 과정 중에 인터럽트가 발생하여 데이터가 일관성 없게 변경되는 것을 방지한다.
    • Sleep List에 추가: list_push_back() 함수를 사용하여 현재 스레드를 sleep_list에 추가한다. 이 리스트는 잠든 스레드들을 관리하며, 각 스레드는 깨어나야 할 시각(wakeup_tick)이 설정되어 있다.
    • 스레드 블록: do_schedule(THREAD_BLOCKED)를 호출하여 스레드의 상태를 BLOCKED로 변경하고, 스케줄러를 통해 다른 스레드로 전환할 수 있도록 한다.

     

    thread.c

    void find_thread_to_wake_up(void) {
        struct list_elem *e;
        for (e = list_begin(&sleep_list); e != list_end(&sleep_list);) {
            struct thread *t = list_entry(e, struct thread, elem);
            if (timer_elapsed(t->wakeup_tick) >= 0) {// 설정된 시간이 경과했는지 확인
                e = list_remove(e);// 시간이 경과한 스레드를 리스트에서 제거
                thread_unblock(t);// 스레드의 상태를 준비 상태로 변경하여 실행 준비
            } else {
                e = list_next(e);// 다음 스레드로 이동
            }
        }
    }

    ▶️ if (timer_elapsed(t->wakeup_tick) >= 0)

      스레드가 지정된 시간 동안만 잠들게 하고, 그 시간이 지나면 자동으로 깨운다.

     

    스레드 깨우는 로직: find_thread_to_wake_up 함수는 sleep_list를 순회하며 각 스레드의 깨어날 시간을 검사

    • 리스트 순회: sleep_list의 시작부터 끝까지 순회하며 각 스레드의 wakeup_tick을 현재 시간과 비교
    • 시간 검사: timer_elapsed(t->wakeup_tick)를 사용하여 설정된 깨어날 시간이 현재 시간을 초과했는지 확인한 후 이 값이 0 이상이면 스레드가 깨어날 준비가 되었다는 의미이다.
    • 스레드 깨우기: 조건을 만족하는 스레드는 list_remove()를 통해 sleep_list에서 제거되고, thread_unblock()을 호출하여 스레드를 READY 상태로 변경한다. 이로써 스레드는 다시 실행될 준비가 된다.

     

     

     

    인터럽트 관리의 중요성

    중간에 인터럽트가 발생할 경우, 스레드 상태의 부분적인 변경이 발생하여 시스템의 불안정성을 초래할 수 있기 때문이다. 데이터의 무결성과 일관성을 유지하는 것은 멀티태스킹 환경에서 스레드 간의 정확한 상호 작용과 자원 공유를 위해 필수적이다.

     

     시간 기반 스레드 관리

    if (timer_elapsed(t->wakeup_tick) >= 0) 조건문은 스레드가 설정된 시간에 맞춰 정확하게 깨어날 수 있도록 한다. 이 로직은 스레드가 지정된 시간 동안만 잠자게 하고, 그 시간이 지나면 자동으로 깨우는 효율적인 방법을 제공한다.

    이는 타이머와 스레드 스케줄링이 긴밀히 연동되어 작동함을 보여주는데, 타이머 기반의 스레드 관리는 시스템 자원을 최적화하고, 스레드가 필요할 때 적시에 실행될 수 있도록 하며, 시스템의 전반적인 반응성과 성능을 향상시킨다.


    reference

     

    mutex와 semaphore에 대한 글

    https://worthpreading.tistory.com/90

     

    뮤텍스(Mutex)와 세마포어(Semaphore)의 차이

    이 글은 Medium에 개시된 글입니다. Medium에서 보시면 좀 더 유쾌한 환경에서 글을 보실 수 있습니다. 뮤텍스(Mutex)와 세마포어(Semaphore)의 차이 Toilet problem 동시성 프로그래밍의 가장 큰 숙제는 ‘공

    worthpreading.tistory.com

     

    OS 요약 지식

    https://velog.io/@jeewoo1025/OS-%EB%A9%B4%EC%A0%91-%EC%A4%80%EB%B9%84

     

    [OS] 면접 준비

    CS 면접을 준비하면서 정리했던 내용들입니다. 개인적으로 프로세스, 스레드, CPU 스케줄링(선점/비선점), 메모리 관리(virtual memory, paging) 부분은 정말 중요한 부분인 것 같습니다. 면접에서 꼬리

    velog.io

     

    프로세스와 스레드?

    https://inpa.tistory.com/entry/%F0%9F%91%A9%E2%80%8D%F0%9F%92%BB-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%E2%9A%94%EF%B8%8F-%EC%93%B0%EB%A0%88%EB%93%9C-%EC%B0%A8%EC%9D%B4

     

    👩‍💻 ‍완전히 정복하는 프로세스 vs 스레드 개념

    한눈에 이해하는 프로세스 & 스레드 개념 전공 지식 없이 컴퓨터의 프로그램을 이용하는데는 문제 없어 왔지만 소프트웨어를 개발하는 사람으로서 컴퓨터 실행 내부 요소를 따져보게 될때, 아

    inpa.tistory.com

     

Designed by Tistory.