linux arm 进程切换之 __switch_to (注释)

内核版本 3.10.90

arch\arm\kernel\entry-armv.S

/*
 * Register switch for ARMv3 and ARMv4 processors
 * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
 * previous and next are guaranteed not to be the same.
 */
ENTRY(__switch_to)       /* 其调用流程为:__schedule --> context_switch --> switch_to --> __switch_to
                          * 由于 arch\arm\include\asm\switch_to.h 里:
                          * #define switch_to(prev,next,last)                    \
                          * do {                                    \
                          *     last = __switch_to(prev,task_thread_info(prev), task_thread_info(next));    \
                          * } while (0)
                          * 从而执行到该函数时,lr 为 context_switch 中 switch_to 的下一条指令,即 barrier();
                          */
 UNWIND(.fnstart    )
 UNWIND(.cantunwind    )
    add ip, r1, #TI_CPU_SAVE        /* r1 指向的是当前运行进程的 thread_info 中的 struct cpu_context_save    cpu_context;
                                     * 在 arch\arm\kernel\asm-offsets.c
                                     *  DEFINE(TI_CPU_SAVE,     offsetof(struct thread_info, cpu_context));
                                     * 在 arch\arm\include\asm\thread_info.h
                                     *  struct cpu_context_save {
                                     *      __u32    r4;
                                     *      __u32    r5;
                                     *      __u32    r6;
                                     *      __u32    r7;
                                     *      __u32    r8;
                                     *      __u32    r9;
                                     *      __u32    sl;
                                     *      __u32    fp;
                                     *      __u32    sp;
                                     *      __u32    pc;
                                     *      __u32    extra[2];        /* Xscale 'acc' register, etc */
                                     *  };
                                     */

    ldr r3, [r2, #TI_TP_VALUE]           /* r2 指向的是要切换进程的 thread_info
                                          * 在 arch\arm\kernel\asm-offsets.c 里 
                                          * DEFINE(TI_TP_VALUE,     offsetof(struct thread_info, tp_value));
                                          * r3 得到要切换进程的 tp_value.
                                          */

 ARM(   stmia   ip!, {r4 - sl, fp, sp, lr} )    @ Store most regs on stack
                                                /* 将当前进程寄存器的值保存到当前进程 thread info的 cpu_context 里
                                                 * 其中 lr 保存到了 cpu_context_save 里的 pc 里。
                                                 * ia 表示 increase after, 由于使用了 !, 从而 ip也会一直更新,
                                                 * 最后指向了 cpu_context 里的 extra
                                                 */
 THUMB(    stmia    ip!, {r4 - sl, fp}       )    @ Store most regs on stack
 THUMB(    str    sp, [ip], #4           )
 THUMB(    str    lr, [ip], #4           )
#ifdef CONFIG_CPU_USE_DOMAINS
    ldr    r6, [r2, #TI_CPU_DOMAIN]
#endif
    set_tls    r3, r4, r5
#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
    ldr    r7, [r2, #TI_TASK]   /* DEFINE(TI_TASK,        offsetof(struct thread_info, task)); 
                              * 通过thread info 得到要切入进程的 task
                              */
    ldr    r8, =__stack_chk_guard
    ldr    r7, [r7, #TSK_STACK_CANARY] /* DEFINE(TSK_STACK_CANARY,    offsetof(struct task_struct, stack_canary));
                                     * 得到切入进程的 task 中的随机值
                                     */
#endif
#ifdef CONFIG_CPU_USE_DOMAINS
    mcr    p15, 0, r6, c3, c0, 0        @ Set domain register
#endif
    mov    r5, r0
    add    r4, r2, #TI_CPU_SAVE     /* r4 指向的要切入进程的 thread_info 中的 struct cpu_context_save    cpu_context; */
    ldr    r0, =thread_notify_head
    mov    r1, #THREAD_NOTIFY_SWITCH
    bl    atomic_notifier_call_chain
#if defined(CONFIG_CC_STACKPROTECTOR) && !defined(CONFIG_SMP)
    str    r7, [r8]   /* 将 task 的随机值 stack_canary 保存到 __stack_chk_guard ,用于栈越界检测 */
#endif
 THUMB(    mov    ip, r4               )
    mov    r0, r5            /* 此时 r0 表示 previous task_struct ,即要切出的进程的 task */
 ARM(   ldmia   r4, {r4 - sl, fp, sp, pc}  )    @ Load all regs saved previously
                                                /* 将要切入进程的 cpu_context 的值加载到寄存器, 
                                                 * 其中加载到 pc,实现跳转执行
                                                 * 如果该切入的进程是之前切出的的,则加载到 pc 的值,为
                                                 * context_switch 中 switch_to 的下一条指令,即 barrier();
                                                 */
 THUMB(    ldmia    ip!, {r4 - sl, fp}       )    @ Load all regs saved previously
 THUMB(    ldr    sp, [ip], #4           )
 THUMB(    ldr    pc, [ip]           )
 UNWIND(.fnend        )
ENDPROC(__switch_to)

 

你可能感兴趣的:(linux,kernel)