Linux 调度基本流程

本文主要描述主动调度过程,如用户态代码调用sched_yield。

 

1:通过系统调用,进入内核态,在内核系统调用处理入口(entry_SYSCALL_64),先保存部分寄存器信息到当前进程对应的内核栈上(task_struct->thread.sp)

pushq        $__USER_DS                        /*pt_regs->ss */

pushq        PER_CPU_VAR(rsp_scratch)        /*pt_regs->sp */

pushq        %r11                                /*pt_regs->flags */

pushq        $__USER_CS                        /*pt_regs->cs */

pushq        %rcx                                /*pt_regs->ip */

pushq        %rax                                /*pt_regs->orig_ax */

pushq        %rdi                                /*pt_regs->di */

pushq        %rsi                                /*pt_regs->si */

pushq        %rdx                                /*pt_regs->dx */

pushq        %rcx                                /*pt_regs->cx */

pushq        $-ENOSYS                        /*pt_regs->ax */

pushq        %r8                                /*pt_regs->r8 */

pushq        %r9                                /*pt_regs->r9 */

pushq        %r10                                /*pt_regs->r10 */

pushq        %r11                                /*pt_regs->r11 */

sub        $(6*8),%rsp                        /*pt_regs->bp, bx, r12-15 not saved */

2:执行系统调用处理函数,

       call    *sys_call_table(, %rax, 8)

3:在系统调用处理函数里,调用schedule函数

4:schedule通过pick_next_task选取下一个要执行的task

5:进入switch_mm_irqs_off,切换页表信息(load_cr3(next->pgd))

6:switch_to切换上下文

       如果是x86平台,最终调用__switch_to_asm

 

ENTRY(__switch_to_asm)

/*

 * Save callee-saved registers

 * This must match the order ininactive_task_frame

 */

pushq        %rbp

pushq        %rbx

pushq        %r12

pushq        %r13

pushq        %r14

pushq        %r15

 

/*switch stack */

movq        %rsp,TASK_threadsp(%rdi)         // 将当前的内核栈信息保存到task1结构体里

movq        TASK_threadsp(%rsi),%rsp        // next_task结构体的thread.sp获取内核栈信息

 

#ifdefCONFIG_CC_STACKPROTECTOR

movq        TASK_stack_canary(%rsi),%rbx

movq        %rbx,PER_CPU_VAR(irq_stack_union)+stack_canary_offset

#endif

 

/*restore callee-saved registers */

popq        %r15

popq        %r14

popq        %r13

popq        %r12

popq        %rbx

popq        %rbp

 

jmp        __switch_to

END(__switch_to_asm)

7:到这一步,当前的rsp已经表示为next_task对应的内核栈信息了,接下来,从内核态返回到用户态,并拿着rsp恢复出所有的寄存器信息,这样执行iret返回用户态时就回到了next_task的用户态现场。

你可能感兴趣的:(Linux)