目录
内核态进程切换关键代码
上下文切换函数定义
void switch_to(struct context *from, struct context *to);
.text
.globl switch_to
switch_to: # switch_to(from, to)
# save from's registers
movl 4(%esp), %eax # eax points to from
popl 0(%eax) # save eip !popl
movl %esp, 4(%eax) # save esp::context of from
movl %ebx, 8(%eax) # save ebx::context of from
movl %ecx, 12(%eax) # save ecx::context of from
movl %edx, 16(%eax) # save edx::context of from
movl %esi, 20(%eax) # save esi::context of from
movl %edi, 24(%eax) # save edi::context of from
movl %ebp, 28(%eax) # save ebp::context of from
# restore to's registers
movl 4(%esp), %eax # not 8(%esp): popped return address already
# eax now points to to
movl 28(%eax), %ebp # restore ebp::context of to
movl 24(%eax), %edi # restore edi::context of to
movl 20(%eax), %esi # restore esi::context of to
movl 16(%eax), %edx # restore edx::context of to
movl 12(%eax), %ecx # restore ecx::context of to
movl 8(%eax), %ebx # restore ebx::context of to
movl 4(%eax), %esp # restore esp::context of to
pushl 0(%eax) # push eip
ret
上下文切换函数调用
struct context {
uint32_t eip;
uint32_t esp;
uint32_t ebx;
uint32_t ecx;
uint32_t edx;
uint32_t esi;
uint32_t edi;
uint32_t ebp;
};
void proc_run(struct proc_struct *proc)
{
L1: local_intr_save(intr_flag);
L2: switch_to(&(prev->context), &(next->context));
L3: local_intr_restore(intr_flag);
}
内核态进程上下文切换整体流程
proc_run执行流程
void proc_run(struct proc_struct *proc)
{
L1: local_intr_save(intr_flag);
L2: switch_to(&(prev->context), &(next->context));
L3: local_intr_restore(intr_flag);
}
jmp switch_to如何改变了指令控制流程
CPU执行指令只靠CS:IP两个寄存器,这条指令的执行最终改变了什么呢?其实关键点在于jmp指令改变了当前的IP值,用个图解释这个原因。等待下次执行指令的时候,CPU通过IP寄存器获得当前执行的指令movl 4(%esp), %eax,这就完成了指令跳转。
执行jmp switch_to指令时,栈情况
内核态进程上下文切换三个过程
在第2节中,分析了proc_run函数执行的整体流程,在这里将进一步分析switch_to汇编代码。
void switch_to(struct context *from, struct context *to);
.text
.globl switch_to
switch_to: # switch_to(from, to)
# save from's registers
movl 4(%esp), %eax # eax points to from
popl 0(%eax) # save eip !popl
movl %esp, 4(%eax)
movl %ebx, 8(%eax)
movl %ecx, 12(%eax)
movl %edx, 16(%eax)
movl %esi, 20(%eax)
movl %edi, 24(%eax)
movl %ebp, 28(%eax)
# restore to's registers
movl 4(%esp), %eax # not 8(%esp): popped return address already
# eax now points to to
movl 28(%eax), %ebp
movl 24(%eax), %edi
movl 20(%eax), %esi
movl 16(%eax), %edx
movl 12(%eax), %ecx
movl 8(%eax), %ebx
movl 4(%eax), %esp
pushl 0(%eax) # push eip
ret
第一阶段,跳转到switch_to函数入口
第二阶段,保存from上下文
核心,切换到from的context中,并将当前的EIP保存到from.context.eip中,这样完成进程控制流的保存。
第三阶段,恢复to上下文
核心,切换至to的context中,将to.context.eip放入栈的函数返回值中,这样切换到另外一个进程控制流。