理论部分请参考《深入理解Linux 内核》第三章。
1 .switch_to 宏:
#define switch_to(prev,next,last) /
do { /
last = __switch_to(prev,task_thread_info(prev), task_thread_info(next)); /
} while (0)
2 .__switch_to 的函数是现在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)
UNWIND(.fnstart )
UNWIND(.cantunwind )
add ip, r1, #TI_CPU_SAVE
ldr r3, [r2, #TI_TP_VALUE]
ARM( stmia ip!, {r4 - sl, fp, sp, lr} ) @ Store most regs on stack
THUMB( stmia ip!, {r4 - sl, fp} ) @ Store most regs on stack
THUMB( str sp, [ip], #4 )
THUMB( str lr, [ip], #4 )
#ifdef CONFIG_MMU
ldr r6, [r2, #TI_CPU_DOMAIN]
#endif
#if defined(CONFIG_HAS_TLS_REG)
mcr p15, 0, r3, c13, c0, 3 @ set TLS register
#elif !defined(CONFIG_TLS_REG_EMUL)
mov r4, #0xffff0fff
str r3, [r4, #-15] @ TLS val at 0xffff0ff0
#endif
#ifdef CONFIG_MMU
mcr p15, 0, r6, c3, c0, 0 @ Set domain register
#endif
mov r5, r0
add r4, r2, #TI_CPU_SAVE
ldr r0, =thread_notify_head
mov r1, #THREAD_NOTIFY_SWITCH
bl atomic_notifier_call_chain
THUMB( mov ip, r4 )
mov r0, r5
ARM( ldmia r4, {r4 - sl, fp, sp, pc} ) @ Load all regs saved previously
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)
为了便于分析,将上面的源代码用工具反汇编后得到下列的输出:
000004f0 <__switch_to>:
r0 = previous task_struct
r1 = previous thread_info
r2 = next thread_info
4f0: e281c01c add ip, r1, #28 ; 0x1c
ip 的内容为previou 进程的thread_info 结构体中cpu_context 成员的地址。
4f4: e5923060 ldr r3, [r2, #96]
取得next 进程的tp_value 。tp_value 里保存了将要写到arm 的TP 寄存器中的值,由于是armv7 新加功能,所以还没有看具体怎么用。
4f8: e8ac6ff0 stmia ip!, {r4, r5, r6, r7, r8, r9, sl, fp, sp, lr}
保存当且上下文到previou 进程的thread_info 结构体中cpu_context 成员中。
4fc: e5926018 ldr r6, [r2, #24]
取得next 进程的cpu_domain 。
500: ee0d3f70 mcr 15, 0, r3, cr13, cr0, {3}
504: ee036f10 mcr 15, 0, r6, cr3, cr0, {0}
将next 进程的tp_value 和cpu_domain 写入arm 的TP 和域寄存器。
注:记得原来的内核只支持3 个domain ,谁知道现在已经支持4 个了,这个有时间可以分析一下。
508: e1a05000 mov r5, r0
50c: e282401c add r4, r2, #28 ; 0x1c
510: e59f000c ldr r0, [pc, #12] ; 524 <__switch_to+0x34>
514: e3a01002 mov r1, #2 ; 0x2
518: ebfffffe bl 0
向通知链上的子系统通知该事件。
51c: e1a00005 mov r0, r5
520: e894aff0 ldm r4, {r4, r5, r6, r7, r8, r9, sl, fp, sp, pc}
将新的进程(next 进程)的上下文恢复,程序切换的新的进程执行。
注:根据ATPCS ,r0~r2 用于传递参数,而r4~r11 全部由子程序负责保存(这个和x86 一人负责一部分的方式还不太一样)。所以r4 中的值可以继续使用,而r0 的值需暂时保存到r5 中。