last = switch_to(prev, next); 为什么需要定义last作为调用switch_to之前的prev的引用
原因如下:
struct task_struct * switch_to(struct task_struct *prev,
struct task_struct *next)
{
... ...
return cpu_switch_to(prev, next);
=> .global cpu_switch_to
cpu_switch_to:
add x8, x0, #THREAD_CPU_CONTEXT
mov x9, sp
stp x19, x20, [x8], #16
stp x21, x22, [x8], #16
stp x23, x24, [x8], #16
stp x25, x26, [x8], #16
stp x27, x28, [x8], #16
stp x29, x9, [x8], #16
str lr, [x8]
add x8, x1, #THREAD_CPU_CONTEXT
ldp x19, x20, [x8], #16
ldp x21, x22, [x8], #16
ldp x23, x24, [x8], #16
ldp x25, x26, [x8], #16
ldp x27, x28, [x8], #16
ldp x29, x9, [x8], #16
ldr lr, [x8]
mov sp, x9 // 切换了 sp, 导致 prev 的堆栈空间切换到了 next 空间的堆栈
ret
}
我们再来看 switch_to 的调用者
static void __schedule(void)
{
struct task_struct *prev, *next, *last;
struct run_queue *rq = &g_rq;
prev = current;
=> #define current get_current()
=> static inline struct task_struct *get_current(void)
{
return (struct task_struct *) (current_stack_pointer & ~(THREAD_SIZE - 1));
=> register unsigned long current_stack_pointer asm ("sp"); // 这时 prev 指向的 sp 已经是 next 进程空间的堆栈
}
/* 检查是否在中断上下文中发生了调度 */
schedule_debug(prev);
/* 关闭中断包含调度器,以免中断发生影响调度器 */
raw_local_irq_disable();
if (prev->state)
dequeue_task(rq, prev);
next = pick_next_task(rq, prev);
clear_task_resched(prev);
if (next != prev) {
last = switch_to(prev, next);
/*
* switch_to函数是用来切换prev进程到next进程。
* switch_to函数执行完成之后,已经切换到next
* 进程,整个栈和时空都发生变化了,不能使用这
* 里的prev变量来表示prev进程,只能通过aarch64
* 的x0寄存器来获取prev进程的task_struct。
*
* 在switch_to函数使用x0寄存器来传递prev进程
* task_struct,返回值也是通过x0寄存器,因此
* 这里last变量表示prev进程的task_struct
*/
rq->nr_switches++;
rq->curr = current;
}
/* 由next进程来收拾prev进程的现场 */
schedule_tail(last);
}
参考:
ARM64体系结构编程与实践