주의! 프로세스 A가 interrupt나 system call로 인해 Kernel로 넘어갔다가 user mode로 다시 복귀한 것은 context switching이 아님
→ 그러나 이 과정에서도 CPU 수행 정보 등 context의 일부는 PCB에 save
schedule()
이 context_switch는 exit, block, yield가 부름, 아니면 preempted될때
/* 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
커널에 입장하기 위해서
switch thread
밑의 스택은 커널 스택 임
switch_threads:
pushl %ebx
pushl %ebp
pushl %esi
pushl %edi
thread 구조체에서 stack 포인터의 offset을 얻음(스택의 시작점인듯)
이를 edx에 저장
mov thread_stack_ofs, %edx
현재 threads구조체의 위치를 eax에 저장
movl SWITCH_CUR(%esp), %eax
%esp
에 CUR 쓰레드 구조체의 위치(= %eax + %edx * 1) 저장
movl %esp, (%eax,%edx,1)
Next 쓰레드 구조체의 주소를 %ecx
에 load
movl SWITCH_NEXT(%esp), %ecx
Next의 커널 스택의 주소를 %esp
에 load
movl (%ecx,%edx,1), %esp
popl %edi
popl %esi
popl %ebp
popl %ebx
ret