TIL Context switching

HiroPark·2023년 7월 20일
0

PintOS

목록 보기
1/9

Context Switching

  • 한 프로세스에서 다른 프로세스로 CPU를 넘겨주기 위해서
    • CPU를 내어주는 프로세스의 상태를 그 프로세스의 PCB에 저장
    • CPU를 새롭게 얻는 프로세스의 상태를 PCB에서 읽어옴

주의! 프로세스 A가 interrupt나 system call로 인해 Kernel로 넘어갔다가 user mode로 다시 복귀한 것은 context switching이 아님

→ 그러나 이 과정에서도 CPU 수행 정보 등 context의 일부는 PCB에 save

schedule()

  • 현재 running 중인 프로세스를 얻고
  • 다음으로 run할 프로세르를 얻고
  • current에서 next로 context를 switch 함

이 context_switch는 exit, block, yield가 부름, 아니면 preempted될때

  • schedule을 부르기전에 interrupt를 거주고
  • 현재 running thread의 state를 running이 아닌 다른걸로 바꿈
/* Schedules a new process. At entry, interrupts must be off.
 * 스케쥴은 그 자체로 새로운 프로세스 이므로 진입시 인터럽트가 비활성화 되어야 한다.
 * This function modify current thread's status to status and then
 * do_schedule 함수는 현재 스레드의 상태를 다른 상태로 바꾸어 주고
 * finds another thread to run and switches to it.
 * 해당 스레드와 바꾸어 실행할 다른 스레드를 찾아준다.
 * It's not safe to call printf() in the schedule().
 * 스케쥴 함수 안에서는 printf() 함수 호출은 권장되지 않음.
 * */
static void do_schedule(int status) {
    ASSERT(intr_get_level() == INTR_OFF);
    ASSERT(thread_current()->status == THREAD_RUNNING);
    while (!list_empty(&destruction_req)) {
        struct thread *victim = list_entry(list_pop_front(&destruction_req), struct thread, elem);
        palloc_free_page(victim);
    }
    thread_current()->status = status;
    schedule();
}

static void schedule(void) {
    struct thread *curr = running_thread ();
    struct thread *next = next_thread_to_run();

    ASSERT(intr_get_level() == INTR_OFF);
    ASSERT(curr->status != THREAD_RUNNING);
    ASSERT(is_thread (next));
    /* Mark us as running. */
    next->status = THREAD_RUNNING;

    /* Start new time slice. */
    thread_ticks = 0;

#ifdef USERPROG
    /* Activate the new address space. */
    process_activate (next);
#endif

    if (curr != next) {
        /* If the thread we switched from is dying, destroy its struct
           thread. This must happen late so that thread_exit() doesn't
           pull out the rug under itself.
           We just queuing the page free reqeust here because the page is
           currently used bye the stack.
           The real destruction logic will be called at the beginning of the
           schedule(). */
        if (curr && curr->status == THREAD_DYING && curr != initial_thread) {
            ASSERT(curr != next);
            list_push_back(&destruction_req, &curr->elem);
        }

        /* Before switching the thread, we first save the information
         * of current running. */
        thread_launch(next);
    }
}

스택의 %esp 는 stack top을 가리킴

  • %eip : 현재 CPU가 실행하고 있는 명령어의 주소(= Program Counter)

  • %esp : 현재 사용중인 스택의 최상단

  • System call

    커널에 입장하기 위해서

    1. user stack에서 kernel stack으로 switch → %eip를 옮김
    2. privilege level 상승
  • switch thread

    1. cur의 커널 스택에 레지스터들을 저장
    2. 현재 스택 탑의 위치를 cur 쓰레드 구조체의 stack 멤버에 저장
    3. 새로운 쓰레드의 stack top을(커널스택에 있음) → esp(CPU의 스택포인터)에 restore
    4. kernel stack에서 레지스터들을 restore

switch_threads 부르기 전

밑의 스택은 커널 스택

현재 context를 스택에 저장

switch_threads:
	pushl %ebx
	pushl %ebp
	pushl %esi
	pushl %edi

switch_threads

  1. thread 구조체에서 stack 포인터의 offset을 얻음(스택의 시작점인듯)

  2. 이를 edx에 저장

    mov thread_stack_ofs, %edx

  1. 현재 threads구조체의 위치를 eax에 저장

    movl SWITCH_CUR(%esp), %eax

  1. %esp에 CUR 쓰레드 구조체의 위치(= %eax + %edx * 1) 저장

    movl %esp, (%eax,%edx,1)

  1. Next 쓰레드 구조체의 주소를 %ecx에 load

    movl SWITCH_NEXT(%esp), %ecx

  1. Next의 커널 스택의 주소를 %esp 에 load

    movl (%ecx,%edx,1), %esp

  1. 새로운 context를 restore

popl %edi
popl %esi
popl %ebp
popl %ebx
ret
  1. next 쓰레드의 상태를 running으로 바꾸고, 죽는 쓰레드가 있으면 메모리 free
profile
https://de-vlog.tistory.com/ 이사중입니다

0개의 댓글