switch_to() in schedule()

Reading ULK about switch_to(prev, next, last), it's quite hard to understand why the need for the 3rd argument. It turns out it's not hard when read it with schedule().

The schedule() perform a process switch, that is suspend current process and start next runnable process. A simplified version is shown below. It does 1) use prev and next for current and next runnable process; 2) call switch_to() to suspend current process and start next process; 3) process other stuff for prev process. Since schedule() needs to deal with prev process after switch_to(), so it needs switch_to() tell which process the current process switch from, that's the reason for the 3rd argument last in switch_to(prev, next, last). In schedule(), the 3rd argument is prev (line 11), so after switch_to(), prev stands for the process that the current process switch from.

void schedule(void)
{
    struct task_struct *prev, *next;
    
    /* prev is executing */
    
    // get prev & next;
    prev = current; next = next_runnable_task
    
    // switch to next, next starts to run after this call
    switch_to(prev, next, prev)  // this is actually called by context_switch()
    
    /*------------------------------------------*/
    /* next is executing */
    
    // process other stuff about prev
    finish_task_switch(prev);
    
    // next continue running
}

one may wonder why need switch_to() set prev again since schedule() already has prev. Because the prev is not correct when the process is executing again after a process switch. Suppose switch_to() has no 3rd argument, that is not set prev again. Suppose now do a process switch from process A to process B. Now A stops at switch_to() because by defination switch_to() stops A and starts to run B. For A, it has (prev=A, next=B) like picture below. Before switch_to() starts B, B is suspend, and suppose B is suspended due to switch to other process by switch_to(). B starts to run and it has (prev=B, next=other process). Why? after process switch, B resume its stack and this stack is saved when switch from B to other process, and B has (prev=B, next=other process) when switch from B to other process. Now B starts at instructions after switch_to(), and need process some stuff for the process it switch from, and it expects prev stands for this process. Since we do process switch from A to B, we expect prev is A instead of its current value B. So prev need reset. switch_to() knows the how to reset prev and provide the 3rd argument for it.

How switch_to() set prev?

switch_to() set its 3rd argument as the switching from process, schedule() pass prev as 3rd argument to save the switching from process. How switch_to() does? See following code snippt with comments.

switch_to(prev, next, last)
    
    # save prev in eax register and next in edx
    movl prev, %eax
    movl next, %edx
    
    # save eflags & ebp
    pushfl
    pushl %ebp
    
    # Saves the content of esp in prev->thread.esp
    movl %esp,484(%eax)
    
    # Loads next->thread.esp in esp. From now on, the kernel operates on the Kernel
    # Mode stack of next, so this instruction performs the actual process switch from
    # prev to next. Because the address of a process descriptor is closely related to that
    # of the Kernel Mode stack (as explained in the section “Identifying a Process” earlier
    # in this chapter), changing the kernel stack means changing the current process
    movl 484(%edx), %esp
    
    # Saves the address labeled 1 in prev->thread.eip
    # When the process being replaced resumes its execution,
    # the process executes the instruction labeled as 1
    movl $1f, 480(%eax)
	
    # On the Kernel Mode stack of next, pushes the next->thread.eip value,
    # which, in most cases, is the address labeled as 1. see last instruction.
    pushl 480(%edx)
    
    # importance: this function make sure eax not changed, eax has prev.
    # do hardware context switch from prev to next
    # when return, it get return address from its stack, and
    # the address is label 1 for most case, see last instruction.
    jmp __switch_to
	
1:
    # pop ebp & eflags
    popl %ebp
    popfl
    
    # save prev stored in eax to last (the 3rd argument)
    movl %eax, last

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章